Skip to content

Commit

Permalink
♻️ Use same modal for create and edit todo
Browse files Browse the repository at this point in the history
  • Loading branch information
homostellaris committed Jul 19, 2024
1 parent 6a9cc3c commit a404d24
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 222 deletions.
120 changes: 8 additions & 112 deletions components/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import useNoteProvider from '../notes/useNoteProvider'
import useSettings from '../settings/useSettings'
import { SelectedTodoProvider } from '../todos/SelectedTodo'
import { useTodoActionSheet } from '../todos/TodoActionSheet'
import { useCreateTodoModal } from '../todos/CreateTodo'

const Home = () => {
// Search stuff
Expand Down Expand Up @@ -115,11 +116,14 @@ const Home = () => {

// Creating todo stuff
const fab = useRef<HTMLIonFabElement>(null)
const [createTodoModalOpen, setCreateTodoModalOpen] = useState(false)
const [presentCreateTodoModal, dismiss] = useCreateTodoModal()
const openCreateTodoModal = useCallback(() => {
setCreateTodoModalOpen(true)
if (fab.current) fab.current.activated = true
}, [fab])
presentCreateTodoModal({
onWillDismiss: () => {
if (fab.current) fab.current.activated = false
},
})
}, [fab, presentCreateTodoModal])

const contentRef = useRef<HTMLIonContentElement>(null)

Expand Down Expand Up @@ -201,13 +205,6 @@ const Home = () => {
<IonIcon icon={add}></IonIcon>
</IonFabButton>
</IonFab>
<CreateTodoModal
fab={fab}
open={createTodoModalOpen}
onWillDismiss={() => {
setCreateTodoModalOpen(false)
}}
/>
</>
)}
</IonContent>
Expand Down Expand Up @@ -735,107 +732,6 @@ export const IceboxItem = ({
)
}

export const CreateTodoModal = ({
fab,
open,
onWillDismiss,
}: {
fab: RefObject<HTMLIonFabElement>
open: boolean
onWillDismiss: ComponentProps<typeof IonModal>['onWillDismiss']
}) => {
const modal = useRef<HTMLIonModalElement>(null)

const input = useRef<HTMLIonInputElement>(null)
const noteInput = useRef<HTMLIonTextareaElement>(null)
const noteProvider = useNoteProvider()

const createTodo = useCallback(
async ({ note, title }: { note?: any; title: any }) => {
let uri
if (note && noteProvider) {
uri = await noteProvider.create({ content: note })
}
await db.todos.add({
createdAt: new Date(),
title,
...(uri && { note: { uri } }),
})
},
[noteProvider],
)

return (
<IonModal
isOpen={open}
ref={modal}
onDidPresent={() => input.current?.setFocus()}
onKeyDown={event => {
if (event.key === 'Enter') {
event.preventDefault()
modal.current?.dismiss({}, 'confirm')
}
}}
onWillDismiss={event => {
onWillDismiss?.(event)
if (event.detail.role === 'confirm') {
createTodo({
title: input.current?.value,
note: noteInput.current?.value,
})
}
if (fab.current) fab.current.activated = false
}}
>
<IonHeader translucent>
<IonToolbar>
<IonTitle>Create todo</IonTitle>
<IonButtons slot="secondary">
<IonButton
role="cancel"
onClick={() => modal.current?.dismiss({}, 'cancel')}
>
Cancel
</IonButton>
</IonButtons>
<IonButtons slot="primary">
<IonButton
onClick={() => {
modal.current?.dismiss({}, '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"
/>
{!noteProvider && (
<p>Set a note provider in settings to enable this feature.</p>
)}
<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>
</IonModal>
)
}

function moveItemInArray<T>(
array: T[],
fromIndex: number,
Expand Down
56 changes: 56 additions & 0 deletions components/todos/CreateTodo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { IonHeader, IonModal, useIonModal } from '@ionic/react'
import { ComponentProps, RefObject, useCallback, useRef } from 'react'
import useNoteProvider from '../notes/useNoteProvider'
import { db } from '../db'
import TodoModal from './TodoModal'
import { HookOverlayOptions } from '@ionic/react/dist/types/hooks/HookOverlayOptions'

export function CreateTodoModal({
dismiss,
}: {
dismiss: (data?: any, role?: string) => void
}) {
return <TodoModal dismiss={dismiss} />
}

export function useCreateTodoModal(): [
({
onWillDismiss,
}: {
onWillDismiss: HookOverlayOptions['onWillDismiss']
}) => void,
(data?: any, role?: string) => void,
] {
const [present, dismiss] = useIonModal(CreateTodoModal, {
dismiss: (data: string, role: string) => dismiss(data, role),
})

const noteProvider = useNoteProvider()
const createTodo = useCallback(
async ({ note, title }: { note?: any; title: any }) => {
let uri
if (note && noteProvider) {
uri = await noteProvider.create({ content: note })
}
await db.todos.add({
createdAt: new Date(),
title,
...(uri && { note: { uri } }),
})
},
[noteProvider],
)

return [
({ onWillDismiss }: HookOverlayOptions) => {
present({
onWillDismiss: event => {
const todo = event.detail.data
if (event.detail.role === 'confirm') createTodo(todo)
onWillDismiss?.(event)
},
})
},
dismiss,
]
}
121 changes: 11 additions & 110 deletions components/todos/EditTodo.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInput,
IonPage,
IonTextarea,
IonTitle,
IonToolbar,
useIonModal,
} from '@ionic/react'
import { openOutline } from 'ionicons/icons'
import { useCallback, useEffect, useRef } from 'react'
import { useIonModal } from '@ionic/react'
import { useCallback } from 'react'
import { CreatedTodo, db } from '../db'
import useNoteProvider from '../notes/useNoteProvider'
import useSelectedTodo from './SelectedTodo'
import TodoModal from './TodoModal'

export function EditTodoModal({
dismiss,
Expand All @@ -24,105 +12,18 @@ export function EditTodoModal({
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()

return (
<IonPage
ref={page}
onKeyDown={event => {
if (event.key === 'Enter') {
event.preventDefault()
dismiss(
{
...todo,
title: input.current?.value,
note: noteInput.current?.value,
},
'confirm',
)
}
}}
>
<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>
<TodoModal
todo={todo}
dismiss={dismiss}
/>
)
}

export function useEditTodoModal() {
export function useEditTodoModal(): [
(todo: CreatedTodo) => void,
(data?: any, role?: string) => void,
] {
const [todo, setTodo] = useSelectedTodo()
const [present, dismiss] = useIonModal(EditTodoModal, {
dismiss: (data: string, role: string) => dismiss(data, role),
Expand Down
Loading

0 comments on commit a404d24

Please sign in to comment.