Capture & Offline Sync
This section documents how DreamStream captures dreams reliably across typing, voice-to-text, and instant audio drafts, including what happens when a device is offline.
Why this feature exists (product intent)
Dreams fade quickly. DreamStream’s capture design aims to:
- Reduce friction in the first 30 seconds after waking.
- Support different user situations (sleepy typing, voice dictation, “record now / edit later”).
- Avoid losing data when offline (typed dreams and quick-capture drafts, including audio when possible).
🎙️ Capture Modes
The most reliable way to preserve every detail. - Data: Story text + metadata (emotions, type, style). - Offline Support: Fully queued.
Rapid transcription integrated into the logger. - Tech: Uses DreamStream’s AI transcription service (Gemini via secure proxy), so it requires connectivity. - Data: Appends text directly to the narrative field.
The "Record Now, Edit Later" workflow.
- UX: 1-tap recording from app icon.
- Drafts: Saved as isDraft placeholders (e.g., “Quick Capture Draft” / “Audio Draft (mm:ss)”) so users can edit later.
- Offline Support: Fallback queue supported on connectivity failures; audio blob backup is best-effort.
A habit-preserving log for mornings where the user remembers nothing. - Data: A lightweight day marker (not a dream entry). - Streaks: Counts as “logged” for streaks. - AI: Does not queue generation jobs and does not affect AI analysis/visuals. - Offline Support: Not supported (requires connectivity).
Where dream logging starts (entry points)
Users can begin logging from multiple places:
- Bottom navigation “Create” (+)
- Opens a compose sheet:
- Log dream → opens the Dream Logger
- Didn't remember → confirms, then logs a day marker (with undo)
- Shows a small badge if there are unsynced offline entries.
- Guided onboarding (new users)
- Walks the user through each field of the Dream Logger with coach prompts.
- Quick Capture (native app)
- A dedicated app shortcut (“Record Dream”).
- Also supports a deep link route that opens recording.
Guided Onboarding (first-time users)
New users experience a guided walkthrough that teaches the Dream Logger field-by-field.
What users see
When a new user first opens DreamStream:
- Name Input Modal – "Hey Dreamer! 👋 What should we call you?"
- Welcome Prompt – On the home screen, a coach bubble invites them to log their first dream
- Logger Walkthrough – Step-by-step bubbles highlight each field:
| Step | Field | Guidance message |
|---|---|---|
| 1 | Date & Time | "When did this dream happen? You can log dreams from last night or the past." |
| 2 | Dream Title | "Give your dream a name. We have pre-filled an example..." |
| 3 | The Narrative | "Describe what happened in detail. Tap the mic to speak your dream..." |
| 4 | Day Residue | "What happened yesterday? This improves the analysis." |
| 5 | Dream Type | "Was it a normal dream, nightmare, or lucid dream?" |
| 6 | Visual Style | "How did it look? Realistic, animated, or surreal?" |
| 7 | Emotions | "How did you feel during the dream? Select all that apply..." |
| 8 | Intensity | "Rate the intensity or stress level of the dream (1-10)." |
- Dream Logged – After saving, a bubble highlights their new dream: "Amazing! Tap your dream to explore AI analysis, symbols, and art."
- Dream Profile – A bubble explains Dream Profile: "As you log more dreams, this section reveals patterns and emotions based on your Dream Signs and selected feelings. Use the toggle, then tap an axis to see the top items and example dreams."
- Profile Celebration – Optional confetti and prompt to set up their profile
Skip option
Users can skip the onboarding at any point. Skipping still saves their name as "Dreamer" and sets them to normal app flow.
Pre-filled example
During onboarding, the Dream Logger is pre-filled with an example dream so users can see what a complete entry looks like.
Pre-filled example
The example includes all fields (title, narrative, emotions, etc.) to demonstrate what a fully-complete dream entry looks like before AI processing.
Standard Dream Logger (typing + voice-to-text)
What users see
The Dream Logger is a structured form with:
- Date & time (when the dream happened)
- Title
- Narrative (main story text)
- Includes an in-field microphone button for dictation
- Day residue (what happened yesterday)
- Dream type (e.g., Normal, Lucid, Nightmare, Sleep Paralysis)
- Visual style (aesthetic for AI visuals)
- Emotions (multi-select)
- Intensity / stress level (1–10)
Voice-to-text inside the logger
- The mic button records using the device microphone.
- When the user stops recording, DreamStream transcribes the audio and appends the text to the narrative field.
- The UI shows an “in progress” state while transcription runs.
- If transcription fails (network or service issue), the user sees an error and can continue typing.
Known People detection (if enabled)
- If the user has Known People saved in their profile, the logger scans the narrative for those names/aliases.
- When matches are found, a small “Detected” chip list appears as a gentle hint (no hard tagging).
Save behavior & validation
- The logger requires both title and narrative to be present before Save.
- On Save, DreamStream dismisses the keyboard (better mobile UX) and commits the dream.
What happens after tapping “Save”
DreamStream behaves differently depending on connectivity quality (including unstable internet where cloud saves fail).
flowchart TD
A["User taps Save in Dream Logger"] --> B{"Is device online?"}
B -->|Yes| C["Try save to cloud database"]
C --> D{"Cloud save succeeds?"}
D -->|Yes| E["Mark generation as processing"]
E --> F["Queue background jobs<br/>(analysis + image + comic)"]
F --> G["User can keep using app<br/>(results appear later)"]
D -->|No (connectivity failure)| H["Fallback: save dream locally<br/>(Offline Queue)"]
B -->|No| H["Save dream locally<br/>(Offline Queue)"]
H --> I["Show 'Saved offline' message"]
I --> J["Badge shows pending sync"]
J --> K["Auto-sync when connection is stable"]
Online save (cloud-first)
When online, DreamStream:
- Saves the dream record to the cloud.
- Immediately queues AI generation jobs (analysis + visuals) to run in the background.
- Shows haptic feedback and returns the user to browsing.
Offline/fallback save (offline queue)
When a cloud save cannot complete due to connectivity (fully offline or unstable internet), DreamStream:
- Saves the dream locally in an Offline Queue.
- Marks the dream in UI as pending sync.
- Shows a clear message that the dream will sync later.
A small badge appears on the bottom “Create” button showing how many dreams are queued.
"Didn't remember" logging (habit marker)
"Didn't remember" is stored separately from dreams so it won’t pollute dream analytics or AI contexts.
User flow
- Tap Create (+).
- Tap Didn't remember.
- Confirm (prevents groggy mis-taps).
- The app logs a day marker and shows a toast:
Logged: "Didn't remember". Tap to undo.
Rules
- Only allowed if no dream exists for that local day.
- Draft dreams / audio drafts count as a dream for this rule.
- If the user later logs a dream (including saving a draft) for that same local day, DreamStream auto-clears the "didn't remember" marker.
- Users can delete a "didn't remember" marker from Dream Activity (calendar selected-day card).
Data model
public.no_recall_dayskeyed by (user_id,day_key).day_keyis a local YYYY-MM-DD day key (same format as streak day keys).day_key_utcis stored as a best-effort guard for server-side reminder jobs (which don’t know the user’s timezone).
Why it’s separate
- Dream entries drive meaning/visual generation, deep insights, and chat context.
- "Didn't remember" is a "logging state", not a dream type, so it lives outside the
dreamstable.
Offline queue: how sync works (user-visible behavior)
When sync happens
Queued entries will sync:
- On app startup (if the device is online)
- When the device transitions from offline → online
- Immediately after fallback queueing when the app still reports online and connectivity stabilizes
What the user sees
- A message like “Syncing X entries…”
- On success: “Synced X dreams/drafts successfully!”
- After sync:
- queued dreams appear normally in the journal and start background generation.
- queued drafts appear as drafts (no generation jobs).
Sync sequence (behind the scenes, simplified)
sequenceDiagram
participant A as App
participant L as Offline Queue (local)
participant DB as Cloud Database
participant Q as Background Jobs
A->>L: Read queued entries
loop Each queued entry
A->>DB: Insert dream/draft record
alt Full dream
A->>Q: Create jobs (analysis + image + comic)
else Draft
A->>DB: Keep idle statuses (no generation jobs)
end
A->>L: Remove from queue
end
A->>Q: Trigger job processing
A->>A: Refresh journal list
Drafts (quick capture + “edit later”)
What a draft is
A draft is a dream entry that is intentionally incomplete.
- It exists so the user can capture something quickly and return later.
- Drafts are treated differently in some areas (e.g., export excludes drafts).
Quick Capture audio draft (native shortcut)
Quick Capture is a fast “Record now” path:
- It opens a full-screen recording UI.
- Recording auto-starts immediately when the screen opens.
- The user can pause/resume recording (timer pauses too).
- If the user closes with X before saving, the recording is discarded (nothing is stored).
- When the user stops recording, they get two choices:
- Save & Sleep (store as a draft)
- Add Details Now (open the Dream Logger pre-filled with the audio/transcript)
Transcription note: Quick Capture uses the same AI transcription service as Voice‑to‑Text. If transcription can’t run, Save & Sleep still saves a draft with a placeholder description so the user can edit later.
flowchart TD
A["Trigger Quick Capture<br/>(app shortcut or deep link)"] --> B["Recording starts automatically"]
B --> C["User stops recording"]
C --> D{"Choose next step"}
D -->|Save & Sleep| E["Create Draft dream record<br/>'Audio Draft (mm:ss)'"]
E --> F{"Cloud save succeeds?"}
F -->|Yes| G["Draft saved to cloud"]
F -->|No| H["Queue draft offline<br/>(audio backup if eligible)"]
D -->|Add Details Now| I["Transcribe (best effort)"]
I --> J["Open Dream Logger<br/>(prefilled, no draft saved)"]
Important limitations & edge cases (drafts)
Quick Capture offline details
Quick Capture drafts now have offline fallback queueing for connectivity failures.
Audio backup is best-effort:
- blobs up to ~10MB are cached locally and uploaded during sync.
- larger audio may fall back to text-only draft sync.
- if local storage is constrained, draft text is still queued without audio backup.
-
Quick Capture requires a signed-in user.
-
Transcription behavior: quick capture may attempt transcription for user convenience, but the draft is saved even if transcription does not complete.
-
Retry behavior: queued drafts are retained for continued retry attempts (they are not auto-dropped after the standard dream retry threshold).
-
Draft UI conventions:
- Drafts display a Draft badge.
- Drafts should not be presented as a specific dream type until the user completes editing.
Duplicate-tap safeguards
DreamStream includes protections to reduce accidental duplicates:
- New dream entries: avoids creating duplicates if the same dream is submitted repeatedly in a short window.
- Draft submissions: avoids inserting the same draft ID twice.
This matters because “Save” and “Record” are often tapped while the user is sleepy or rushed.
Exporting your journal
DreamStream allows users to take their data with them via an Export Journal feature.
Format & Filtering
- Format: PDF (premium look), CSV (data analysis), or TXT (simple text)
- Selection: Users can "Select All" or manually pick specific dreams from a list
- Exclusions: Drafts and Sample Dreams are excluded from exports by default to keep the journal clean.
Custom Field Selection
Users can toggle which properties to include in the export: - Story (Narrative) - Type (Normal, Lucid, etc.) - Rate (Intensity/Stress) - Image(s) (R2 links/placeholders) - Emotions & Dream Signs (Tags) - Analysis (AI breakdown) - Actions & Archetype - Summary & Day Context - Visual Style
Native Integration
- File Saving (native): Exports are written to the app’s sandboxed Documents directory via Capacitor Filesystem.
- iOS: Appears in the Files app under On My iPhone → DreamStream when file sharing is enabled (no extra permissions needed).
- Android: Currently saves exports to the app’s sandboxed Documents directory (scoped storage). The app does not automatically present a share sheet, so users must open the Files app or use the app’s export/share action to move the file.
- Share sheet (default pattern): Use a share sheet immediately after export to surface Files/cloud apps and avoid access friction. Android should use a share intent; iOS should use UIActivityViewController/UIDocumentInteractionController.
- Web: Uses a standard browser download.
- Filenames: Uses the format
DreamStream_Export_YYYY-MM-DD.extension. - Haptics: Provides feedback during the export process.
- Checklist: ✅ After every mobile export, show a share sheet so users can save to Files/Drive/Dropbox (required for sandboxed exports).
Future consideration (not implemented)
If public Downloads are required on Android, use Storage Access Framework / MediaStore to let the user pick a destination. This is not implemented in the current flow.
Sample Dreams (for discovery)
To help users understand the value of DreamStream quickly, new accounts are pre-populated with Sample Dreams.
Why they exist
- Feature Discovery: Shows what a dream looks like with 100% completion (Image, Comic, Deep Analysis, Archetype).
- Educational Tooltip: When opening a sample dream, first-time users see an info tooltip explaining that this is a demonstration of the app's potential.
- Safe Exploration: Users can tap around and see how the "Insight Lens" or "Dream Radar" looks with data before they have logged their own.
Sample dreams excluded from exports
Sample dreams are marked with an isSample flag and can be deleted by the user at any time. They are strictly local/demonstration data and do not affect the user's personal analytics or exports.
Troubleshooting signals (Support-facing)
- User says: “My dream disappeared.”
- Check if they were offline/unstable network and the dream is queued (pending badge on +).
- If queued count is zero and cloud save failed repeatedly, check error logs for non-connectivity failures.
- User says: “Quick record didn’t save.”
- Check pending queue count and reconnect; quick-capture drafts now use offline fallback queueing too.
- If audio is missing but draft text exists, local audio backup likely exceeded size/storage limits.
- User says: “Mic didn’t work.”
- Microphone permission may be denied; voice-to-text requires permission.
Implementation map (for accuracy checks)
- Dream Logger (typed + voice-to-text):
components/DreamLogger.tsx - Quick Capture (audio draft):
components/QuickCaptureOverlay.tsx - Draft save + audio upload:
App.tsx(handleSaveDream) - Online dream save + offline queueing:
App.tsx(addDream) - Offline queue storage:
src/services/offlineQueueService.ts - Offline pending badge:
components/BottomNav.tsx - Export Journal:
components/ExportJournalScreen.tsx