diff options
| author | soryu <soryu@soryu.co> | 2026-01-18 17:44:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-18 17:44:50 +0000 |
| commit | 869f21ee2efaefed6a5aa4fbd417c25df8dec02a (patch) | |
| tree | 2a90820ac817173e5b7154e0ba5e4f5d095f9613 /apps/mobile/stores/taskStore.ts | |
| parent | 219bca168508e1ea5e91e8a9ce98338afeddfbd2 (diff) | |
| download | soryu-869f21ee2efaefed6a5aa4fbd417c25df8dec02a.tar.gz soryu-869f21ee2efaefed6a5aa4fbd417c25df8dec02a.zip | |
Add React Native mobile app for Makima (#3)
* [WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC
* feat(mobile): complete mobile app integration and verification
- Add ThemeColors type export to Colors.ts for type safety
- Export SUPABASE_URL from supabase.ts and use environment variables
- Update .env.example with correct default URLs
- Add comprehensive README.md with setup instructions
Verified:
- TypeScript compiles without errors
- App exports successfully for iOS and Android
- All screens accessible (login, dashboard, tasks, settings, task detail)
- Auth flow working with Zustand store and Supabase
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Task completion checkpoint
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'apps/mobile/stores/taskStore.ts')
| -rw-r--r-- | apps/mobile/stores/taskStore.ts | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/apps/mobile/stores/taskStore.ts b/apps/mobile/stores/taskStore.ts new file mode 100644 index 0000000..1a08a45 --- /dev/null +++ b/apps/mobile/stores/taskStore.ts @@ -0,0 +1,84 @@ +import { create } from 'zustand'; +import type { TaskSummary, TaskOutputEntry } from '../lib/api'; + +interface TaskState { + // Data + tasks: TaskSummary[]; + selectedTaskId: string | null; + taskOutputs: Record<string, TaskOutputEntry[]>; + + // Actions + setTasks: (tasks: TaskSummary[]) => void; + updateTask: (taskId: string, update: Partial<TaskSummary>) => void; + selectTask: (taskId: string | null) => void; + appendOutput: (taskId: string, output: TaskOutputEntry) => void; + setTaskOutputs: (taskId: string, outputs: TaskOutputEntry[]) => void; + clearTaskOutputs: (taskId: string) => void; +} + +export const useTaskStore = create<TaskState>((set) => ({ + // Initial state + tasks: [], + selectedTaskId: null, + taskOutputs: {}, + + // Actions + setTasks: (tasks) => + set({ tasks }), + + updateTask: (taskId, update) => + set((state) => ({ + tasks: state.tasks.map((task) => + task.id === taskId ? { ...task, ...update } : task + ), + })), + + selectTask: (taskId) => + set({ selectedTaskId: taskId }), + + appendOutput: (taskId, output) => + set((state) => { + const existing = state.taskOutputs[taskId] ?? []; + // Avoid duplicates by checking ID + if (existing.some((o) => o.id === output.id)) { + return state; + } + return { + taskOutputs: { + ...state.taskOutputs, + [taskId]: [...existing, output], + }, + }; + }), + + setTaskOutputs: (taskId, outputs) => + set((state) => ({ + taskOutputs: { + ...state.taskOutputs, + [taskId]: outputs, + }, + })), + + clearTaskOutputs: (taskId) => + set((state) => { + const { [taskId]: _, ...rest } = state.taskOutputs; + return { taskOutputs: rest }; + }), +})); + +// Selectors for common use cases +export const selectSelectedTask = (state: TaskState) => + state.tasks.find((t) => t.id === state.selectedTaskId); + +export const selectRunningTasks = (state: TaskState) => + state.tasks.filter((t) => + ['running', 'initializing', 'starting'].includes(t.status) + ); + +export const selectPendingTasks = (state: TaskState) => + state.tasks.filter((t) => t.status === 'pending'); + +export const selectCompletedTasks = (state: TaskState) => + state.tasks.filter((t) => + ['done', 'failed', 'merged'].includes(t.status) + ); |
