Skip to content

Commit

Permalink
misc
Browse files Browse the repository at this point in the history
  • Loading branch information
aeharding committed Jun 12, 2023
1 parent 088fe73 commit cc047c6
Show file tree
Hide file tree
Showing 25 changed files with 480 additions and 181 deletions.
14 changes: 5 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@
"lint": "eslint"
},
"dependencies": {
"@capacitor/app": "5.0.3",
"@capacitor/core": "5.0.5",
"@capacitor/haptics": "5.0.4",
"@capacitor/keyboard": "5.0.4",
"@capacitor/status-bar": "5.0.4",
"@emotion/css": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
Expand All @@ -26,20 +21,21 @@
"@reduxjs/toolkit": "^1.9.5",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.3.3",
"date-fns": "^2.30.0",
"ionicons": "^7.0.0",
"lemmy-js-client": "^0.17.2-rc.24",
"lodash": "^4.17.21",
"markdown-to-txt": "^2.0.1",
"photoswipe": "^5.3.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.7",
"react-photoswipe-gallery": "^2.2.7",
"react-redux": "^8.0.7",
"react-router": "^5.3.4",
"react-router-dom": "^5.3.4",
"react-virtuoso": "^4.3.10"
},
"devDependencies": {
"@capacitor/cli": "5.0.5",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
Expand All @@ -52,10 +48,10 @@
"eslint": "^8.35.0",
"eslint-plugin-react": "^7.32.2",
"jsdom": "^21.1.0",
"terser": "^5.17.7",
"typescript": "^4.9.3",
"vite": "^4.1.0",
"vitest": "^0.29.2",
"terser": "^5.17.7"
"vitest": "^0.29.2"
},
"description": "An Ionic project"
}
11 changes: 0 additions & 11 deletions src/App.css

This file was deleted.

