Commit graph

99 commits

Author SHA1 Message Date
le king fu
5b0d27175c fix: replace broken swipe-to-refresh with toolbar refresh button (#61)
The RefreshControl on DraggableFlatList never worked because the
library wraps its FlatList in a GestureDetector with Gesture.Pan(),
which intercepts vertical swipes before RefreshControl can detect
them — particularly with activationDistance=0 in position sort mode.

Replace with a toolbar refresh button (RefreshCw icon) on inbox and
list detail screens. The button uses an Animated spin during refresh,
matching the web UX. Removes all dead RefreshControl code and the
useless refreshControl prop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:37:26 -04:00
1a1eddfd68 Merge pull request 'fix: refresh Android widget after sync push and pull (#65)' (#66) from issue-65-widget-sync-refresh into master 2026-04-09 13:37:06 +00:00
le king fu
23f3144dc4 fix: refresh Android widget after sync push and pull (#65)
The sync client writes directly to the DB via drizzle, bypassing the
repository functions that normally trigger syncWidgetData(). As a
result, changes coming from the web (or any remote source) never
refreshed the home screen widget.

Call syncWidgetData() once at the end of pullChanges (after all remote
changes are applied) and after a successful pushChanges (to reflect
synced state). Single call per cycle avoids spamming widget updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:01:29 -04:00
le king fu
1df41cef1f fix: auto-apply migrations on startup + cleanup duplicate inboxes (#60)
- Add migration 0002 to soft-delete duplicate inboxes per user, keeping
  the oldest one and reassigning tasks to it.
- Run drizzle migrations on server startup via drizzle-orm/node-postgres
  migrator.
- Update Dockerfile to copy the migrations folder into the runtime image
  and externalize pg/drizzle-orm from the esbuild bundle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:53:09 -04:00
le king fu
2a0dd01307 chore: bump version to 1.6.0 (versionCode 12)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:37:50 -04:00
137dc83bf8 Merge pull request 'fix: issues #60 #61 #62 #63 — inbox, refresh, subtask depth, chevron/detail' (#64) from issue-60-fix-duplicate-inbox into master 2026-04-09 01:33:40 +00:00
le king fu
78471543c3 fix: separate subtask expand chevron from detail view icon (#63)
Split TaskItem into two independent states:
- `expanded` (chevron): toggles subtask visibility, only shown when
  subtasks exist
- `detailOpen` (search icon + title click): opens detail panel with
  notes, priority, edit/delete actions

The two actions are fully independent — expanding subtasks does not
open the detail view and vice versa.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:15:59 -04:00
le king fu
21020406b2 fix: prevent sub-subtask creation, limit nesting to 2 levels (#62)
Web: hide "Add subtask" button when depth >= 1 in TaskItem.
API: reject task creation if parentId points to a task that already
has a parentId (max depth validation).
Mobile: hide subtask section in task detail when viewing a subtask.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:13:43 -04:00
le king fu
894ac03072 feat: add refresh button on web + swipe-to-refresh on mobile (#61)
Web: add a RefreshCw button next to the list title in TaskList that
calls router.refresh() with a spin animation.

Mobile: add RefreshControl to DraggableFlatList on both inbox and
list detail screens, using the app's blue accent color.

Also deduplicate list insert values in sync/route.ts (review feedback).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:04:55 -04:00
le king fu
6c36ebcce5 fix: wrap inbox merge in transaction, revert seed to random UUID (#60)
Address review feedback:
1. Wrap inbox deduplication (select, reassign tasks, soft-delete) in a
   db.transaction() for atomicity.
2. Revert seed.ts to use random UUID — a fixed ID shared across users
   would cause PK conflicts. The sync endpoint handles deduplication.
3. Subtasks share the same listId as their parent, so the reassign
   query already covers them (clarified in comment).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:54:55 -04:00
le king fu
d9daf9eda4 fix: resolve duplicate inbox on web after mobile sync (#60)
When mobile syncs its inbox (fixed ID) to the web, check if an inbox
already exists for the user. If so, reassign tasks and soft-delete the
old inbox to prevent duplicates. Also harmonize seed.ts to use the same
fixed inbox ID as mobile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:50:12 -04:00
le king fu
71ee702739 chore: bump version to 1.5.2 (versionCode 11)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:42:22 -04:00
0893cea489 Merge pull request 'fix: resolve sync data inconsistency between mobile and web (#55)' (#58) from fix/simpl-liste-55-sync-data-inconsistency into master 2026-04-08 19:32:15 +00:00
le king fu
8f7204e4b1 fix: resolve sync data inconsistency between mobile and web (#55)
Three root causes fixed:

1. API GET /api/sync returned raw entities but mobile client expected
   {changes: [...], sync_token} format — pullChanges() iterated
   data.changes which was undefined, silently skipping all server data.
   Now transforms entities into the SyncPullChange format.

2. Mobile outbox writes used snake_case keys (due_date, list_id, etc.)
   but server processOperation spreads data directly into Drizzle which
   expects camelCase (dueDate, listId). Fixed all outbox writes to use
   camelCase. Also fixed task_tag → taskTag entity type.

3. Missing completedAt in task outbox payloads — completion state was
   lost during sync. Added completedAt to both create and update outbox
   entries, and added Date conversion in server update handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:28:36 -04:00
818f66205b Merge pull request 'fix: update deps for high vulnerabilities (#54)' (#57) from fix/simpl-liste-54-vulnerability-updates into master 2026-04-08 19:23:37 +00:00
le king fu
5b16882a83 fix: update drizzle-orm and @xmldom/xmldom to fix high vulnerabilities (#54)
- drizzle-orm 0.45.1 → 0.45.2 (SQL injection via improperly escaped identifiers)
- @xmldom/xmldom 0.8.11 → 0.8.12 (XML injection via unsafe CDATA serialization)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:22:42 -04:00
45d463c849 Merge pull request 'fix: rename .env.example to .env.template (#53)' (#56) from fix/simpl-liste-53-env-example-false-positive into master 2026-04-08 19:21:40 +00:00
le king fu
a5e9aa6f09 fix: rename .env.example to .env.template to avoid false positive (#53)
The defenseur flagged web/.env.example as a tracked secret file.
Renaming to .env.template avoids the .env* pattern match while
keeping the same purpose as a configuration template.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:20:55 -04:00
le king fu
89b3dada4a fix: replace Link with anchor tags for Logto auth routes
Next.js <Link> components prefetch routes on render/hover. When used
for /api/logto/sign-out, this triggered the sign-out handler during
normal navigation, clearing the session cookie and causing auth loops.

Also: wrap getAuthenticatedUser with React cache() for deduplication,
clean up diagnostic logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:31:04 -04:00
le king fu
9933c3678e fix: pass full URL to handleSignIn for callback URI matching
The Logto SDK needs the full callback URL (not just searchParams) to
verify it matches the redirect URI registered during sign-in.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:28:08 -04:00
le king fu
f786947941 fix: resolve Logto auth crash on web — remove illegal cookie set in layout
The (app)/layout.tsx was calling cookieStore.set() which is forbidden in
Server Components under Next.js 16 (only allowed in Server Actions and
Route Handlers). This caused a 500 error immediately after Logto login.

Also includes: mobile sync client improvements, i18n updates, web API
rate limiting, Bearer token support for mobile clients, and Dockerfile
optimizations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:12:59 -04:00
14c208be46 Merge pull request 'fix: force widget refresh after subtask toggle (#32)' (#33) from fix/simpl-liste-32-widget-subtask-toggle into master 2026-04-08 16:44:46 +00:00
le king fu
6328a8d0d3 fix: correct Logto session cookie prefix in middleware
Cookie is named `logto_<appId>` not `logto:<appId>`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:56:21 -04:00
8462aa9ef4 Merge pull request 'feat: add mobile sync client with outbox pattern (#40)' (#47) from issue-40-sync-mobile into master 2026-04-06 16:59:00 +00:00
b7a090df71 Merge pull request 'feat: implement web frontend with full task management UI (#39)' (#46) from issue-39-frontend-web into master 2026-04-06 16:58:35 +00:00
f4df9bbfd0 Merge pull request 'feat: setup Next.js web project with Drizzle + PostgreSQL schema (#35)' (#42) from issue-35-web-setup into master 2026-04-06 16:58:04 +00:00
d486be9227 Merge pull request 'feat: extract shared types, colors, priority and recurrence (#34)' (#41) from issue-34-shared-types into master 2026-04-06 16:57:29 +00:00
le king fu
c496d9586c feat: add mobile sync client with outbox pattern (#40)
- sync_outbox table in SQLite (migration 0004)
- Sync service: push/pull changes, fullSync, outbox cleanup
- Outbox writing in task/list/tag repositories after mutations
- Settings store: syncEnabled, lastSyncAt, userId
- Sync polling: on launch, every 2 min, on return from background
- Settings UI: Compte section with connect/sync/disconnect buttons
- i18n keys for sync strings (FR + EN)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:54:44 -04:00
le king fu
cb04adcc2e feat: implement web frontend with full task management UI (#39)
- Protected (app) layout with sidebar, header, theme toggle
- List detail page with tasks, filters, sorting
- Inline task editing (title, notes, priority, due date, recurrence)
- Subtask creation and nested display
- Dark mode (class-based, persisted to localStorage)
- WebSocket sync hook (connects via ticket auth, auto-refresh)
- Responsive sidebar (hamburger on mobile)
- French UI strings throughout
- Components: Sidebar, TaskList, TaskItem, TaskForm, FilterBar,
  ThemeToggle, Header, AppShell

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:40:11 -04:00
2f2a48f644 Merge pull request 'feat: add WebSocket server with ticket auth and heartbeat (#38)' (#45) from issue-38-websocket into issue-35-web-setup 2026-04-06 16:07:00 +00:00
le king fu
6d2e7449f3 feat: add WebSocket server with ticket auth and heartbeat (#38)
- Custom server (server.ts) wrapping Next.js + ws on same port
- Ticket-based auth: validates ephemeral nonce from /api/ws-ticket
- Origin validation against allowlist
- Session revalidation every 15 min (sends auth_expired, closes)
- Heartbeat every 30s (ping/pong, terminates dead connections)
- broadcastToUser() for API routes to notify connected clients
- Shared ticket store between API route and WS server via globalThis
- Health endpoint now reports active WS connections
- Dockerfile updated to use custom server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:55:28 -04:00
46ead345b4 Merge pull request 'feat: implement REST API backend with full CRUD and sync (#37)' (#44) from issue-37-api-rest into issue-35-web-setup 2026-04-06 15:52:30 +00:00
le king fu
be9ba65337 feat: implement REST API backend with full CRUD and sync (#37)
- Lists, Tasks, Tags CRUD endpoints with soft-delete
- Sync endpoints (GET since + POST batch with idempotency keys)
- WS ticket endpoint (ephemeral nonce, 30s TTL, single use)
- Auth middleware on all endpoints via getAuthenticatedUser()
- BOLA prevention: userId check on every entity operation
- Zod strict schemas for input validation
- Filters and sorting on task listing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:47:53 -04:00
0369597eb6 Merge pull request 'feat: integrate Logto auth with middleware and login page (#36)' (#43) from issue-36-auth-logto into issue-35-web-setup 2026-04-06 15:38:45 +00:00
le king fu
42c39907cd feat: integrate Logto auth with middleware and login page (#36)
- Logto config matching la-compagnie-maximus pattern
- API routes: sign-in, callback, sign-out
- Next.js middleware protecting all routes except /auth and /api
- Auth helper to extract userId (sub) from Logto context
- Login page with Compte Maximus branding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:37:20 -04:00
le king fu
022fe53b92 feat: setup Next.js web project with Drizzle + PostgreSQL schema (#35)
- Init Next.js App Router with TypeScript, Tailwind, standalone output
- Drizzle ORM pg-core schema (sl_lists, sl_tasks, sl_tags, sl_task_tags)
- Database client, seed script, drizzle.config
- Health endpoint (/api/health) with DB latency check
- Dockerfile for Coolify deployment
- .env.example with DATABASE_URL and Logto config placeholders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:03:34 -04:00
le king fu
91eef58186 fix: remove duplicate RecurrenceType definition in shared/types (#34)
Re-export from shared/recurrence instead of redefining.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:39:12 -04:00
le king fu
b277232462 feat: extract shared types, colors, priority and recurrence (#34)
Create src/shared/ with platform-agnostic types and helpers for
mobile/web code sharing. Add updatedAt to tags schema for sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:38:16 -04:00
escouade-bot
156e45496f fix: deduplicate WIDGET_NAMES and remove double-render in TOGGLE_SUBTASK (#32)
- Export WIDGET_NAMES from widgetSync.ts and import in widgetTaskHandler.ts
- Remove renderWithState call before forceWidgetRefresh to avoid double-render
- Use shared WIDGET_NAMES in widgetSync.ts refresh loop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 04:01:01 -04:00
escouade-bot
645f778db5 fix: remove unnecessary forceWidgetRefresh from TOGGLE_COMPLETE and TOGGLE_EXPAND (#32)
Only TOGGLE_SUBTASK needs forceWidgetRefresh() because ListView caches
items. TOGGLE_COMPLETE and TOGGLE_EXPAND already work with renderWithState()
alone since they perform structural changes (remove item / toggle children).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 02:00:49 -04:00
escouade-bot
82b501e753 fix: force widget refresh via requestWidgetUpdate after click actions (#32)
The Android ListView caches its items, so visual-only changes (like
toggling a subtask checkbox) were not reflected without calling
requestWidgetUpdate() to invalidate the cache. Added forceWidgetRefresh()
helper that calls requestWidgetUpdate() for all 3 widget sizes after
TOGGLE_SUBTASK, TOGGLE_COMPLETE, and TOGGLE_EXPAND handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 00:01:14 -04:00
le king fu
7f8a0832d4 chore: bump version to 1.4.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:14:47 -04:00
244fbee405 Merge pull request 'fix: consolidate widget AsyncStorage and debounce expand (#29)' (#31) from issue-29-widget-expand-perf into master 2026-03-31 00:13:13 +00:00
9b1f7e79c9 Merge pull request 'feat: inline edit and delete for subtasks (#25)' (#30) from issue-25-edit-delete-subtasks into master 2026-03-31 00:13:04 +00:00
723f5d6501 Merge pull request 'fix: update vulnerable dependencies' (#28) from fix/simpl-liste-26-vulnerable-deps into master 2026-03-31 00:02:07 +00:00
le king fu
992c983026 chore: remove unused isWidgetTask function (#29)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:00:19 -04:00
le king fu
810bf2e939 fix: consolidate widget AsyncStorage keys and debounce expand (#29)
Merge widget:tasks, widget:isDark, and widget:expandedTaskIds into a
single widget:state key to reduce AsyncStorage I/O from 3 reads to 1.
Add 2s debounce on TOGGLE_EXPAND to prevent double-tap from collapsing
the subtask list. Legacy keys are migrated on first read.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:45:02 -04:00
le king fu
19706fa4a3 feat: add inline edit and delete for subtasks (#25)
Long-press a subtask to edit its title inline. Tap X to delete
with confirmation. Tap still toggles completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:36:24 -04:00
escouade-bot
aa7ca20731 fix: use npm audit fix instead of broad overrides for vulnerable deps (#26)
Replace aggressive >=major overrides (picomatch>=4, brace-expansion>=2, etc.)
with npm audit fix which patches each dependency within its compatible semver
range: picomatch 2.3.2/3.0.2/4.0.4, brace-expansion 1.1.13/2.0.3/5.0.5,
undici 6.24.1, node-forge 1.4.0, tar 7.5.13, yaml 1.10.3/2.8.3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:03:32 -04:00
escouade-bot
36e138ec55 fix: override vulnerable transitive dependencies with patched versions (#26)
Add npm overrides for picomatch, node-forge, tar, undici, brace-expansion,
and yaml to resolve 6 security vulnerabilities (4 high, 2 moderate).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:01:26 -04:00