summaryrefslogtreecommitdiff
path: root/apps/mobile/stores/taskStore.ts
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-18 17:44:50 +0000
committerGitHub <noreply@github.com>2026-01-18 17:44:50 +0000
commit869f21ee2efaefed6a5aa4fbd417c25df8dec02a (patch)
tree2a90820ac817173e5b7154e0ba5e4f5d095f9613 /apps/mobile/stores/taskStore.ts
parent219bca168508e1ea5e91e8a9ce98338afeddfbd2 (diff)
downloadsoryu-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.ts84
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)
+ );