Fix for dynamic routes with app router SSR

This commit is contained in:
Nathan Chapman
2024-03-07 11:36:12 -06:00
parent 83ede65b5e
commit 9d8c110044
18 changed files with 327 additions and 229 deletions
+11 -11
View File
@@ -1,29 +1,29 @@
'use client';
import { IonApp, IonRouterOutlet, setupIonicReact } from '@ionic/react';
import { StatusBar, Style } from '@capacitor/status-bar';
import { IonReactRouter } from '@ionic/react-router';
import { Redirect, Route } from 'react-router-dom';
import { Route } from 'react-router-dom';
import Tabs from './pages/Tabs';
setupIonicReact({});
window.matchMedia('(prefers-color-scheme: dark)').addListener(async status => {
try {
await StatusBar.setStyle({
style: status.matches ? Style.Dark : Style.Light,
});
} catch {}
});
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', async status => {
try {
await StatusBar.setStyle({
style: status.matches ? Style.Dark : Style.Light,
});
} catch {}
});
const AppShell = () => {
return (
<IonApp>
<IonReactRouter>
<IonRouterOutlet id="main">
<Route path="/tabs" render={() => <Tabs />} />
<Route path="/" render={() => <Redirect to="/tabs/feed" />} exact={true} />
<Route path="/" render={() => <Tabs />} />
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
+37 -9
View File
@@ -25,22 +25,47 @@ type FeedCardProps = {
author: string;
authorAvatar: string;
image: string;
}
};
const FeedCard = ({ title, type, text, author, authorAvatar, image }: FeedCardProps) => (
const FeedCard = ({
title,
type,
text,
author,
authorAvatar,
image,
}: FeedCardProps) => (
<Card className="my-4 mx-auto">
<div className="h-32 w-full relative">
<Image className="rounded-t-xl object-cover min-w-full min-h-full max-w-full max-h-full" src={image} alt="" fill />
<Image
className="rounded-t-xl object-cover min-w-full min-h-full max-w-full max-h-full"
src={image}
alt=""
fill
/>
</div>
<div className="px-4 py-4 bg-white rounded-b-xl dark:bg-gray-900">
<h4 className="font-bold py-0 text-s text-gray-400 dark:text-gray-500 uppercase">{type}</h4>
<h2 className="font-bold text-2xl text-gray-800 dark:text-gray-100">{title}</h2>
<p className="sm:text-sm text-s text-gray-500 mr-1 my-3 dark:text-gray-400">{text}</p>
<h4 className="font-bold py-0 text-s text-gray-400 dark:text-gray-500 uppercase">
{type}
</h4>
<h2 className="font-bold text-2xl text-gray-800 dark:text-gray-100">
{title}
</h2>
<p className="sm:text-sm text-s text-gray-500 mr-1 my-3 dark:text-gray-400">
{text}
</p>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 relative">
<Image src={authorAvatar} className="rounded-full object-cover min-w-full min-h-full max-w-full max-h-full" alt="" fill />
<Image
src={authorAvatar}
className="rounded-full object-cover min-w-full min-h-full max-w-full max-h-full"
alt=""
fill
/>
</div>
<h3 className="text-gray-500 dark:text-gray-200 m-l-8 text-sm font-medium">{author}</h3>
<h3 className="text-gray-500 dark:text-gray-200 m-l-8 text-sm font-medium">
{author}
</h3>
</div>
</div>
</Card>
@@ -71,7 +96,10 @@ const Feed = () => {
<IonTitle size="large">Feed</IonTitle>
</IonToolbar>
</IonHeader>
<Notifications open={showNotifications} onDidDismiss={() => setShowNotifications(false)} />
<Notifications
open={showNotifications}
onDidDismiss={() => setShowNotifications(false)}
/>
{homeItems.map((i, index) => (
<FeedCard {...i} key={index} />
))}
+15 -7
View File
@@ -22,7 +22,7 @@ type ListDetailParams = {
listId: string;
};
const ListItems = ({ list }: {list: TodoListItem}) => {
const ListItems = ({ list }: { list: TodoListItem }) => {
return (
<IonList>
{(list?.items || []).map((item, key) => (
@@ -32,10 +32,20 @@ const ListItems = ({ list }: {list: TodoListItem}) => {
);
};
const ListItemEntry = ({ list, item }: {list: TodoListItem, item: ListItem}) => (
const ListItemEntry = ({
list,
item,
}: {
list: TodoListItem;
item: ListItem;
}) => (
<IonItem onClick={() => actions.setDone(list, item, !item.done)}>
<IonLabel>{item.name}</IonLabel>
<IonCheckbox checked={item.done || false} slot="end" />
<IonCheckbox
aria-label={item.name}
checked={item.done || false}
slot="end"
/>
</IonItem>
);
@@ -50,14 +60,12 @@ const ListDetail = () => {
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton defaultHref="/tabs/lists" />
<IonBackButton defaultHref="/lists" />
</IonButtons>
<IonTitle>{loadedList?.name}</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
{loadedList && <ListItems list={loadedList} />}
</IonContent>
<IonContent>{loadedList && <ListItems list={loadedList} />}</IonContent>
</IonPage>
);
};
+7 -6
View File
@@ -1,7 +1,6 @@
import { TodoListItem } from '../../mock';
import Store from '../../store';
import * as selectors from '../../store/selectors';
import {
IonPage,
IonHeader,
@@ -13,11 +12,13 @@ import {
IonList,
} from '@ionic/react';
const ListEntry = ({ list }: {list: TodoListItem}) => (
<IonItem routerLink={`/tabs/lists/${list.id}`} className="list-entry">
<IonLabel>{list.name}</IonLabel>
</IonItem>
);
const ListEntry = ({ list }: { list: TodoListItem }) => {
return (
<IonItem routerLink={`/lists/${list.id}`} className="list-entry">
<IonLabel>{list.name}</IonLabel>
</IonItem>
);
};
const AllLists = () => {
const lists = Store.useState(selectors.selectLists);
+18 -3
View File
@@ -17,7 +17,11 @@ import { selectNotifications } from '../../store/selectors';
import { close } from 'ionicons/icons';
import { NotificationItem } from '../../mock';
const NotificationItem = ({ notification }: {notification: NotificationItem}) => (
const NotificationItem = ({
notification,
}: {
notification: NotificationItem;
}) => (
<IonItem>
<IonLabel>{notification.title}</IonLabel>
<IonNote slot="end">{notification.when}</IonNote>
@@ -27,7 +31,13 @@ const NotificationItem = ({ notification }: {notification: NotificationItem}) =>
</IonItem>
);
const Notifications = ({ open, onDidDismiss }: {open: boolean, onDidDismiss: () => void}) => {
const Notifications = ({
open,
onDidDismiss,
}: {
open: boolean;
onDidDismiss: () => void;
}) => {
const notifications = Store.useState(selectNotifications);
return (
@@ -35,7 +45,12 @@ const Notifications = ({ open, onDidDismiss }: {open: boolean, onDidDismiss: ()
<IonHeader>
<IonToolbar>
<IonTitle>Notifications</IonTitle>
<IonButton slot="end" fill="clear" color="dark" onClick={onDidDismiss}>
<IonButton
slot="end"
fill="clear"
color="dark"
onClick={onDidDismiss}
>
<IonIcon icon={close} />
</IonButton>
</IonToolbar>
+3 -4
View File
@@ -7,7 +7,6 @@ import {
IonContent,
IonList,
IonToggle,
IonLabel,
} from '@ionic/react';
import Store from '../../store';
@@ -16,7 +15,6 @@ import { setSettings } from '../../store/actions';
const Settings = () => {
const settings = Store.useState(selectors.selectSettings);
return (
<IonPage>
<IonHeader>
@@ -27,7 +25,6 @@ const Settings = () => {
<IonContent>
<IonList>
<IonItem>
<IonLabel>Enable Notifications</IonLabel>
<IonToggle
checked={settings.enableNotifications}
onIonChange={e => {
@@ -36,7 +33,9 @@ const Settings = () => {
enableNotifications: e.target.checked,
});
}}
/>
>
Enable Notifications
</IonToggle>
</IonItem>
</IonList>
</IonContent>
+20 -10
View File
@@ -1,6 +1,12 @@
import { Redirect, Route } from 'react-router-dom';
import { IonRouterOutlet, IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router';
import {
IonRouterOutlet,
IonTabs,
IonTabBar,
IonTabButton,
IonIcon,
IonLabel,
} from '@ionic/react';
import { cog, flash, list } from 'ionicons/icons';
import Home from './Feed';
@@ -12,22 +18,26 @@ const Tabs = () => {
return (
<IonTabs>
<IonRouterOutlet>
<Route path="/tabs/feed" render={() => <Home />} exact={true} />
<Route path="/tabs/lists" render={() => <Lists />} exact={true} />
<Route path="/tabs/lists/:listId" render={() => <ListDetail />} exact={true} />
<Route path="/tabs/settings" render={() => <Settings />} exact={true} />
<Route path="/tabs" render={() => <Redirect to="/tabs/feed" />} exact={true} />
<Route path="/feed" render={() => <Home />} exact={true} />
<Route path="/lists" render={() => <Lists />} exact={true} />
<Route
path="/lists/:listId"
render={() => <ListDetail />}
exact={true}
/>
<Route path="/settings" render={() => <Settings />} exact={true} />
<Route path="" render={() => <Redirect to="/feed" />} exact={true} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/tabs/feed">
<IonTabButton tab="tab1" href="/feed">
<IonIcon icon={flash} />
<IonLabel>Feed</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tabs/lists">
<IonTabButton tab="tab2" href="/lists">
<IonIcon icon={list} />
<IonLabel>Lists</IonLabel>
</IonTabButton>
<IonTabButton tab="tab3" href="/tabs/settings">
<IonTabButton tab="tab3" href="/settings">
<IonIcon icon={cog} />
<IonLabel>Settings</IonLabel>
</IonTabButton>
+10 -2
View File
@@ -1,8 +1,16 @@
import classNames from 'classnames';
const Card = ({ children, className }: {children: React.ReactElement[], className: string}) => (
const Card = ({
children,
className,
}: {
children: React.ReactElement[];
className: string;
}) => (
<div className={classNames('max-w-xl', className)}>
<div className="bg-white shadow-md rounded-b-xl dark:bg-black">{children}</div>
<div className="bg-white shadow-md rounded-b-xl dark:bg-black">
{children}
</div>
</div>
);