This repository has been archived on 2026-06-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
bvandeusen 7df7b5ff85 feat(offline): tier 2 phase 3 — write queue with optimistic UI
Offline writes now queue + apply optimistically instead of throwing.
On reconnect, the queue drains automatically; conflicts and rejections
surface as one-time SnackBars so the user knows their edit didn't land.

- Drift schema v3: `pending_writes` table (verb + payload + baseline +
  tries + last_error). Negative ids serve as cache placeholders for
  offline-created rows until replay assigns the server id.
- Each write repository (notes, tasks, projects, milestones, events)
  now catches NetworkException, applies the change to cache (with a
  fresh `nextTempId()` for creates), and enqueues the API call.
- Edits to a still-queued offline-created row coalesce into the
  original create payload — only one server call per row, in order.
- Deletes of still-queued offline-created rows drop the queue entry
  and the cache row; no server call ever happens.
- New WriteQueue service drains the queue oldest-first.
  Server-wins on `updated_at` baseline check (notes/tasks/projects);
  last-writer-wins for milestones/events (no getOne available).
  4xx → drop + surface as `rejected`; conflict → drop + `overwritten`;
  404 on update → drop + `missing`; 5xx/network → keep + retry next
  online cycle.
- Replay fires on AuthStatus → authenticated transitions
  (cold-start, came-back-online, login).
- OfflineBanner shows "Retry (N)" with the pending-writes count.
- Distinct ServerException added so 4xx no longer masquerade as
  NetworkException — the queue can drop them instead of looping.

flutter analyze clean; 21 tests pass (3 new for QueueFailure messaging).
Phase 4 (read-only UI indicators on cached/temp rows) still ahead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 22:08:40 -04:00
2026-02-28 21:28:53 -05:00

Fabled — Android App

Native Android client for FabledAssistant, a self-hosted AI second-brain and productivity assistant.

Features

  • Daily Briefing — the primary screen; opens on launch. Shows your AI-compiled morning digest (tasks, calendar, weather, RSS) with a full conversation you can reply to inline. Refresh manually or browse past briefings from the overflow menu.
  • Quick Capture — always-visible input bar above all tabs. Type a note or task and submit; multiple captures queue sequentially so the input is never blocked. Falls back to offline persistence when the server is unreachable.
  • Knowledge — unified browsable feed of notes, people, places, lists, and tasks across six filter tabs. Tag filters, inline search (debounced), and two-tier pagination (ID list → batch hydration). Tasks load from /api/tasks directly and are fully integrated with the knowledge feed.
  • Chat — streaming AI conversations with real-time SSE display, including live tool-use status notifications (e.g. "Calling create_note…"). Tap + to start a new conversation or open an existing one.
  • Calendar — month strip + daily agenda view backed by the server's internal event store. Full event CRUD with a modal form: title, all-day toggle, start/end date+time pickers, repeat (None/Daily/Weekly/Monthly/Yearly), description, location, and colour chips. Custom RRULE strings are preserved read-only.
  • News — RSS article feed with per-feed filtering and reactions. Tap "Discuss" to open any article in a new chat conversation.
  • Projects — browse and edit projects; tap a project to see its milestone-grouped task list.
  • Voice I/O — tap the microphone in the capture bar or chat to dictate. Server-side STT transcribes audio; TTS reads assistant replies aloud in voice mode.
  • Note & task editing — full Markdown editor for notes, task editor with due date, priority, project, and milestone assignment.
  • OAuth / SSO — authenticates via your server's OIDC provider; local username/password login also supported if enabled server-side.
  • Session persistence — stays logged in across restarts via a persistent cookie jar.
  • Auto-refresh — data refreshes automatically when the app returns to the foreground (throttled to once per 5 minutes) and when switching between shell tabs.
  • Auto-update — checks your Forgejo releases on launch and prompts to download and install new APKs in-app.

Requirements

  • A running FabledAssistant server (self-hosted)
  • Android 5.0+ (API 21)

Getting Started

Prerequisites

  • Flutter 3.x SDK
  • Android Studio (for Android SDK and emulator)
  • JDK 17

Setup

flutter pub get
flutter run

