feat: show mobile note create state
This commit is contained in:
@@ -44,6 +44,8 @@ export function MobileApp() {
|
||||
const showsProperties = width >= 1120
|
||||
const [availableNotes, setAvailableNotes] = useState(fallbackNotes)
|
||||
const [compactNavigation, setCompactNavigation] = useState(() => createCompactNavigationState(fallbackNotes[0].id))
|
||||
const [createNoteFailed, setCreateNoteFailed] = useState(false)
|
||||
const [isCreatingNote, setIsCreatingNote] = useState(false)
|
||||
const [saveStateByNoteId, setSaveStateByNoteId] = useState<Record<string, MobileEditorSaveState>>({})
|
||||
const selectedNote = useMemo(
|
||||
() => availableNotes.find((note) => note.id === compactNavigation.selectedNoteId) ?? availableNotes[0],
|
||||
@@ -89,6 +91,12 @@ export function MobileApp() {
|
||||
}
|
||||
const saveDraft = useCallback((draft: MobileEditorDraft) => autosaveQueue.enqueue(draft), [autosaveQueue])
|
||||
const createNote = useCallback(() => {
|
||||
if (isCreatingNote) {
|
||||
return
|
||||
}
|
||||
|
||||
setCreateNoteFailed(false)
|
||||
setIsCreatingNote(true)
|
||||
void createDemoVaultNote()
|
||||
.then((note) => {
|
||||
if (!note) {
|
||||
@@ -98,8 +106,13 @@ export function MobileApp() {
|
||||
setAvailableNotes((notes) => [note, ...notes.filter((item) => item.id !== note.id)])
|
||||
setCompactNavigation((state) => transitionCompactNavigation(state, { type: 'selectNote', noteId: note.id }))
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
.catch(() => {
|
||||
setCreateNoteFailed(true)
|
||||
})
|
||||
.finally(() => {
|
||||
setIsCreatingNote(false)
|
||||
})
|
||||
}, [isCreatingNote])
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
@@ -110,6 +123,8 @@ export function MobileApp() {
|
||||
<NoteListPanel
|
||||
notes={availableNotes}
|
||||
selectedNoteId={compactNavigation.selectedNoteId}
|
||||
createNoteFailed={createNoteFailed}
|
||||
isCreatingNote={isCreatingNote}
|
||||
onCreateNote={createNote}
|
||||
onSelectNote={selectNote}
|
||||
/>
|
||||
@@ -125,6 +140,8 @@ export function MobileApp() {
|
||||
selectedNoteId={compactNavigation.selectedNoteId}
|
||||
onNavigate={(event) => setCompactNavigation((state) => transitionCompactNavigation(state, event))}
|
||||
onDraftChange={saveDraft}
|
||||
createNoteFailed={createNoteFailed}
|
||||
isCreatingNote={isCreatingNote}
|
||||
onCreateNote={createNote}
|
||||
onSelectNote={selectNote}
|
||||
/>
|
||||
@@ -141,6 +158,8 @@ function CompactShell({
|
||||
saveState,
|
||||
onNavigate,
|
||||
onDraftChange,
|
||||
createNoteFailed,
|
||||
isCreatingNote,
|
||||
onCreateNote,
|
||||
onSelectNote,
|
||||
selectedNoteId,
|
||||
@@ -149,6 +168,8 @@ function CompactShell({
|
||||
note: MobileNote
|
||||
notes: MobileNote[]
|
||||
saveState: MobileEditorSaveState
|
||||
createNoteFailed: boolean
|
||||
isCreatingNote: boolean
|
||||
onNavigate: (event: CompactNavigationEvent) => void
|
||||
onDraftChange: (draft: MobileEditorDraft) => void
|
||||
onCreateNote: () => void
|
||||
@@ -190,6 +211,8 @@ function CompactShell({
|
||||
<NoteListPanel
|
||||
notes={notes}
|
||||
selectedNoteId={selectedNoteId}
|
||||
createNoteFailed={createNoteFailed}
|
||||
isCreatingNote={isCreatingNote}
|
||||
onCreateNote={onCreateNote}
|
||||
onOpenSidebar={() => onNavigate({ type: 'openSidebar' })}
|
||||
onSelectNote={onSelectNote}
|
||||
@@ -233,12 +256,16 @@ function SidebarPanel({ onClose }: { onClose?: () => void }) {
|
||||
|
||||
function NoteListPanel({
|
||||
notes,
|
||||
createNoteFailed,
|
||||
isCreatingNote,
|
||||
onCreateNote,
|
||||
onOpenSidebar,
|
||||
onSelectNote,
|
||||
selectedNoteId,
|
||||
}: {
|
||||
notes: MobileNote[]
|
||||
createNoteFailed: boolean
|
||||
isCreatingNote: boolean
|
||||
onCreateNote: () => void
|
||||
onOpenSidebar?: () => void
|
||||
onSelectNote: (note: MobileNote) => void
|
||||
@@ -280,7 +307,17 @@ function NoteListPanel({
|
||||
</Pressable>
|
||||
)}
|
||||
/>
|
||||
<Pressable onPress={onCreateNote} style={styles.composeButton}>
|
||||
{createNoteFailed ? <Text style={styles.createNoteError}>Could not create note</Text> : null}
|
||||
<Pressable
|
||||
accessibilityLabel="Create note"
|
||||
disabled={isCreatingNote}
|
||||
onPress={onCreateNote}
|
||||
style={({ pressed }) => [
|
||||
styles.composeButton,
|
||||
isCreatingNote ? styles.composeButtonDisabled : null,
|
||||
pressed ? styles.pressed : null,
|
||||
]}
|
||||
>
|
||||
<PencilSimple size={28} color="#ffffff" />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { commonStyles } from './styles/commonStyles'
|
||||
import { editorSaveStateStyles } from './styles/editorSaveStateStyles'
|
||||
import { editorStyles } from './styles/editorStyles'
|
||||
import { noteCreateStyles } from './styles/noteCreateStyles'
|
||||
import { noteListStyles } from './styles/noteListStyles'
|
||||
import { propertiesStyles } from './styles/propertiesStyles'
|
||||
import { sidebarStyles } from './styles/sidebarStyles'
|
||||
@@ -9,6 +10,7 @@ export const styles = {
|
||||
...commonStyles,
|
||||
...editorStyles,
|
||||
...editorSaveStateStyles,
|
||||
...noteCreateStyles,
|
||||
...noteListStyles,
|
||||
...propertiesStyles,
|
||||
...sidebarStyles,
|
||||
|
||||
21
apps/mobile/src/styles/noteCreateStyles.ts
Normal file
21
apps/mobile/src/styles/noteCreateStyles.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { StyleSheet } from 'react-native'
|
||||
import { colors, spacing } from '../theme'
|
||||
|
||||
export const noteCreateStyles = StyleSheet.create({
|
||||
composeButtonDisabled: {
|
||||
opacity: 0.55,
|
||||
},
|
||||
createNoteError: {
|
||||
position: 'absolute',
|
||||
right: spacing.xl,
|
||||
bottom: spacing.xl + 76,
|
||||
maxWidth: 220,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
color: colors.textSoft,
|
||||
backgroundColor: colors.chipRed,
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
},
|
||||
})
|
||||
@@ -8,7 +8,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
|
||||
|
||||
- Branch: `codex/mobile`
|
||||
- Active phase: Phase 2 - Mobile Shell
|
||||
- Active slice: Create local mobile note
|
||||
- Active slice: Add create-note UX state
|
||||
- Push policy: commit locally; do not push unless explicitly requested
|
||||
- Validation target: iPad/iOS simulator first
|
||||
|
||||
@@ -63,6 +63,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
|
||||
- Added a debounced mobile autosave queue that coalesces rapid TenTap draft changes, marks edited drafts as queued, and ignores stale save results when newer drafts supersede them.
|
||||
- Refreshed the in-memory mobile note projection after the latest successful editor save so the note list, editor source, properties words, and snippets update from canonical Markdown.
|
||||
- Added the first app-local mobile note creation path and wired the compose button to write a new Markdown note, prepend it to the current list, and select it through the shared compact navigation reducer.
|
||||
- Added create-note UX state so the compose button disables during app-local creation and displays a compact failure message if storage creation fails.
|
||||
|
||||
## Next Action
|
||||
|
||||
@@ -70,7 +71,7 @@ Continue Phase 2 with the next mobile shell slice:
|
||||
|
||||
1. Dismiss or suppress Expo Go's first-run tools modal during simulator QA so screenshots capture the app without the overlay.
|
||||
2. Add a focused simulator interaction path for editor typing/autosave once Expo Go's overlay no longer blocks clean screenshots.
|
||||
3. Add a durable note-create UX state: disabled/pressed state while creating, create failure feedback, and eventual title-entry flow.
|
||||
3. Add an eventual title-entry flow for new mobile notes instead of creating `Untitled` immediately.
|
||||
|
||||
## Verification Log
|
||||
|
||||
@@ -209,6 +210,11 @@ Continue Phase 2 with the next mobile shell slice:
|
||||
- CodeScene after local note creation: `apps/mobile/src/MobileApp.tsx`, `apps/mobile/src/mobileDemoVault.ts`, and `apps/mobile/src/mobileNoteCreate.test.ts` scored `10`; `apps/mobile/src/mobileNoteCreate.ts` returned no scorable code and no findings.
|
||||
- `pnpm --filter @tolaria/mobile test` passed after local note creation: 17 files / 53 tests.
|
||||
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after local note creation.
|
||||
- `pnpm --filter @tolaria/mobile test -- src/mobileNoteCreate.test.ts` passed after create-note UX state: 17 files / 53 tests.
|
||||
- `pnpm --filter @tolaria/mobile typecheck` passed after create-note UX state.
|
||||
- CodeScene after create-note UX state: `apps/mobile/src/MobileApp.tsx`, `apps/mobile/src/styles/noteListStyles.ts`, and `apps/mobile/src/styles/noteCreateStyles.ts` scored `10`.
|
||||
- `pnpm --filter @tolaria/mobile test` passed after create-note UX state: 17 files / 53 tests.
|
||||
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after create-note UX state.
|
||||
|
||||
## Risks / Watch Items
|
||||
|
||||
|
||||
Reference in New Issue
Block a user