Cleaning up list detail

This commit is contained in:
Max Lynch
2021-01-19 17:24:46 -06:00
parent ba7de799cc
commit 2d4b04674c
4 changed files with 43 additions and 130 deletions
+41 -32
View File
@@ -1,56 +1,65 @@
import {
IonBackButton,
IonButtons,
IonCheckbox,
IonContent,
IonHeader,
IonItem,
IonLabel,
IonList,
IonPage,
IonTitle,
IonToolbar,
} from '@ionic/react';
import Link from '../../components/Link'; import Link from '../../components/Link';
import usePage from '../../hooks/usePage';
import Store from '../../store'; import Store from '../../store';
import * as actions from '../../store/actions'; import * as actions from '../../store/actions';
import * as selectors from '../../store/selectors'; import * as selectors from '../../store/selectors';
import Content from '../ui/Content';
import List from '../ui/List';
const ListItems = ({ list }) => { const ListItems = ({ list }) => {
return ( return (
<> <IonList>
<div className="py-2">
<Link href="/lists">All Lists</Link>
</div>
{(list?.items || []).map(item => ( {(list?.items || []).map(item => (
<ListItemEntry list={list} item={item} /> <ListItemEntry list={list} item={item} />
))} ))}
</> </IonList>
); );
}; };
const ListItemEntry = ({ list, item }) => ( const ListItemEntry = ({ list, item }) => (
<div <IonItem onClick={() => actions.setDone(list, item, !item.done)}>
className="p-4 border-solid border-b cursor-pointer flex select-none" <IonLabel>{item.name}</IonLabel>
onClick={() => actions.setDone(list, item, !item.done)} <IonCheckbox checked={item.done || false} slot="end" />
> </IonItem>
<span className="text-md flex-1">{item.name}</span>
<input
className="pointer-events-none select-none"
type="checkbox"
checked={item.done || false}
readOnly={true}
/>
</div>
); );
const ListDetail = ({ selected, listId, params }) => { const ListDetail = ({ match }) => {
const lists = Store.useState(selectors.getLists); const lists = Store.useState(selectors.getLists);
const actualListId = listId ? listId : params?.listId || null; const {
const loadedList = lists.find(l => l.id === actualListId); params: { listId },
} = match;
usePage({ const loadedList = lists.find(l => l.id === listId);
title: loadedList.name,
});
return ( return (
<Content className="p-4"> <IonPage>
<List className="h-full w-full"> <IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton defaultHref="/tabs/lists" />
</IonButtons>
<IonTitle>{loadedList.name}</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">{loadedList.name}</IonTitle>
</IonToolbar>
</IonHeader>
<ListItems list={loadedList} /> <ListItems list={loadedList} />
</List> </IonContent>
</Content> </IonPage>
); );
}; };
+2
View File
@@ -5,6 +5,7 @@ import { cog, flash, list } from 'ionicons/icons';
import Home from './Feed'; import Home from './Feed';
import Lists from './Lists'; import Lists from './Lists';
import ListDetail from './ListDetail';
import Settings from './Settings'; import Settings from './Settings';
const Tabs = () => { const Tabs = () => {
@@ -14,6 +15,7 @@ const Tabs = () => {
<IonRouterOutlet> <IonRouterOutlet>
<Route path="/tabs/feed" component={Home} exact={true} /> <Route path="/tabs/feed" component={Home} exact={true} />
<Route path="/tabs/lists" component={Lists} exact={true} /> <Route path="/tabs/lists" component={Lists} exact={true} />
<Route path="/tabs/lists/:listId" component={ListDetail} exact={true} />
<Route path="/tabs/settings" component={Settings} exact={true} /> <Route path="/tabs/settings" component={Settings} exact={true} />
<Route path="/tabs" render={() => <Redirect to="/tabs/feed" />} exact={true} /> <Route path="/tabs" render={() => <Redirect to="/tabs/feed" />} exact={true} />
</IonRouterOutlet> </IonRouterOutlet>
-85
View File
@@ -1,85 +0,0 @@
import { useEffect, useRef, useState, useCallback } from 'react';
// useEffect(() => {
// While History API does have `popstate` event, the only
// proper way to listen to changes via `push/replaceState`
// is to monkey-patch these methods.
//
// See https://stackoverflow.com/a/4585031
if (typeof history !== 'undefined') {
for (const type of [eventPushState, eventReplaceState]) {
const original = history[type];
history[type] = function () {
const result = original.apply(this, arguments);
const event = new Event(type);
event.arguments = arguments;
dispatchEvent(event);
return result;
};
}
} else {
console.log('No history API');
}
// });
/**
* History API docs @see https://developer.mozilla.org/en-US/docs/Web/API/History
*/
const eventPopstate = 'popstate';
const eventPushState = 'pushState';
const eventReplaceState = 'replaceState';
export const events = [eventPopstate, eventPushState, eventReplaceState];
const useLocation = ({ base = '' } = {}) => {
const getCurrentPathname = useCallback(base => {
const path = typeof location === 'undefined' ? '/' : location.pathname;
return !path.toLowerCase().indexOf(base.toLowerCase())
? path.slice(base.length) || '/'
: '~' + path;
}, []);
const [path, update] = useState(() => getCurrentPathname(base)); // @see https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const prevPath = useRef(path);
useEffect(() => {
// this function checks if the location has been changed since the
// last render and updates the state only when needed.
// unfortunately, we can't rely on `path` value here, since it can be stale,
// that's why we store the last pathname in a ref.
const checkForUpdates = () => {
const pathname = getCurrentPathname(base);
prevPath.current !== pathname && update((prevPath.current = pathname));
};
events.map(e => addEventListener(e, checkForUpdates));
// it's possible that an update has occurred between render and the effect handler,
// so we run additional check on mount to catch these updates. Based on:
// https://gist.github.com/bvaughn/e25397f70e8c65b0ae0d7c90b731b189
checkForUpdates();
return () => events.map(e => removeEventListener(e, checkForUpdates));
}, [base]);
// the 2nd argument of the `useLocation` return value is a function
// that allows to perform a navigation.
//
// the function reference should stay the same between re-renders, so that
// it can be passed down as an element prop without any performance concerns.
const navigate = useCallback(
(to, { replace = false } = {}) =>
history[replace ? eventReplaceState : eventPushState](
null,
'',
// handle nested routers and absolute paths
to[0] === '~' ? to.slice(1) : base + to
),
[base]
);
return [path, navigate];
};
export default useLocation;
-13
View File
@@ -1,13 +0,0 @@
import { useEffect } from 'react';
import Store from '../store';
const usePage = fields => {
useEffect(() => {
console.log('Use page effect', fields.title);
Store.update(s => {
s.currentPage = fields;
});
}, [fields]);
};
export default usePage;