From 1f66fbda59a2de910b892716b512ebc69ddb0800 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 4 May 2026 19:05:13 +0200 Subject: [PATCH] feat: show mobile note create state --- apps/mobile/src/MobileApp.tsx | 43 ++++++++++++++++++++-- apps/mobile/src/styles.ts | 2 + apps/mobile/src/styles/noteCreateStyles.ts | 21 +++++++++++ docs/MOBILE_PROGRESS.md | 10 ++++- 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 apps/mobile/src/styles/noteCreateStyles.ts diff --git a/apps/mobile/src/MobileApp.tsx b/apps/mobile/src/MobileApp.tsx index a50ab109..b1ac7d17 100644 --- a/apps/mobile/src/MobileApp.tsx +++ b/apps/mobile/src/MobileApp.tsx @@ -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>({}) 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 ( @@ -110,6 +123,8 @@ export function MobileApp() { @@ -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({ 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({ )} /> - + {createNoteFailed ? Could not create note : null} + [ + styles.composeButton, + isCreatingNote ? styles.composeButtonDisabled : null, + pressed ? styles.pressed : null, + ]} + > diff --git a/apps/mobile/src/styles.ts b/apps/mobile/src/styles.ts index 066e0116..89963889 100644 --- a/apps/mobile/src/styles.ts +++ b/apps/mobile/src/styles.ts @@ -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, diff --git a/apps/mobile/src/styles/noteCreateStyles.ts b/apps/mobile/src/styles/noteCreateStyles.ts new file mode 100644 index 00000000..f9303dba --- /dev/null +++ b/apps/mobile/src/styles/noteCreateStyles.ts @@ -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', + }, +}) diff --git a/docs/MOBILE_PROGRESS.md b/docs/MOBILE_PROGRESS.md index 1fb71b51..7666b3a2 100644 --- a/docs/MOBILE_PROGRESS.md +++ b/docs/MOBILE_PROGRESS.md @@ -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