From b700ad2465dec886ac98fed2793581858730f7b0 Mon Sep 17 00:00:00 2001 From: Daniel Metcalfe Date: Fri, 8 Nov 2024 08:26:21 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20rocket=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 362402 -> 363100 bytes components/common/Header.tsx | 2 +- components/common/Starship.tsx | 21 + components/common/cn.ts | 6 + components/demo/Journey/index.tsx | 45 +- components/landingPage/Journey/Starship.tsx | 13 - components/landingPage/Journey/Trajectory.tsx | 15 +- components/landingPage/Journey/index.tsx | 2 +- components/landingPage/Layout.tsx | 2 +- components/pages/Home.tsx | 856 +++++++++--------- components/todos/SelectedTodo.tsx | 30 - components/todos/TodoContext.tsx | 50 + components/todos/create/useCreateTodoModal.ts | 6 +- components/todos/edit/useEditTodoModal.ts | 6 +- components/todos/index.tsx | 4 +- components/view/index.tsx | 2 +- cypress/e2e/spec.cy.ts | 34 +- package.json | 4 +- 18 files changed, 583 insertions(+), 515 deletions(-) create mode 100644 components/common/Starship.tsx create mode 100644 components/common/cn.ts delete mode 100644 components/landingPage/Journey/Starship.tsx delete mode 100644 components/todos/SelectedTodo.tsx create mode 100644 components/todos/TodoContext.tsx diff --git a/bun.lockb b/bun.lockb index da6873c22dfca58a7b2d00da4277802d128eeaf3..fbadf68f5671730c8e25007f763e64d8f42c54a1 100755 GIT binary patch delta 68716 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 diff --git a/components/common/Header.tsx b/components/common/Header.tsx index 345d7c7..de79ccc 100644 --- a/components/common/Header.tsx +++ b/components/common/Header.tsx @@ -33,7 +33,7 @@ export const Header = ({ title }: { title: string }) => { {title} ( + function Starship({ className }, ref) { + return ( + A starship from a birds-eye view + ) + }, +) + +export default Starship diff --git a/components/common/cn.ts b/components/common/cn.ts new file mode 100644 index 0000000..568ec19 --- /dev/null +++ b/components/common/cn.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...args: ClassValue[]) { + return twMerge(clsx(args)) +} diff --git a/components/demo/Journey/index.tsx b/components/demo/Journey/index.tsx index c94291c..75e35a0 100644 --- a/components/demo/Journey/index.tsx +++ b/components/demo/Journey/index.tsx @@ -1,5 +1,5 @@ -import { createRef, useLayoutEffect, useRef, useState } from 'react' -import Starship from '../../landingPage/Journey/Starship' +import { createRef, useEffect, useLayoutEffect, useRef, useState } from 'react' +import Starship from '../../common/Starship' import Todos from '../../todos' import { Todo } from '../../todos/interfaces' import { useWindowSize } from '../../common/useWindowResize' @@ -9,14 +9,14 @@ export default function Journey({ todos }: { todos: Todo[] }) { const todosRef = createRef() useMaintainScrollOffset(currentTodoRef, todosRef) - const [starshipY, _] = useStarshipYPosition(currentTodoRef) + // const [starshipY, _] = useStarshipYPosition(currentTodoRef) return (
@@ -50,18 +50,41 @@ function useMaintainScrollOffset(currentTodoRef, todosRef) { }, [currentTodoRef, todosRef]) } -function useStarshipYPosition(currentTodoRef) { +export function useStarshipYPosition( + starship: HTMLElement | null, + nextTodoPosition: DOMRect | null, + commonAncestor: HTMLElement | null, +) { + console.debug('Starship position render') const size = useWindowSize() const [starshipY, setStarshipY] = useState(0) // TODO: This causes it to render a second time when a todo completion changes the scroll height. But maybe that doesn't matter and maybe we can move the starship into its own component hierarchy so it doesn't re-render other things unnecessarily. - useLayoutEffect(() => { - const elementPadding = 80 - const starshipImagePadding = 10 - setStarshipY( - currentTodoRef.current.offsetTop - elementPadding - starshipImagePadding, + useEffect(() => { + console.debug('Starship position effect') + if ( + starship === null || + nextTodoPosition === null || + nextTodoPosition.height === null || + commonAncestor === null ) - }, [currentTodoRef, size, setStarshipY]) + return setStarshipY(0) + + const commonAncestorRect = commonAncestor.getBoundingClientRect() + const todoDistanceFromCommonAncestor = + nextTodoPosition.y - commonAncestorRect.y + const starshipHeightAdjustment = + (nextTodoPosition.height - starship?.offsetHeight) / 2 + + const y = todoDistanceFromCommonAncestor + starshipHeightAdjustment + console.debug(`Setting startship Y to ${y}`, { + commonAncestorRect, + nextTodoPosition, + todoDistanceFromCommonAncestor, + starshipHeightAdjustment, + }) + setStarshipY(y) + }, [commonAncestor, nextTodoPosition, size, starship, setStarshipY]) return [starshipY, setStarshipY] } diff --git a/components/landingPage/Journey/Starship.tsx b/components/landingPage/Journey/Starship.tsx deleted file mode 100644 index 5fce502..0000000 --- a/components/landingPage/Journey/Starship.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Image from 'next/image' - -export default function Starship() { - return ( - A starship from a birds-eye view - ) -} diff --git a/components/landingPage/Journey/Trajectory.tsx b/components/landingPage/Journey/Trajectory.tsx index 893e9a3..3cf0663 100644 --- a/components/landingPage/Journey/Trajectory.tsx +++ b/components/landingPage/Journey/Trajectory.tsx @@ -1,3 +1,14 @@ -export default function Tracjectory() { - return
+import { cn } from '../../common/cn' + +export default function Tracjectory(props: JSX.IntrinsicElements['div']) { + return ( +
+ ) } diff --git a/components/landingPage/Journey/index.tsx b/components/landingPage/Journey/index.tsx index ee834e2..4e55eff 100644 --- a/components/landingPage/Journey/index.tsx +++ b/components/landingPage/Journey/index.tsx @@ -1,4 +1,4 @@ -import Starship from './Starship' +import Starship from '../../common/Starship' import Tracjectory from './Trajectory' export default function Journey() { diff --git a/components/landingPage/Layout.tsx b/components/landingPage/Layout.tsx index 6815e85..0d61d35 100644 --- a/components/landingPage/Layout.tsx +++ b/components/landingPage/Layout.tsx @@ -4,7 +4,7 @@ import Footer from '../common/Footer' import Planets from '../common/Planets' import { ScrollDirection } from '../common/scroll' import Contact from './Contact' -import Starship from './Journey/Starship' +import Starship from '../common/Starship' import Tracjectory from './Journey/Trajectory' const Layout = ({ diff --git a/components/pages/Home.tsx b/components/pages/Home.tsx index 63d6bf0..955f4c5 100644 --- a/components/pages/Home.tsx +++ b/components/pages/Home.tsx @@ -6,6 +6,7 @@ import { IonCardHeader, IonCardTitle, IonCheckbox, + IonCol, IonContent, IonFab, IonFabButton, @@ -33,11 +34,14 @@ import { IonToast, IonToggle, IonToolbar, + useIonViewDidEnter, + useIonViewDidLeave, + useIonViewWillEnter, + useIonViewWillLeave, } from '@ionic/react' import { useLiveQuery } from 'dexie-react-hooks' import { add, - checkmarkDoneCircleSharp, documentText, filterSharp, locateOutline, @@ -45,10 +49,10 @@ import { } from 'ionicons/icons' import _ from 'lodash' import { - forwardRef, RefObject, useCallback, useEffect, + useLayoutEffect, useMemo, useRef, useState, @@ -58,11 +62,14 @@ import { db, Todo } from '../db' import NoteProviders from '../notes/providers' import useSettings from '../settings/useSettings' import { getIonIcon } from '../starRoles/icons' -import { SelectedTodoProvider } from '../todos/SelectedTodo' +import useTodoContext, { TodoContextProvider } from '../todos/TodoContext' import { useTodoActionSheet } from '../todos/TodoActionSheet' import { useCreateTodoModal } from '../todos/create/useCreateTodoModal' import useView, { ViewProvider } from '../view' import order, { calculateReorderIndices, starMudder } from '../common/order' +import Tracjectory from '../landingPage/Journey/Trajectory' +import Starship from '../common/Starship' +import { useStarshipYPosition } from '../demo/Journey' const Home = () => { useGlobalKeyboardShortcuts() @@ -70,7 +77,7 @@ const Home = () => { return ( <> - + @@ -101,7 +108,7 @@ const Home = () => { - + ) @@ -113,31 +120,16 @@ export const TodoLists = ({}: {}) => { // Initial loading & scrolling stuff const contentRef = useRef(null) const [enablePagination, setEnablePagination] = useState(false) - useEffect(() => { - setTimeout(() => { - // TODO: See if ionViewDidEnter works better than setTimeout - console.debug('Scrolling to bottom', contentRef.current) - contentRef.current?.scrollToBottom(500) - setTimeout(() => { - setEnablePagination(true) - }, 500) - }, 200) - }, []) - - // Loading spinner stuff - const [ready, setReady] = useState<{ - log: boolean - wayfinder: boolean - icebox: boolean - }>({ - log: false, - wayfinder: false, - icebox: false, - }) - const isLoading = useMemo( - () => Object.values(ready).some(ready => ready === false), - [ready], - ) + // useEffect(() => { + // setTimeout(() => { + // // TODO: See if ionViewDidEnter works better than setTimeout + // console.debug('Scrolling to bottom', contentRef.current) + // contentRef.current?.scrollToBottom(500) + // setTimeout(() => { + // setEnablePagination(true) + // }, 500) + // }, 200) + // }, []) // Query stuff const [logLimit, setLogLimit] = useState(7) @@ -178,66 +170,373 @@ export const TodoLists = ({}: {}) => { } }, [contentRef, fab, openCreateTodoModal]) + const { inActiveStarRoles, query } = useView() + + const todos = useLiveQuery(async () => { + const logTodosPromise = db.todos + .orderBy('completedAt') + .reverse() + .filter( + todo => + !!todo.completedAt && + matchesQuery(query, todo) && + inActiveStarRoles(todo), + ) + .limit(logLimit) + .toArray() + + const todoOrderItems = await db.wayfinderOrder.orderBy('order').toArray() + const todoIds = todoOrderItems.map(({ todoId }) => todoId) + const wayfinderTodosPromise = db.todos.bulkGet(todoIds).then(todoIds => { + return todoIds + .map((todo, index) => ({ + ...todo!, + order: todoOrderItems[index].order, + })) + .filter( + todo => matchesQuery(query, todo) && inActiveStarRoles(todo), + ) as (Todo & { order: string })[] + }) + + const iceboxTodosPromise = db.todos + .where('id') + .noneOf(todoOrderItems.map(({ todoId }) => todoId)) + .and( + todo => + todo.completedAt === undefined && + matchesQuery(query, todo) && + inActiveStarRoles(todo), + ) + .reverse() + .limit(iceboxLimit) + .toArray() + + return Promise.all([ + logTodosPromise, + wayfinderTodosPromise, + iceboxTodosPromise, + ]) + }, [inActiveStarRoles, iceboxLimit, logLimit, query]) + + const loading = todos === undefined + + const starRoles = useLiveQuery(() => db.starRoles.toArray()) + + const [debug, setDebug] = useState('') + useEffect(() => { + const params = new URLSearchParams(window.location.search) + const searchQuery = params.get('debug') + if (searchQuery) { + setDebug(searchQuery) + } + }, []) + const todosCount = useMemo( + () => todos?.reduce((acc, todos) => acc + todos.length, 0), + [todos], + ) + + // Its possible for ref not to change when todo is completed because one other than 'next' is completed in which case starship doesn't move + // Consider using a callback ref instead: https://stackoverflow.com/questions/60881446/receive-dimensions-of-element-via-getboundingclientrect-in-react + const nextTodoRef = useRef(null) + const { + nextTodo: { + position: [_, setNextTodoPosition], + }, + } = useTodoContext() + // TODO: When dev tools aren't open the todo has zero height + // useLayoutEffect doesn't work + // setTimeout 0 doesn't work + // callbackRef doesn't work + // This person thinks its an Ionic bug but I'm not sure: https://stackoverflow.com/questions/60881446/receive-dimensions-of-element-via-getboundingclientrect-in-react + useEffect(() => { + setTimeout(() => { + if (nextTodoRef.current) { + console.debug( + 'Setting next todo with ID', + nextTodoRef.current.dataset.id, + ) + setNextTodoPosition(nextTodoRef.current.getBoundingClientRect()) // Send this rather than the current ref as if unchanged then is will be memoised and nothing will happen. + } else { + console.debug('No next todo ref') + setNextTodoPosition(null) // Send this rather than the current ref as if unchanged then is will be memoised and nothing will happen. + } + }, 1) + }, [nextTodoRef, setNextTodoPosition, todos]) // The todos dep is used as an imperfect proxy for one the position of the next todo changes + + const [present] = useTodoActionSheet() + return ( - {isLoading && ( -
- -
- )} - <> - { - setLogLimit(limit => limit + 10) - setTimeout(() => event.target.complete(), 500) - }} - > - - - setReady(state => ({ ...state, log: true }))} - /> - setReady(state => ({ ...state, wayfinder: true }))} - /> - setReady(state => ({ ...state, icebox: true }))} - /> - { - setIceboxLimit(limit => limit + 10) - setTimeout(() => event.target.complete(), 500) - }} - > - - - - - - - - + + + + + + + + + + + + {/* TODO: Use suspense instead */} + {loading ? ( +
+ +
+ ) : todosCount === 0 ? ( +
+ +

Create some todos to get started

+
+ ) : ( + <> + { + setLogLimit(limit => limit + 10) + setTimeout(() => event.target.complete(), 500) + }} + > + + + + {todos[0].sort(byDate).map(todo => ( + { + present(todo) + }} + > + { + // Prevents the IonItem onClick from firing when completing a todo + event.stopPropagation() + }} + onIonChange={event => { + db.transaction( + 'rw', + db.wayfinderOrder, + db.todos, + async () => { + const wayfinderOrder = await db.wayfinderOrder + .orderBy('order') + .limit(1) + .keys() + await Promise.all([ + db.wayfinderOrder.add({ + todoId: todo.id, + order: order( + undefined, + wayfinderOrder[0]?.toString(), + ), + }), + db.todos.update(todo.id, { + completedAt: event.detail.checked + ? new Date() + : undefined, + }), + ]) + }, + ) + }} + checked={!!todo.completedAt} + /> + {todo?.title} + + ))} + { + // We don't use this to reorder for us because it results in a flash of 'unordered' content. + // Instead we re-order right away, calculate the new order ourselves, and update the DB. + event.detail.complete() + + const wayfinderTodos = await db.wayfinderOrder + .orderBy('order') + .toArray() + /* If the todo moves down then all the todos after its target location must be nudged up + * If the todo moves up then all the todos + */ + // TODO: Could make this easier with IDs in the DOM + const fromTodo = todos[1][event.detail.from] + const toTodo = todos[1][event.detail.to] + const unfilteredFromIndex = wayfinderTodos.findIndex( + ({ todoId }) => todoId === fromTodo.id, + ) + const unfilteredToIndex = wayfinderTodos.findIndex( + ({ todoId }) => todoId === toTodo.id, + ) + + const [startIndex, endIndex] = calculateReorderIndices( + unfilteredFromIndex, + unfilteredToIndex, + ) + const start = wayfinderTodos[startIndex]?.order + const end = wayfinderTodos[endIndex]?.order + const newOrder = order(start, end) + + console.debug('Re-ordering', { + unfilteredFromIndex, + unfilteredToIndex, + start, + end, + newOrder, + }) + + await db.wayfinderOrder.update(fromTodo.id, { + order: newOrder, + }) + }} + > + {todos[1].map((todo, index) => ( +
+ { + // Prevent the action sheet from opening when reordering + if (event.target['localName'] === 'ion-item') return + + present(todo, { + buttons: [ + { + text: 'Move to icebox', + data: { + action: 'icebox', + }, + handler: async () => { + db.transaction( + 'rw', + db.wayfinderOrder, + async () => { + await db.wayfinderOrder.delete(todo.id) + }, + ) + }, + }, + ], + }) + }} + > + { + // Prevents the IonItem onClick from firing when completing a todo + event.stopPropagation() + }} + onIonChange={async event => { + db.transaction( + 'rw', + db.wayfinderOrder, + db.todos, + async () => { + await Promise.all([ + db.wayfinderOrder.delete(todo.id), + db.todos.update(todo.id, { + completedAt: event.detail.checked + ? new Date() + : undefined, + }), + ]) + }, + ) + }} + /> + {todo?.title} + {debug && ( + + {todo.id} + + {todo.order} + + + )} + {todo.starRole && ( + starRole.id === todo.starRole, + )?.icon?.name, + )} + slot="end" + /> + )} + {todo.note && ( + + + + )} + + +
+ ))} +
+
+ + { + setIceboxLimit(limit => limit + 10) + setTimeout(() => event.target.complete(), 500) + }} + > + + + + + )} +
+ +
+
+
+
) } @@ -509,310 +808,41 @@ export const ViewMenu = () => { ) } -export const Log = ({ - limit, - onLoad, +export const Journey = ({ + commonAncestor, }: { - limit: number - onLoad: () => void + commonAncestor: RefObject }) => { - const { inActiveStarRoles, query } = useView() - - const todos = useLiveQuery(async () => { - console.debug('Re-running log query') - return db.todos - .orderBy('completedAt') - .reverse() - .filter( - todo => - !!todo.completedAt && - matchesQuery(query, todo) && - inActiveStarRoles(todo), - ) - .limit(limit) - .toArray() - }, [inActiveStarRoles, limit, query]) - - useEffect(() => { - if (todos !== undefined) { - onLoad() - } - }, [todos]) - - const [present] = useTodoActionSheet() - - if (todos === undefined) return null - - return ( -
-

Log

- {todos?.length ? ( - - {todos.sort(byDate).map(todo => ( - { - present(todo) - }} - > - { - // Prevents the IonItem onClick from firing when completing a todo - event.stopPropagation() - }} - onIonChange={async event => { - db.transaction( - 'rw', - db.wayfinderOrder, - db.todos, - async () => { - const wayfinderOrder = await db.wayfinderOrder - .orderBy('order') - .limit(1) - .keys() - await Promise.all([ - db.wayfinderOrder.add({ - todoId: todo.id, - order: order(undefined, wayfinderOrder[0].toString()), - }), - db.todos.update(todo.id, { - completedAt: event.detail.checked - ? new Date() - : undefined, - }), - ]) - }, - ) - }} - checked={!!todo.completedAt} - /> - {todo?.title} - - ))} - - ) : ( -
- -

Your completed todos will appear here

-
- )} -
+ const { + nextTodo: { + position: [nextTodoPosition], + }, + } = useTodoContext() + const starship = useRef(null) + const [starshipY] = useStarshipYPosition( + starship?.current, + nextTodoPosition, + commonAncestor?.current, ) -} - -export const Wayfinder = ({ onLoad }: { onLoad: () => void }) => { - const { inActiveStarRoles, query } = useView() - - const todos = useLiveQuery(async () => { - const todoOrderItems = await db.wayfinderOrder.orderBy('order').toArray() - const todoIds = todoOrderItems.map(({ todoId }) => todoId) - return (await db.todos.bulkGet(todoIds)) - .map((todo, index) => ({ ...todo!, order: todoOrderItems[index].order })) - .filter( - todo => matchesQuery(query, todo) && inActiveStarRoles(todo), - ) as (Todo & { order: string })[] - }, [inActiveStarRoles, query]) - - const starRoles = useLiveQuery(() => db.starRoles.toArray()) - - useEffect(() => { - if (todos !== undefined) { - setTimeout(onLoad, 2000) - } - }, [todos]) - - const [debug, setDebug] = useState('') - useEffect(() => { - const params = new URLSearchParams(window.location.search) - const searchQuery = params.get('debug') - if (searchQuery) { - setDebug(searchQuery) - } - }, []) - - const [present] = useTodoActionSheet() - - if (todos === undefined) return null return ( -
-

Wayfinder

- {todos?.length ? ( - - { - // We don't use this to reorder for us because it results in a flash of 'unordered' content. - // Instead we re-order right away, calculate the new order ourselves, and update the DB. - event.detail.complete() - - const wayfinderTodos = await db.wayfinderOrder - .orderBy('order') - .toArray() - /* If the todo moves down then all the todos after its target location must be nudged up - * If the todo moves up then all the todos - */ - // TODO: Could make this easier with IDs in the DOM - const fromTodo = todos[event.detail.from] - const toTodo = todos[event.detail.to] - const unfilteredFromIndex = wayfinderTodos.findIndex( - ({ todoId }) => todoId === fromTodo.id, - ) - const unfilteredToIndex = wayfinderTodos.findIndex( - ({ todoId }) => todoId === toTodo.id, - ) - - const [startIndex, endIndex] = calculateReorderIndices( - unfilteredFromIndex, - unfilteredToIndex, - ) - const start = wayfinderTodos[startIndex]?.order - const end = wayfinderTodos[endIndex]?.order - const newOrder = order(start, end) - - console.debug('Re-ordering', { - unfilteredFromIndex, - unfilteredToIndex, - start, - end, - newOrder, - }) - - await db.wayfinderOrder.update(fromTodo.id, { - order: newOrder, - }) - }} - > - {todos.map(todo => ( - { - // Prevent the action sheet from opening when reordering - if (event.target['localName'] === 'ion-item') return - - present(todo, { - buttons: [ - { - text: 'Move to icebox', - data: { - action: 'icebox', - }, - handler: async () => { - db.transaction('rw', db.wayfinderOrder, async () => { - await db.wayfinderOrder.delete(todo.id) - }) - }, - }, - ], - }) - }} - key={todo.id} - > - { - // Prevents the IonItem onClick from firing when completing a todo - event.stopPropagation() - }} - onIonChange={async event => { - db.transaction( - 'rw', - db.wayfinderOrder, - db.todos, - async () => { - await Promise.all([ - db.wayfinderOrder.delete(todo.id), - db.todos.update(todo.id, { - completedAt: event.detail.checked - ? new Date() - : undefined, - }), - ]) - }, - ) - }} - /> - {todo?.title} - {debug && ( - - {todo.id} - {todo.order} - - )} - {todo.starRole && ( - starRole.id === todo.starRole) - ?.icon?.name, - )} - slot="end" - /> - )} - {todo.note && ( - - - - )} - - - ))} - - - ) : ( -
- -

Create some todos to get started

-
- )} -
+
+ +
+ +
+
) } -export const Icebox = ({ - limit, - onLoad, -}: { - limit: number - onLoad: () => void -}) => { - const { inActiveStarRoles, query } = useView() - - const todos = useLiveQuery(async () => { - console.debug('Re-running icebox query') - const todoOrderItems = await db.wayfinderOrder.orderBy('order').toArray() - return db.todos - .where('id') - .noneOf(todoOrderItems.map(({ todoId }) => todoId)) - .and( - todo => - todo.completedAt === undefined && - matchesQuery(query, todo) && - inActiveStarRoles(todo), - ) - .reverse() - .limit(limit) - .toArray() - }, [limit, inActiveStarRoles, query]) - - useEffect(() => { - if (todos !== undefined) onLoad() - }, [todos]) - +export const Icebox = ({ todos }: { todos: Todo[] }) => { const [present] = useTodoActionSheet() const onClick = useCallback( todo => { @@ -869,51 +899,31 @@ export const Icebox = ({ if (todos === undefined) return null return ( -
- -

Icebox

- - {todos === undefined ? ( - - ) : ( - todos.map(todo => ( - - )) - )} +
+ + + {todos.map(todo => ( + { + onClick(todo) + }} + > + + {todo.title} + + + ))}
) } -export const IceboxItem = ({ - onClick, - todo, -}: { - onClick: (todo: Todo) => void - todo: Todo -}) => { - return ( - { - onClick(todo) - }} - > - - {todo.title} - - - ) -} - export const Searchbar = () => { const searchbarRef = useRef(null) @@ -954,12 +964,6 @@ export const Searchbar = () => { ) } -const removeItemFromArray = (array: any[], index: number): any[] => { - const newArray = [...array] - newArray.splice(index, 1) - return newArray -} - const byDate = (a: any, b: any) => { const dateA = new Date(a.completedAt) const dateB = new Date(b.completedAt) diff --git a/components/todos/SelectedTodo.tsx b/components/todos/SelectedTodo.tsx deleted file mode 100644 index 1549997..0000000 --- a/components/todos/SelectedTodo.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { - createContext, - Dispatch, - SetStateAction, - useContext, - useState, -} from 'react' -import { Todo } from '../db' - -export const SelecteTodoContext = createContext< - [todo: Todo | null, setTodo: Dispatch>] ->([null, () => null]) - -export function SelectedTodoProvider({ - children, -}: { - children: React.ReactNode -}) { - const [todo, setTodo] = useState(null) - - return ( - - {children} - - ) -} - -export default function useSelectedTodo() { - return useContext(SelecteTodoContext) -} diff --git a/components/todos/TodoContext.tsx b/components/todos/TodoContext.tsx new file mode 100644 index 0000000..a13a76b --- /dev/null +++ b/components/todos/TodoContext.tsx @@ -0,0 +1,50 @@ +import { + createContext, + Dispatch, + SetStateAction, + useContext, + useState, +} from 'react' +import { Todo } from '../db' + +export const TodoContext = createContext<{ + nextTodo: { + position: [ + get: DOMRect | null, + set: Dispatch>, + ] + } + selectedTodo: [ + todo: Todo | null, + setTodo: Dispatch>, + ] +}>({ + nextTodo: { position: [null, () => null] }, + selectedTodo: [null, () => null], +}) + +export function TodoContextProvider({ + children, +}: { + children: React.ReactNode +}) { + const [nextTodoPosition, setNextTodoPosition] = useState(null) + const [selectedTodo, setSelectedTodo] = useState(null) + + return ( + + {children} + + ) +} + +export default function useTodoContext() { + return useContext(TodoContext) +} diff --git a/components/todos/create/useCreateTodoModal.ts b/components/todos/create/useCreateTodoModal.ts index 9a7ab06..2c3439d 100644 --- a/components/todos/create/useCreateTodoModal.ts +++ b/components/todos/create/useCreateTodoModal.ts @@ -5,7 +5,7 @@ import { db, ListType, Todo } from '../../db' import useNoteProvider from '../../notes/useNoteProvider' import { CreateTodoModal } from './modal' import order from '../../common/order' -import useSelectedTodo from '../SelectedTodo' +import useTodoContext from '../TodoContext' export function useCreateTodoModal(): [ ({ @@ -17,7 +17,9 @@ export function useCreateTodoModal(): [ }) => void, (data?: any, role?: string) => void, ] { - const [todo, setTodo] = useSelectedTodo() + const { + selectedTodo: [todo, setTodo], + } = useTodoContext() const titleInput = useRef(null) const [present, dismiss] = useIonModal(CreateTodoModal, { dismiss: (data: string, role: string) => dismiss(data, role), diff --git a/components/todos/edit/useEditTodoModal.ts b/components/todos/edit/useEditTodoModal.ts index 3e716a1..0afc6cb 100644 --- a/components/todos/edit/useEditTodoModal.ts +++ b/components/todos/edit/useEditTodoModal.ts @@ -2,14 +2,16 @@ import { useIonModal } from '@ionic/react' import { useCallback, useRef } from 'react' import { Todo, db } from '../../db' import useNoteProvider from '../../notes/useNoteProvider' -import useSelectedTodo from '../SelectedTodo' +import useTodoContext from '../TodoContext' import { EditTodoModal } from './modal' export function useEditTodoModal(): [ (todo: Todo) => void, (data?: any, role?: string) => void, ] { - const [todo, setTodo] = useSelectedTodo() + const { + selectedTodo: [todo, setTodo], + } = useTodoContext() const titleInput = useRef(null) const [present, dismiss] = useIonModal(EditTodoModal, { dismiss: (data: string, role: string) => dismiss(data, role), diff --git a/components/todos/index.tsx b/components/todos/index.tsx index f21afc2..f6ef482 100644 --- a/components/todos/index.tsx +++ b/components/todos/index.tsx @@ -15,7 +15,9 @@ export default function Todos({ () => todos.sort((a, b) => { if (a.completedAt || b.completedAt) { - return a.completedAt!.getTime() || 0 - b.completedAt!.getTime() || 0 + return ( + (a.completedAt?.getTime() || 0) - (b.completedAt?.getTime() || 0) + ) } return Number(b.rank) - Number(a.rank) }), diff --git a/components/view/index.tsx b/components/view/index.tsx index e1ad815..0fccf72 100644 --- a/components/view/index.tsx +++ b/components/view/index.tsx @@ -66,7 +66,7 @@ export function ViewProvider({ children }: { children: React.ReactNode }) { const inActiveStarRoles = useCallback( (todo: Todo) => { if (todo.starRole === 'str0PBouoSd4NWkh6Em771Nj0Ojcom') { - console.log('in active star role', { + console.debug('in active star role', { todo, allStarRolesActive, activeStarRoles, diff --git a/cypress/e2e/spec.cy.ts b/cypress/e2e/spec.cy.ts index 4341fa4..9242ea0 100644 --- a/cypress/e2e/spec.cy.ts +++ b/cypress/e2e/spec.cy.ts @@ -5,10 +5,6 @@ before(() => { it('works', () => { cy.visit('/home') - cy.get('#log').contains('Log').should('be.visible') - cy.get('#wayfinder').contains('Wayfinder').should('be.visible') - cy.get('#icebox').contains('Icebox').should('be.visible') - cy.get('ion-fab-button').click() cy.get('ion-modal').within(() => { cy.contains('label', 'Title') @@ -18,7 +14,7 @@ it('works', () => { cy.contains('Confirm').click() }) - assertLists([], [], ['take the bins out']) + assertLists([], ['take the bins out']) cy.get('#view-menu-button').click() cy.contains('ion-button', 'Edit roles') @@ -76,41 +72,39 @@ it('works', () => { cy.get('ion-modal').contains('Confirm').click() assertLists( - [], [], ['plan birthday day out', 'be silly together', 'take the bins out'], ) cy.get('#icebox').contains('take the bins out').click() cy.get('#todo-action-sheet').contains('Move to wayfinder').click() - cy.get('#wayfinder').contains('take the bins out') + cy.get('#log-and-wayfinder').contains('take the bins out') cy.get('#todo-action-sheet').should('not.exist') cy.get('#icebox').contains('be silly together').click() cy.get('#todo-action-sheet').contains('Move to wayfinder').click() - cy.get('#wayfinder').contains('be silly together') + cy.get('#log-and-wayfinder').contains('be silly together') cy.get('#todo-action-sheet').should('not.exist') cy.get('#icebox').contains('plan birthday day out').click() cy.get('#todo-action-sheet').contains('Move to wayfinder').click() - cy.get('#wayfinder').contains('plan birthday day out') + cy.get('#log-and-wayfinder').contains('plan birthday day out') cy.get('#todo-action-sheet').should('not.exist') assertLists( - [], ['plan birthday day out', 'be silly together', 'take the bins out'], [], ) // reorderImportantTodo(0, 2) - // cy.get('#wayfinder .todo') + // cy.get('#log-and-wayfinder .todo') // .first() // .find('ion-reorder') // .shadow() // .find('.reorder-icon') // .shadow() // .find('svg') - // .drag('#wayfinder .todo:nth-child(3)') + // .drag('#log-and-wayfinder .todo:nth-child(3)') // assertLists( // [], // ['be silly together', 'plan birthday day out', 'take the bins out'], @@ -129,25 +123,19 @@ it('works', () => { // [], // ) - cy.get('#wayfinder') + cy.get('#log-and-wayfinder') .contains('.todo', 'take the bins out') .find('ion-checkbox') .click() assertLists( - ['take the bins out'], - ['plan birthday day out', 'be silly together'], + ['take the bins out', 'plan birthday day out', 'be silly together'], [], ) }) -function assertLists(log: string[], wayfinder: string[], icebox: string[]) { - cy.get('#log .todo') - .should('have.length', log.length) - .invoke('toArray') - .invoke('map', item => item.textContent) - .should('deep.equal', log) - cy.get('#wayfinder .todo') +function assertLists(wayfinder: string[], icebox: string[]) { + cy.get('#log-and-wayfinder .todo') .should('have.length', wayfinder.length) .invoke('toArray') .invoke('map', item => item.textContent) @@ -160,7 +148,7 @@ function assertLists(log: string[], wayfinder: string[], icebox: string[]) { } function reorderWayfinderTodo(todoIndex: number, places: number) { - cy.get(`#wayfinder .todo`) + cy.get(`#log-and-wayfinder .todo`) .eq(todoIndex) .find('ion-reorder') .shadow() diff --git a/package.json b/package.json index 0c58f26..1dec8c0 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@ionic/react-router": "7.7.4", "@types/lodash": "^4.17.6", "classnames": "2.5.1", + "clsx": "^2.1.1", "convex": "^1.11.3", "debounce": "^1.2.1", "dexie": "^4.0.9", @@ -56,7 +57,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-router": "5.3.4", - "react-router-dom": "5.3.4" + "react-router-dom": "5.3.4", + "tailwind-merge": "^2.5.4" }, "devDependencies": { "@4tw/cypress-drag-drop": "^2.2.5",