Skip to content

Commit

Permalink
✨ Edit todos
Browse files Browse the repository at this point in the history
  • Loading branch information
homostellaris committed Jul 17, 2024
1 parent e1361de commit a0afc6a
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 101 deletions.
99 changes: 49 additions & 50 deletions components/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { menuController } from '@ionic/core/components'
import _ from 'lodash'
import {
ActionSheetButton,
IonActionSheet,
IonButton,
IonButtons,
IonCard,
Expand Down Expand Up @@ -43,12 +40,12 @@ import { useLiveQuery, useObservable } from 'dexie-react-hooks'
import {
add,
checkmarkDoneCircleSharp,
filterSharp,
rocketSharp,
cloudOfflineSharp,
cloudDoneSharp,
logOutSharp,
cloudOfflineSharp,
documentText,
filterSharp,
logOutSharp,
rocketSharp,
} from 'ionicons/icons'
import {
ComponentProps,
Expand All @@ -59,14 +56,12 @@ import {
useState,
} from 'react'
import { Link } from 'react-router-dom'
import { CreatedTodo, db, Todo } from '../db'
import { CreatedTodo, db } from '../db'
import NoteProviders from '../notes/providers'
import useSettings from '../settings/useSettings'
import useNoteProvider from '../notes/useNoteProvider'
import {
TodoActionSheetProvider,
useTodoActionSheet,
} from '../todos/TodoActionSheet'
import useSettings from '../settings/useSettings'
import { SelectedTodoProvider } from '../todos/SelectedTodo'
import { useTodoActionSheet } from '../todos/TodoActionSheet'

const Home = () => {
// Search stuff
Expand Down Expand Up @@ -178,11 +173,11 @@ const Home = () => {
>
<IonInfiniteScrollContent></IonInfiniteScrollContent>
</IonInfiniteScroll>
<TodoActionSheetProvider>
<SelectedTodoProvider>
<Log todos={logTodos} />
<Important todos={importantTodos} />
<Icebox todos={iceboxTodos} />
</TodoActionSheetProvider>
</SelectedTodoProvider>
<IonInfiniteScroll
disabled={!enablePagination}
position="bottom"
Expand Down Expand Up @@ -472,7 +467,7 @@ export const FilterMenu = () => {
}

export const Log = ({ todos }: { todos: any[] }) => {
const todoActionSheet = useTodoActionSheet()
const [present] = useTodoActionSheet()

return (
<>
Expand All @@ -484,7 +479,7 @@ export const Log = ({ todos }: { todos: any[] }) => {
button
key={todo.id}
onClick={_event => {
todoActionSheet.open(todo)
present(todo)
}}
>
<IonCheckbox
Expand Down Expand Up @@ -544,7 +539,7 @@ export const Important = ({ todos }: { todos: any[] }) => {
order: reorderedTodoIds,
})
}
const todoActionSheet = useTodoActionSheet()
const [present] = useTodoActionSheet()

return (
<>
Expand All @@ -559,25 +554,27 @@ export const Important = ({ todos }: { todos: any[] }) => {
<IonItem
button
onClick={_event => {
todoActionSheet.open(todo, [
{
text: 'Move to icebox',
data: {
action: 'icebox',
},
handler: async () => {
db.transaction('rw', db.lists, async () => {
const list = await db.lists.get('#important')
await db.lists.update('#important', {
order: removeItemFromArray(
list!.order,
list!.order.indexOf(todo.id),
),
present(todo, {
buttons: [
{
text: 'Move to icebox',
data: {
action: 'icebox',
},
handler: async () => {
db.transaction('rw', db.lists, async () => {
const list = await db.lists.get('#important')
await db.lists.update('#important', {
order: removeItemFromArray(
list!.order,
list!.order.indexOf(todo.id),
),
})
})
})
},
},
},
])
],
})
}}
key={todo.id}
>
Expand Down Expand Up @@ -661,28 +658,30 @@ export const IceboxItem = ({
title: string
}
}) => {
const todoActionSheet = useTodoActionSheet()
const [present] = useTodoActionSheet()

return (
<IonCard
className="cursor-pointer"
onClick={() => {
todoActionSheet.open(todo as CreatedTodo, [
{
text: 'Move to ranked',
data: {
action: 'ranked',
},
handler: async () => {
db.transaction('rw', db.lists, async () => {
const list = await db.lists.get('#important')
db.lists.update('#important', {
order: [...list!.order, todo.id],
present(todo as CreatedTodo, {
buttons: [
{
text: 'Move to ranked',
data: {
action: 'ranked',
},
handler: async () => {
db.transaction('rw', db.lists, async () => {
const list = await db.lists.get('#important')
db.lists.update('#important', {
order: [...list!.order, todo.id],
})
})
})
},
},
},
])
],
})
}}
>
<IonCardHeader>
Expand Down
166 changes: 166 additions & 0 deletions components/todos/EditTodo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInput,
IonPage,
IonTextarea,
IonTitle,
IonToolbar,
useIonModal,
} from '@ionic/react'
import { openOutline } from 'ionicons/icons'
import { useCallback, useEffect, useRef, useState } from 'react'
import useNoteProvider from '../notes/useNoteProvider'
import { CreatedTodo, db } from '../db'
import useSelectedTodo from './SelectedTodo'

export function EditTodoModal({
dismiss,
todo,
}: {
dismiss: (data?: any, role?: string) => void
todo: CreatedTodo
}) {
const page = useRef<HTMLIonModalElement>(null)
const input = useRef<HTMLIonInputElement>(null)
const noteInput = useRef<HTMLIonTextareaElement>(null)

useEffect(() => {
input.current?.setFocus()
}, [])

const noteProvider = useNoteProvider()

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault()
dismiss(
{
title: input.current?.value,
note: noteInput.current?.value,
},
'confirm',
)
}
}
page.current?.addEventListener('keydown', handleKeyDown)
return () => {
page.current?.removeEventListener('keydown', handleKeyDown)
}
}, [dismiss])

return (
<IonPage ref={page}>
<IonHeader>
<IonToolbar>
<IonTitle>Edit todo</IonTitle>
<IonButtons slot="secondary">
<IonButton
role="cancel"
onClick={() => dismiss(null, 'cancel')}
>
Cancel
</IonButton>
</IonButtons>
<IonButtons slot="primary">
<IonButton
onClick={() => {
dismiss(
{
...todo,
title: input.current?.value,
note: noteInput.current?.value,
},
'confirm',
)
}}
strong={true}
>
Confirm
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent className="space-y-4 ion-padding">
<IonInput
fill="outline"
ref={input}
type="text"
label="Title"
labelPlacement="floating"
value={todo?.title}
/>
{!noteProvider && (
<p>Set a note provider in settings to enable this feature.</p>
)}
{todo?.note ? (
<div>
<a
className="space-x-1"
href={todo?.note?.uri}
target="_blank"
rel="noreferrer"
>
<span>Open note</span>
<IonIcon icon={openOutline} />
</a>
</div>
) : (
<IonTextarea
className="h-48"
disabled={!noteProvider}
helperText="A note with this initial content will be created with your note provider and linked to this todo."
fill="outline"
label="Note"
labelPlacement="floating"
placeholder="Write markdown here..."
ref={noteInput}
/>
)}
</IonContent>
</IonPage>
)
}

export function useEditTodoModal() {
const [todo, setTodo] = useSelectedTodo()
const [present, dismiss] = useIonModal(EditTodoModal, {
dismiss: (data: string, role: string) => dismiss(data, role),
todo,
})
const noteProvider = useNoteProvider()
const editTodo = useCallback(
async (updatedTodo: CreatedTodo) => {
let uri
if (updatedTodo.note && noteProvider) {
uri = await noteProvider.create({ content: updatedTodo.note })
}
await db.todos.update(updatedTodo.id, {
createdAt: new Date(),
title: updatedTodo.title,
...(uri && { note: { uri } }),
})
},
[noteProvider],
)

return [
(todo: CreatedTodo) => {
present({
onWillPresent: () => {
setTodo(todo)
},
onWillDismiss: event => {
const todo = event.detail.data
if (event.detail.role === 'confirm') editTodo(todo)
setTodo(null)
},
})
},
dismiss,
]
}
33 changes: 33 additions & 0 deletions components/todos/SelectedTodo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
createContext,
Dispatch,
SetStateAction,
useContext,
useState,
} from 'react'
import { CreatedTodo } from '../db'

export const SelecteTodoContext = createContext<
[
todo: CreatedTodo | null,
setTodo: Dispatch<SetStateAction<CreatedTodo | null>>,
]
>([null, () => null])

export function SelectedTodoProvider({
children,
}: {
children: React.ReactNode
}) {
const [todo, setTodo] = useState<CreatedTodo | null>(null)

return (
<SelecteTodoContext.Provider value={[todo, setTodo]}>
{children}
</SelecteTodoContext.Provider>
)
}

export default function useSelectedTodo() {
return useContext(SelecteTodoContext)
}
Loading

0 comments on commit a0afc6a

Please sign in to comment.