feat: git push status indicator + multi-select bulk push
Release APK / build (push) Has been cancelled
Release APK / build (push) Has been cancelled
- DB migration v2: git_pushed_at column on incidents - markIncidentPushed(id): stamps the push timestamp - loadGitConfig(): shared helper in lib/git.ts, removes duplication - Home screen: colored dot per incident (red=never pushed, orange=dirty/modified after push, green=up to date); dot visible only when git is configured - Home screen: long-press enters selection mode, tap toggles; action bar with Select unpushed / Select all shortcuts + bulk push with sequential progress counter - [id].tsx: marks incident pushed after successful push, updates local state without reload Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,13 @@ async function migrate(db: SQLite.SQLiteDatabase): Promise<void> {
|
||||
INSERT INTO schema_version (version) VALUES (1);
|
||||
`);
|
||||
}
|
||||
|
||||
if (current < 2) {
|
||||
await db.execAsync(`
|
||||
ALTER TABLE incidents ADD COLUMN git_pushed_at TEXT;
|
||||
INSERT INTO schema_version (version) VALUES (2);
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- row mapper ---
|
||||
@@ -60,6 +67,7 @@ interface IncidentRow {
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
tags: string;
|
||||
git_pushed_at: string | null;
|
||||
}
|
||||
|
||||
function rowToIncident(row: IncidentRow): Incident {
|
||||
@@ -74,6 +82,7 @@ function rowToIncident(row: IncidentRow): Incident {
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
tags: JSON.parse(row.tags) as string[],
|
||||
gitPushedAt: row.git_pushed_at ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,6 +116,7 @@ export async function createIncident(draft: IncidentDraft): Promise<Incident> {
|
||||
id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
gitPushedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -163,6 +173,14 @@ export async function deleteIncident(id: string): Promise<void> {
|
||||
await db.runAsync("DELETE FROM incidents WHERE id = ?", [id]);
|
||||
}
|
||||
|
||||
export async function markIncidentPushed(id: string): Promise<void> {
|
||||
const db = await getDb();
|
||||
await db.runAsync(
|
||||
"UPDATE incidents SET git_pushed_at = ? WHERE id = ?",
|
||||
[new Date().toISOString(), id]
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMaxTitleNumber(prefix: string): Promise<number> {
|
||||
const db = await getDb();
|
||||
const rows = await db.getAllAsync<{ title: string }>(
|
||||
|
||||
+39
@@ -1,3 +1,4 @@
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
import { incidentToFilename, renderMarkdown } from "./markdown";
|
||||
import type { Incident } from "@/types/incident";
|
||||
|
||||
@@ -19,6 +20,44 @@ export interface PushResult {
|
||||
|
||||
const TIMEOUT_MS = 15_000;
|
||||
|
||||
export async function loadGitConfig(): Promise<GitConfig | null> {
|
||||
const [provider, gu, gt, go, gr, ghu, ghown, ghrepo, glu, glt, glo, glr] =
|
||||
await Promise.all([
|
||||
SecureStore.getItemAsync("git_provider"),
|
||||
SecureStore.getItemAsync("gitea_url"),
|
||||
SecureStore.getItemAsync("gitea_token"),
|
||||
SecureStore.getItemAsync("gitea_owner"),
|
||||
SecureStore.getItemAsync("gitea_repo"),
|
||||
SecureStore.getItemAsync("github_token"),
|
||||
SecureStore.getItemAsync("github_owner"),
|
||||
SecureStore.getItemAsync("github_repo"),
|
||||
SecureStore.getItemAsync("gitlab_url"),
|
||||
SecureStore.getItemAsync("gitlab_token"),
|
||||
SecureStore.getItemAsync("gitlab_owner"),
|
||||
SecureStore.getItemAsync("gitlab_repo"),
|
||||
]);
|
||||
|
||||
const p = (provider ?? "gitea") as GitProvider;
|
||||
let url: string | undefined;
|
||||
let token: string | null;
|
||||
let owner: string | null;
|
||||
let repo: string | null;
|
||||
|
||||
if (p === "gitea") {
|
||||
url = gu ?? undefined;
|
||||
token = gt; owner = go; repo = gr;
|
||||
} else if (p === "github") {
|
||||
token = ghu; owner = ghown; repo = ghrepo;
|
||||
} else {
|
||||
url = glu ?? undefined;
|
||||
token = glt; owner = glo; repo = glr;
|
||||
}
|
||||
|
||||
const needsUrl = p === "gitea" || p === "gitlab";
|
||||
if (!token || !owner || !repo || (needsUrl && !url)) return null;
|
||||
return { provider: p, url, token, owner, repo };
|
||||
}
|
||||
|
||||
function fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
||||
|
||||
Reference in New Issue
Block a user