Merge pull request #60 from mlynch/fix-app-routing-dynamic-routes

Fix for dynamic routes with app router SSR
This commit is contained in:
Nathan Chapman
2024-03-07 11:38:05 -06:00
committed by GitHub
18 changed files with 327 additions and 229 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
{
"singleQuote": true,
"arrowParens": "avoid",
"printWidth": 100
"singleQuote": true,
"arrowParens": "avoid",
"trailingComma": "all"
}
+8 -2
View File
@@ -1,11 +1,17 @@
import dynamic from 'next/dynamic';
import { lists } from '../../mock';
const App = dynamic(() => import('../../components/AppShell'), {
ssr: false,
});
export function generateStaticParams() {
return [{ all: ['tabs', 'feed'] }];
export async function generateStaticParams() {
return [
{ all: ['feed'] },
{ all: ['lists'] },
...lists.map(list => ({ all: ['lists', list.id] })),
{ all: ['settings'] },
];
}
export default function Page() {
+10 -2
View File
@@ -40,8 +40,16 @@ export default function RootLayout({
return (
<html lang="en">
<body>{children}</body>
<Script type="module" src="https://unpkg.com/ionicons@5.2.3/dist/ionicons/ionicons.esm.js" />
<Script noModule src="https://unpkg.com/ionicons@5.2.3/dist/ionicons/ionicons.js" />
<Script
type="module"
src="https://unpkg.com/ionicons@5.2.3/dist/ionicons/ionicons.esm.js"
strategy="lazyOnload"
/>
<Script
noModule
src="https://unpkg.com/ionicons@5.2.3/dist/ionicons/ionicons.js"
strategy="lazyOnload"
/>
</html>
);
}
+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>
);
+31 -26
View File
@@ -1,9 +1,3 @@
export const images = [
'/img/c1.avif',
'/img/c2.avif',
'/img/c3.avif',
];
export type HomeItem = {
id: number;
title: string;
@@ -22,27 +16,25 @@ export const homeItems: HomeItem[] = [
text: 'We just got back from a trip to Maui, and we had a great time...',
author: 'Max Lynch',
authorAvatar: '/img/max.jpg',
image: images[0],
image: '/img/c1.avif',
},
{
id: 2,
title: 'Arctic Adventures',
type: 'Blog',
text:
'Last month we took a trek to the Arctic Circle. The isolation was just what we needed after...',
author: 'Max Lynch',
authorAvatar: '/img/max.jpg',
image: images[1],
text: 'Last month we took a trek to the Arctic Circle. The isolation was just what we needed after...',
author: 'Nathan Chapman',
authorAvatar: '/img/nathan.jpg',
image: '/img/c2.avif',
},
{
id: 3,
title: 'Frolicking in the Faroe Islands',
type: 'Blog',
text:
'The Faroe Islands are a North Atlantic archipelago located 320 kilometres (200 mi) north-northwest of Scotland...',
author: 'Max Lynch',
authorAvatar: '/img/max.jpg',
image: images[2],
text: 'The Faroe Islands are a North Atlantic archipelago located 320 kilometres (200 mi) north-northwest of Scotland...',
author: 'Leo Giovanetti',
authorAvatar: '/img/leo.jpg',
image: '/img/c3.avif',
},
];
@@ -62,24 +54,29 @@ export const notifications: NotificationItem[] = [
export type ListItem = {
name: string;
done?: boolean;
}
};
export type TodoListItem = {
name: string;
id: string;
items?: ListItem[];
}
};
// Some fake lists
export const lists: TodoListItem[] = [
{
name: 'Groceries',
id: 'groceries',
items: [{ name: 'Apples' }, { name: 'Bananas' }, { name: 'Milk' }, { name: 'Ice Cream' }],
id: '01HRCYTYED31N83MJ0WK97WC02',
items: [
{ name: 'Apples' },
{ name: 'Bananas' },
{ name: 'Milk' },
{ name: 'Ice Cream' },
],
},
{
name: 'Hardware Store',
id: 'hardware',
id: '01HRCYV2KPNJQJ43Y7X526BHVX',
items: [
{ name: 'Circular Saw' },
{ name: 'Tack Cloth' },
@@ -87,14 +84,22 @@ export const lists: TodoListItem[] = [
{ name: 'Router' },
],
},
{ name: 'Work', id: 'work', items: [{ name: 'TPS Report' }, { name: 'Set up email' }] },
{ name: 'Reminders', id: 'reminders' },
{
name: 'Work',
id: '01HRCYV6C3YWAJRF2ZE7AZ17K7',
items: [{ name: 'TPS Report' }, { name: 'Set up email' }],
},
{
name: 'Reminders',
id: '01HRCYVADRPCM5SYV5BH98C7HS',
items: [{ name: 'Get car inspection', done: true }],
},
];
export type Settings = {
enableNotifications: boolean;
}
};
export const settings: Settings = {
enableNotifications: true,
}
};
+7 -2
View File
@@ -13,5 +13,10 @@ module.exports = {
},
output: 'export',
swcMinify: true,
transpilePackages: ['@ionic/react', '@ionic/core', '@stencil/core', 'ionicons'],
}
transpilePackages: [
'@ionic/react',
'@ionic/core',
'@stencil/core',
'ionicons',
],
};
+122 -122
View File
@@ -8,38 +8,38 @@
"name": "nextjs-tailwind-ionic-capacitor-starter",
"version": "5.0.0",
"dependencies": {
"@capacitor/android": "5.7.0",
"@capacitor/core": "5.7.0",
"@capacitor/ios": "5.7.0",
"@capacitor/android": "5.7.2",
"@capacitor/core": "5.7.2",
"@capacitor/ios": "5.7.2",
"@capacitor/status-bar": "5.0.7",
"@ionic/react": "7.7.3",
"@ionic/react-router": "7.7.3",
"@ionic/react": "7.7.4",
"@ionic/react-router": "7.7.4",
"classnames": "2.5.1",
"next": "14.1.0",
"next": "14.1.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router": "5.3.4",
"react-router-dom": "5.3.4",
"react-virtuoso": "4.7.0"
"react-virtuoso": "4.7.1"
},
"devDependencies": {
"@capacitor/cli": "5.7.0",
"@capacitor/cli": "5.7.2",
"@types/jest": "29.5.12",
"@types/node": "20.11.19",
"@types/react": "18.2.57",
"@types/react-dom": "18.2.19",
"@types/node": "20.11.25",
"@types/react": "18.2.64",
"@types/react-dom": "18.2.21",
"@types/react-router": "5.1.20",
"@types/react-router-dom": "5.3.3",
"autoprefixer": "10.4.17",
"eslint": "8.56.0",
"eslint-config-next": "14.1.0",
"autoprefixer": "10.4.18",
"eslint": "8.57.0",
"eslint-config-next": "14.1.3",
"ionicons": "7.2.2",
"postcss": "8.4.35",
"prettier": "3.2.5",
"pullstate": "1.25",
"reselect": "5.1.0",
"tailwindcss": "3.4.1",
"typescript": "5.3.3"
"typescript": "5.4.2"
},
"engines": {
"node": ">=18.17"
@@ -256,17 +256,17 @@
}
},
"node_modules/@capacitor/android": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.7.0.tgz",
"integrity": "sha512-0bnG1dqfT/nTjzMeHF/a5kF8mqGjHrPLADNqn41seWDfb2ch6AMiKUHsmHpEOWmGIrWOM25qNTrTOytoCSpuXg==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.7.2.tgz",
"integrity": "sha512-T4U+15R/1PyokW0Le92j7AV19kuO25his2ymF2xf2I04fZUDj8RjmXA+za7i3K8vhxtKkTdY2dPAywrfNAM09Q==",
"peerDependencies": {
"@capacitor/core": "^5.7.0"
}
},
"node_modules/@capacitor/cli": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.7.0.tgz",
"integrity": "sha512-md6217RXFQwSNo9vr1gDgBqR88MJaQVwu3C5W3bpWlmajhec6NUR7yT7QNcBWErhCIJfqOOqXu4ZSSShndF0ug==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.7.2.tgz",
"integrity": "sha512-dTW48klx39Mm2twkRU5pHw7tFRGtGk80fw5psopmbx8Ep5FG08HprqnwK5DXsFPhgJaC+ax4VDwmFCvb8uAGFA==",
"dev": true,
"dependencies": {
"@ionic/cli-framework-output": "^2.2.5",
@@ -296,17 +296,17 @@
}
},
"node_modules/@capacitor/core": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.0.tgz",
"integrity": "sha512-wa9Fao+Axa1t2ZERMyQD9r0xyfglQyC4DHQKintzKaIqcRuVe9J31TmfD3IxROYi9LGpY4X8cq4m4bjb0W94Qg==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.2.tgz",
"integrity": "sha512-/OUtfINmk7ke32VtKIHRAy8NlunbeK+aCqCHOS+fvtr7nUsOJXPkYgbgqZp/CWXET/gSK1xxMecaVBzpE98UKA==",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@capacitor/ios": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.7.0.tgz",
"integrity": "sha512-zoEdsYQHI1zz2vjKsTpu5bSfxQQ5jrk3Qs6Op9MYcckZZ2QWIs0YpL99p+zODXNpkkyLG73NXEIrOjvyI9jx8A==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.7.2.tgz",
"integrity": "sha512-msh+Kqjv/MyVCrSH0zVtwxptXnsgky4FENUq+Xdaa1pqEglmpHlUKod1Jf7qhfAhTLhHPyokOZMvaIyTtoSwCA==",
"peerDependencies": {
"@capacitor/core": "^5.7.0"
}
@@ -367,9 +367,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -423,9 +423,9 @@
}
},
"node_modules/@ionic/core": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.7.3.tgz",
"integrity": "sha512-DSv6DPuiLU2MXsgDAXKFJW5OXxT7EyPy2jcQf03RcWooWeFryy979mqotPw7BgUuWt/fVGuz2tl3peAJGSqmDQ==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.7.4.tgz",
"integrity": "sha512-zThio3ZfbTu+3eM6QBdyeEk5OBc7M0ApFwSlP/G7rrFVcTPm12FNvG9VPD+aN5NwnYy0EsV3hlMkxbawoqjVLw==",
"dependencies": {
"@stencil/core": "^4.12.2",
"ionicons": "^7.2.2",
@@ -433,11 +433,11 @@
}
},
"node_modules/@ionic/react": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.7.3.tgz",
"integrity": "sha512-b8jLpqv4dZ9nB9zoxhe0KR1Wk9bWMQ3UXQcOPu20+zYrxExwPqpLJ93LI0bU4F7ellduMjsakvELY486FeRrXw==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.7.4.tgz",
"integrity": "sha512-UBNBUjBN1fmCUyH8hetu0/q3F4pSNFVpjhh3Bt3s/bUXy0ksCuGbiYg/hET9QW1ja17ijq0+coqREXEB8lTmrA==",
"dependencies": {
"@ionic/core": "7.7.3",
"@ionic/core": "7.7.4",
"ionicons": "^7.0.0",
"tslib": "*"
},
@@ -447,11 +447,11 @@
}
},
"node_modules/@ionic/react-router": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.7.3.tgz",
"integrity": "sha512-NmEk801pfbrqzyTAb5nLDWGseUzm7kMpUy0dMY6OU76tpuHrEFBCFkZYAlTJWqXhkGRj9cR0cMnFAhPtGeSCkg==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.7.4.tgz",
"integrity": "sha512-phPpcRGoeQA3YTxRx1069yrtgsqWXhsDfcR7BJlgj0/uk4Wmx2NsUKV/nQrFmrtXdZSspFIrbd3yorq3gRhClA==",
"dependencies": {
"@ionic/react": "7.7.3",
"@ionic/react": "7.7.4",
"tslib": "*"
},
"peerDependencies": {
@@ -791,23 +791,23 @@
}
},
"node_modules/@next/env": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
"integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw=="
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.3.tgz",
"integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.0.tgz",
"integrity": "sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.3.tgz",
"integrity": "sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==",
"dev": true,
"dependencies": {
"glob": "10.3.10"
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
"integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.3.tgz",
"integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==",
"cpu": [
"arm64"
],
@@ -820,9 +820,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
"integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.3.tgz",
"integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==",
"cpu": [
"x64"
],
@@ -835,9 +835,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
"integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.3.tgz",
"integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==",
"cpu": [
"arm64"
],
@@ -850,9 +850,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
"integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.3.tgz",
"integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==",
"cpu": [
"arm64"
],
@@ -865,9 +865,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz",
"integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.3.tgz",
"integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==",
"cpu": [
"x64"
],
@@ -880,9 +880,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz",
"integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.3.tgz",
"integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==",
"cpu": [
"x64"
],
@@ -895,9 +895,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
"integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.3.tgz",
"integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==",
"cpu": [
"arm64"
],
@@ -910,9 +910,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
"integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.3.tgz",
"integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==",
"cpu": [
"ia32"
],
@@ -925,9 +925,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
"integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.3.tgz",
"integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==",
"cpu": [
"x64"
],
@@ -1072,9 +1072,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
"integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
"version": "20.11.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
"integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1087,9 +1087,9 @@
"dev": true
},
"node_modules/@types/react": {
"version": "18.2.57",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.57.tgz",
"integrity": "sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw==",
"version": "18.2.64",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.64.tgz",
"integrity": "sha512-MlmPvHgjj2p3vZaxbQgFUQFvD8QiZwACfGqEdDSWou5yISWxDQ4/74nCAwsUiX7UFLKZz3BbVSPj+YxeoGGCfg==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
@@ -1098,9 +1098,9 @@
}
},
"node_modules/@types/react-dom": {
"version": "18.2.19",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz",
"integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==",
"version": "18.2.21",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.21.tgz",
"integrity": "sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==",
"dev": true,
"dependencies": {
"@types/react": "*"
@@ -1590,9 +1590,9 @@
}
},
"node_modules/autoprefixer": {
"version": "10.4.17",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz",
"integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==",
"version": "10.4.18",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
"integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==",
"dev": true,
"funding": [
{
@@ -1609,8 +1609,8 @@
}
],
"dependencies": {
"browserslist": "^4.22.2",
"caniuse-lite": "^1.0.30001578",
"browserslist": "^4.23.0",
"caniuse-lite": "^1.0.30001591",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.0.0",
@@ -1827,9 +1827,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001588",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz",
"integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==",
"version": "1.0.30001594",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz",
"integrity": "sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==",
"funding": [
{
"type": "opencollective",
@@ -2344,16 +2344,16 @@
}
},
"node_modules/eslint": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@eslint/js": "8.57.0",
"@humanwhocodes/config-array": "^0.11.14",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
@@ -2399,12 +2399,12 @@
}
},
"node_modules/eslint-config-next": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.1.0.tgz",
"integrity": "sha512-SBX2ed7DoRFXC6CQSLc/SbLY9Ut6HxNB2wPTcoIWjUMd7aF7O/SIE7111L8FdZ9TXsNV4pulUDnfthpyPtbFUg==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.1.3.tgz",
"integrity": "sha512-sUCpWlGuHpEhI0pIT0UtdSLJk5Z8E2DYinPTwsBiWaSYQomchdl0i60pjynY48+oXvtyWMQ7oE+G3m49yrfacg==",
"dev": true,
"dependencies": {
"@next/eslint-plugin-next": "14.1.0",
"@next/eslint-plugin-next": "14.1.3",
"@rushstack/eslint-patch": "^1.3.3",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0",
"eslint-import-resolver-node": "^0.3.6",
@@ -4278,11 +4278,11 @@
"dev": true
},
"node_modules/next": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
"integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==",
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/next/-/next-14.1.3.tgz",
"integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==",
"dependencies": {
"@next/env": "14.1.0",
"@next/env": "14.1.3",
"@swc/helpers": "0.5.2",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
@@ -4297,15 +4297,15 @@
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.1.0",
"@next/swc-darwin-x64": "14.1.0",
"@next/swc-linux-arm64-gnu": "14.1.0",
"@next/swc-linux-arm64-musl": "14.1.0",
"@next/swc-linux-x64-gnu": "14.1.0",
"@next/swc-linux-x64-musl": "14.1.0",
"@next/swc-win32-arm64-msvc": "14.1.0",
"@next/swc-win32-ia32-msvc": "14.1.0",
"@next/swc-win32-x64-msvc": "14.1.0"
"@next/swc-darwin-arm64": "14.1.3",
"@next/swc-darwin-x64": "14.1.3",
"@next/swc-linux-arm64-gnu": "14.1.3",
"@next/swc-linux-arm64-musl": "14.1.3",
"@next/swc-linux-x64-gnu": "14.1.3",
"@next/swc-linux-x64-musl": "14.1.3",
"@next/swc-win32-arm64-msvc": "14.1.3",
"@next/swc-win32-ia32-msvc": "14.1.3",
"@next/swc-win32-x64-msvc": "14.1.3"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -5064,9 +5064,9 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-virtuoso": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.7.0.tgz",
"integrity": "sha512-cpgvI1rSOETGDMhqVAVDuH+XHbWO1uIGKv5I6l4CyC71xWYUeGrE5n7sgTZklROB4+Vbv85pcgfWloTlY48HGQ==",
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.7.1.tgz",
"integrity": "sha512-V1JIZLEwgX7R+YNkbY8dq6NcnIGKGWXe4mnMJJPsA2L4qeFKst0LY3mDk6sBCJyKRbMzYFxTZWyTT4QsA1JvVQ==",
"engines": {
"node": ">=10"
},
@@ -5906,9 +5906,9 @@
}
},
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"node_modules/tiny-warning": {
"version": "1.0.3",
@@ -6069,9 +6069,9 @@
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
+16 -15
View File
@@ -11,6 +11,7 @@
"build": "next build",
"dev": "next dev",
"lint": "eslint . --ext .ts,.tsx",
"prettify": "prettier --write **/*.tsx",
"preserve": "npm run build",
"serve": "npx serve out",
"presync": "npm run build",
@@ -19,37 +20,37 @@
"ios": "npx cap run ios"
},
"dependencies": {
"@capacitor/android": "5.7.0",
"@capacitor/core": "5.7.0",
"@capacitor/ios": "5.7.0",
"@capacitor/android": "5.7.2",
"@capacitor/core": "5.7.2",
"@capacitor/ios": "5.7.2",
"@capacitor/status-bar": "5.0.7",
"@ionic/react": "7.7.3",
"@ionic/react-router": "7.7.3",
"@ionic/react": "7.7.4",
"@ionic/react-router": "7.7.4",
"classnames": "2.5.1",
"next": "14.1.0",
"next": "14.1.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router": "5.3.4",
"react-router-dom": "5.3.4",
"react-virtuoso": "4.7.0"
"react-virtuoso": "4.7.1"
},
"devDependencies": {
"@capacitor/cli": "5.7.0",
"@capacitor/cli": "5.7.2",
"@types/jest": "29.5.12",
"@types/node": "20.11.19",
"@types/react": "18.2.57",
"@types/react-dom": "18.2.19",
"@types/node": "20.11.25",
"@types/react": "18.2.64",
"@types/react-dom": "18.2.21",
"@types/react-router-dom": "5.3.3",
"@types/react-router": "5.1.20",
"autoprefixer": "10.4.17",
"eslint": "8.56.0",
"eslint-config-next": "14.1.0",
"autoprefixer": "10.4.18",
"eslint": "8.57.0",
"eslint-config-next": "14.1.3",
"ionicons": "7.2.2",
"postcss": "8.4.35",
"prettier": "3.2.5",
"pullstate": "1.25",
"reselect": "5.1.0",
"tailwindcss": "3.4.1",
"typescript": "5.3.3"
"typescript": "5.4.2"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+8 -4
View File
@@ -21,13 +21,17 @@ export const setSettings = (settings: Settings) => {
// App-specific actions
export const setDone = (list: TodoListItem, item: ListItem, done: boolean) => {
export const setDone = (
list: TodoListItem,
listItem: ListItem,
done: boolean,
) => {
Store.update((s, o) => {
const listIndex = o.lists.findIndex(l => l === list);
const items = o.lists[listIndex].items;
if(!items) return;
const itemIndex = items.findIndex(i => i === item);
const item = items[itemIndex];
const itemIndex = items?.findIndex(i => i === listItem);
const item = items?.[itemIndex ?? -1];
if (!item) return;
item.done = done;
if (list === o.selectedList) {
s.selectedList = s.lists[listIndex];