On first launch, enter your FabledAssistant server URL (e.g. https://fabled.example.com) and sign in.

Architecture

lib/
├── main.dart                        # Entry point; resolves async deps before runApp
├── app.dart                         # GoRouter + auth guards + 3-tab shell + auto-refresh
├── core/
│   ├── constants.dart               # Route name constants
│   ├── exceptions.dart              # AppException hierarchy
│   └── theme.dart                   # Custom slate-indigo ColorScheme + Fraunces typography
├── data/
│   ├── api/                         # Dio HTTP layer (one class per resource)
│   │   ├── chat_api.dart            # SSE streaming; typed ChatStreamEvent (text/status)
│   │   ├── events_api.dart          # Calendar event CRUD
│   │   ├── knowledge_api.dart       # Two-tier paginated knowledge feed
│   │   ├── news_api.dart            # RSS feed + reactions
│   │   └── voice_api.dart           # STT + TTS endpoints
│   ├── models/                      # Plain Dart models with fromJson/toJson
│   │   ├── calendar_event.dart      # CalendarEvent + dateOnly() helper
│   │   ├── knowledge_item.dart      # KnowledgeItem (notes/tasks unified)
│   │   └── message.dart             # Chat message with streaming status
│   └── repositories/                # Thin wrappers over API classes
├── providers/                       # Riverpod providers (state + dependency wiring)
│   ├── briefing_provider.dart       # Today's briefing — optimistic UI, SSE, polling
│   ├── calendar_provider.dart       # CalendarNotifier: month navigation + event CRUD
│   ├── chat_provider.dart           # Conversations + streaming messages + status
│   ├── knowledge_provider.dart      # Two-tier pagination; delegates tasks to TasksApi
│   ├── news_provider.dart           # NewsNotifier + FeedsNotifier
│   └── capture_work_queue_provider.dart  # Sequential in-memory capture queue
├── screens/
│   ├── briefing/                    # BriefingScreen + BriefingHistoryScreen
│   ├── calendar/                    # CalendarScreen (TableCalendar) + EventFormSheet
│   ├── chat/                        # ConversationsTabScreen + ChatScreen
│   ├── knowledge/                   # KnowledgeScreen (6-tab feed + search + tag filters)
│   ├── library/                     # ProjectTasksScreen (milestone-grouped task list)
│   ├── news/                        # NewsScreen (feed filter + reactions + discuss)
│   ├── notes/                       # NoteDetailScreen + NoteEditScreen
│   ├── projects/                    # ProjectsScreen + ProjectEditScreen
│   ├── tasks/                       # TaskEditScreen
│   └── settings/ auth/ setup/ splash/
└── widgets/
    ├── chat_message_bubble.dart     # Shared bubble (Chat + Briefing); shows tool status
    ├── knowledge_item_card.dart     # Card for notes, people, places, lists, tasks
    ├── news_card.dart               # RSS article card with reactions
    └── voice_mic_button.dart        # Animated mic button (capture bar + chat)

Key packages: flutter_riverpod, go_router, dio + cookie_jar, google_fonts, flutter_markdown_plus, table_calendar, record, just_audio

Building a Release APK

flutter build apk --release

The APK will be at build/app/outputs/flutter-apk/app-release.apk.

CI / CD

CI and build are defined in a single workflow (.forgejo/workflows/ci.yml) running on the shared py3.12-node22 act runner with the ghcr.io/cirruslabs/flutter:stable container.

Trigger Analyze + Test Build APK Release
PR
Push dev ✓ (artifact fabledapp-dev-<sha>)
Push main
Tag v* ✓ (artifact fabledapp-<sha>) ✓ Forgejo Release + APK attached

The build job has needs: [analyze] — a failed analyze or test blocks the APK build.

To cut a release:

git tag v26.04.06 && git push origin v26.04.06

Requires a RELEASE_TOKEN secret (Forgejo PAT with write:repository scope) set in repo Settings → Secrets → Actions.

S
Description
mobile app to connect to the fabled assistant
Readme 2.6 MiB
Languages
Dart 87.9%
C++ 5.7%
CMake 4.2%
Swift 0.7%
Shell 0.5%
Other 1%