Inline app/ (was an embedded git repo)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
52
app/src/lib/api.ts
Normal file
52
app/src/lib/api.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { getConfig } from "./config";
|
||||
import type { Project, Task } from "./types";
|
||||
|
||||
class ApiError extends Error {
|
||||
constructor(public status: number, message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function req<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const { url, token } = await getConfig();
|
||||
if (!token) throw new ApiError(401, "No API token set — open Settings");
|
||||
const res = await fetch(`${url}${path}`, {
|
||||
...init,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
...init?.headers,
|
||||
},
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({ error: res.statusText }));
|
||||
throw new ApiError(res.status, (body as { error?: string }).error ?? `HTTP ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export const api = {
|
||||
tasks: {
|
||||
list: (params: { status?: string; project_id?: string } = {}) => {
|
||||
const q = new URLSearchParams(
|
||||
Object.entries(params).filter(([, v]) => v != null) as [string, string][],
|
||||
).toString();
|
||||
return req<Task[]>(`/v1/tasks${q ? `?${q}` : ""}`);
|
||||
},
|
||||
get: (id: string) => req<Task>(`/v1/tasks/${id}`),
|
||||
create: (data: Partial<Task> & { title: string }) =>
|
||||
req<Task>("/v1/tasks", { method: "POST", body: JSON.stringify(data) }),
|
||||
update: (id: string, data: Partial<Task>) =>
|
||||
req<Task>(`/v1/tasks/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
trash: (id: string) => req<Task>(`/v1/tasks/${id}`, { method: "DELETE" }),
|
||||
},
|
||||
projects: {
|
||||
list: () => req<Project[]>("/v1/projects"),
|
||||
create: (data: Partial<Project> & { name: string }) =>
|
||||
req<Project>("/v1/projects", { method: "POST", body: JSON.stringify(data) }),
|
||||
update: (id: string, data: Partial<Project>) =>
|
||||
req<Project>(`/v1/projects/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
drop: (id: string) => req<Project>(`/v1/projects/${id}`, { method: "DELETE" }),
|
||||
},
|
||||
contexts: { list: () => req<string[]>("/v1/contexts") },
|
||||
};
|
||||
24
app/src/lib/config.ts
Normal file
24
app/src/lib/config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
|
||||
export const DEFAULT_URL = "https://gtd.rehbock.xyz/api";
|
||||
|
||||
let cached: { url: string; token: string } | null = null;
|
||||
|
||||
export async function getConfig() {
|
||||
if (!cached) {
|
||||
const [url, token] = await Promise.all([
|
||||
AsyncStorage.getItem("api_url"),
|
||||
AsyncStorage.getItem("api_token"),
|
||||
]);
|
||||
cached = { url: url || DEFAULT_URL, token: token || "" };
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
export async function setConfig(url: string, token: string) {
|
||||
cached = { url: url.trim().replace(/\/$/, "") || DEFAULT_URL, token: token.trim() };
|
||||
await Promise.all([
|
||||
AsyncStorage.setItem("api_url", cached.url),
|
||||
AsyncStorage.setItem("api_token", cached.token),
|
||||
]);
|
||||
}
|
||||
11
app/src/lib/theme.ts
Normal file
11
app/src/lib/theme.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const C = {
|
||||
bg: "#0F1115",
|
||||
surface: "#1A1D24",
|
||||
surface2: "#232733",
|
||||
border: "#2C313D",
|
||||
text: "#E6E8EC",
|
||||
muted: "#8A909C",
|
||||
accent: "#5B8DEF",
|
||||
done: "#4CAF7D",
|
||||
danger: "#E05B5B",
|
||||
};
|
||||
29
app/src/lib/types.ts
Normal file
29
app/src/lib/types.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export type TaskStatus = "inbox" | "next" | "waiting" | "scheduled" | "someday" | "done" | "trashed";
|
||||
export type ProjectStatus = "active" | "someday" | "completed" | "dropped";
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
title: string;
|
||||
notes: string;
|
||||
status: TaskStatus;
|
||||
project_id: string | null;
|
||||
context: string | null;
|
||||
waiting_for: string | null;
|
||||
due_date: string | null;
|
||||
defer_date: string | null;
|
||||
completed_at: string | null;
|
||||
sort_order: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
status: ProjectStatus;
|
||||
notes: string;
|
||||
sort_order: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
open_tasks?: number;
|
||||
}
|
||||
Reference in New Issue
Block a user