64 changes: 13 additions & 51 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Redirect, Route } from "react-router-dom";
import { Redirect, Route, useHistory, useLocation } from "react-router-dom";
import {
IonApp,
IonIcon,
Expand Down Expand Up @@ -32,8 +32,6 @@ import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";

import "./App.css";

/* Theme variables */
import "./theme/variables.css";
import PostDetail from "./components/PostDetail";
Expand All @@ -43,6 +41,8 @@ import ThemeColorUpdater from "./ThemeColorUpdater";
import { isInstalled } from "./helpers/device";
import Communities from "./pages/Communities";
import Community from "./pages/Community";
import React, { useEffect, useState } from "react";
import Tabs from "./Tabs";

setupIonicReact({
rippleEffect: false,
Expand All @@ -53,6 +53,15 @@ setupIonicReact({
function Router({ children }: { children: React.ReactNode }) {
const history = createMemoryHistory();

useEffect(() => {
const unListen = history.listen(() => {
window.scrollTo(0, 0);
});
return () => {
unListen();
};
}, []);

if (isInstalled())
return (
<IonReactMemoryRouter history={history}>{children}</IonReactMemoryRouter>
Expand All @@ -61,61 +70,14 @@ function Router({ children }: { children: React.ReactNode }) {
return <IonReactRouter>{children}</IonReactRouter>;
}

const DEFAULT_ACTOR = "lemmy.ml";

function App() {
return (
<>
<ThemeColorUpdater />
<Provider store={store}>
<IonApp>
<Router>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/">
<Redirect to={`/${DEFAULT_ACTOR}/home`} />
</Route>
<Route exact path="/:actor/communities">
<Communities />
</Route>
<Route
exact
path="/:actor/"
render={(props) => (
<Redirect to={`/${props.match.params.actor}/home`} />
)}
></Route>
<Route exact path="/:actor/home">
<Home />
</Route>
<Route exact path="/:actor/c/:community">
<Community />
</Route>
<Route exact path="/:actor/c/:community/comments/:id">
<PostDetail />
</Route>
<Route exact path="/tab2">
<Tab2 />
</Route>
<Route path="/tab3">
<Tab3 />
</Route>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="posts" href={`/${DEFAULT_ACTOR}/home`}>
<IonIcon aria-hidden="true" icon={logoWebComponent} />
<IonLabel>Posts</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tab2">
<IonIcon aria-hidden="true" icon={person} />
<IonLabel>lemmy_user</IonLabel>
</IonTabButton>
<IonTabButton tab="tab3" href="/tab3">
<IonIcon aria-hidden="true" icon={settings} />
<IonLabel>Settings</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
<Tabs />
</Router>
</IonApp>
</Provider>
Expand Down
111 changes: 111 additions & 0 deletions src/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {
Redirect,
Route,
useHistory,
useLocation,
useParams,
} from "react-router-dom";
import {
IonApp,
IonIcon,
IonLabel,
IonRouterOutlet,
IonTabBar,
IonTabButton,
IonTabs,
setupIonicReact,
useIonRouter,
} from "@ionic/react";
import { IonReactMemoryRouter, IonReactRouter } from "@ionic/react-router";
import { logoWebComponent, settings, person } from "ionicons/icons";
import Home from "./pages/Home";
import Tab2 from "./pages/Tab2";
import Tab3 from "./pages/Tab3";
import PostDetail from "./components/PostDetail";
import { Provider } from "react-redux";
import store from "./store";
import ThemeColorUpdater from "./ThemeColorUpdater";
import { isInstalled } from "./helpers/device";
import Communities from "./pages/Communities";
import Community from "./pages/Community";
import React, { useEffect, useState } from "react";

const DEFAULT_ACTOR = "lemmy.ml";

export default function Tabs() {
const location = useLocation();
const router = useIonRouter();

return (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/">
<Redirect to={`/${DEFAULT_ACTOR}/home`} />
</Route>
<Route exact path="/:actor/home">
<Home />
</Route>
<Route exact path="/:actor">
<Communities />
</Route>
<Route exact path="/:actor/c/:community">
<Community />
</Route>
<Route exact path="/:actor/c/:community/comments/:id">
<PostDetail />
</Route>
<Route exact path="/tab2">
<Tab2 />
</Route>
<Route path="/tab3">
<Tab3 />
</Route>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton
tab="posts"
selected={
!location.pathname.startsWith("/tab2") &&
!location.pathname.startsWith("/tab3")
}
onClick={() => {
// Hacks on hacks on hacks
const pages = document.querySelectorAll(
"div.ion-page:not(.ion-page.hidden)"
);
const page = pages[pages.length - 1];
const scroll =
page.querySelector('[data-virtuoso-scroller="true"]') ??
page
.querySelector("ion-content")
?.shadowRoot?.querySelector(".inner-scroll");

if (scroll?.scrollTop) {
scroll.scrollTo({ top: 0, behavior: "smooth" });
return;
}

if (location.pathname.endsWith("/home")) {
router.push(`/${DEFAULT_ACTOR}`, "back");
return;
}
if (location.pathname === `/${DEFAULT_ACTOR}`) return;

router.push(`/${DEFAULT_ACTOR}/home`, "back");
}}
>
<IonIcon aria-hidden="true" icon={logoWebComponent} />
<IonLabel>Posts</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tab2">
<IonIcon aria-hidden="true" icon={person} />
<IonLabel>lemmy_user</IonLabel>
</IonTabButton>
<IonTabButton tab="tab3" href="/tab3">
<IonIcon aria-hidden="true" icon={settings} />
<IonLabel>Settings</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}
17 changes: 10 additions & 7 deletions src/ThemeColorUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ const ThemeColorUpdater = () => {
document.head.appendChild(metaThemeColor);
}

const handleVisibilityChange = () => {
if (document.visibilityState === "visible") {
// Update theme color when the app becomes visible
metaThemeColor?.setAttribute("content", themeColor);
}
};

document.addEventListener("visibilitychange", handleVisibilityChange);

return () => {
mediaQuery.removeEventListener("change", handleThemeChange);
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, []);

useEffect(() => {
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
if (metaThemeColor) {
metaThemeColor.setAttribute("content", themeColor);
}
}, [themeColor]);

return null;
Expand Down
45 changes: 45 additions & 0 deletions src/components/Ago.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import { formatDistanceToNowStrict } from "date-fns";

interface AgoProps {
date: Date;
}

const Ago: React.FC<AgoProps> = ({ date }) => {
const relativeDate = formatDistanceToNowStrict(date, { addSuffix: false });

const getRelativeDateString = (relativeDate: string) => {
const [value, unit] = relativeDate.split(" ");
let formattedString = "";

if (unit === "seconds") {
formattedString = value === "less" ? "<1m" : `${value}s`;
} else {
switch (unit) {
case "minutes":
formattedString = `${value}m`;
break;
case "hours":
formattedString = `${value}h`;
break;
case "days":
formattedString = `${value}d`;
break;
case "months":
formattedString = `${value}m`;
break;
case "years":
formattedString = `${value}y`;
break;
default:
formattedString = relativeDate;
}
}

return formattedString;
};

return <span>{getRelativeDateString(relativeDate)}</span>;
};

export default Ago;
31 changes: 31 additions & 0 deletions src/components/AppBackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IonBackButton } from "@ionic/react";
import { useState } from "react";

interface AppBackButtonProps {
defaultText?: string;
defaultHref?: string;
}

export default function AppBackButton({
defaultText,
defaultHref,
}: AppBackButtonProps) {
// Totally a hack, but I haven't found a better solution
const [lastPageTitle] = useState(
(() => {
const hiddenPages = document.querySelectorAll(".ion-page");
if (!hiddenPages.length) return;

return hiddenPages[hiddenPages.length - 1]
.querySelector(".title-default")
?.textContent?.trim();
})()
);

return (
<IonBackButton
text={lastPageTitle ?? defaultText}
defaultHref={defaultHref}
/>
);
}
Loading

0 comments on commit cc047c6

Please sign in to comment.