Add MVP features: edit mode, voice input, Gitea push, service autocomplete

- new.tsx: edit mode via editId param, per-field voice dictation (MIC/STOP),
  service autocomplete dropdown from DB history
- incident/[id].tsx: Push to Gitea action reads SecureStore config + md template
- index.tsx: homeView=capture redirects to /new on first mount
- _layout.tsx: onboarding gate wired (useSettings + router.replace)
- ci: GitHub Actions workflow builds debug APK via expo prebuild + Gradle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 12:03:44 +02:00
parent 8388f49fd4
commit 418eb1daa1
5 changed files with 294 additions and 19 deletions
+53
View File
@@ -9,8 +9,10 @@ import {
} from "react-native";
import { useLocalSearchParams, router, useNavigation } from "expo-router";
import * as Clipboard from "expo-clipboard";
import * as SecureStore from "expo-secure-store";
import { getIncidentById, updateIncident } from "@/lib/db";
import { renderMarkdown } from "@/lib/markdown";
import { pushIncidentToGitea } from "@/lib/gitea";
import type { Incident } from "@/types/incident";
import { Colors } from "@/constants/theme";
@@ -38,6 +40,7 @@ export default function IncidentDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const navigation = useNavigation();
const [incident, setIncident] = useState<Incident | null>(null);
const [pushing, setPushing] = useState(false);
useEffect(() => {
if (id) {
@@ -56,6 +59,33 @@ export default function IncidentDetailScreen() {
setIncident({ ...incident, status: "resolved" });
}
async function handleGiteaPush() {
if (!incident) return;
const [url, token, owner, repo, template] = await Promise.all([
SecureStore.getItemAsync("gitea_url"),
SecureStore.getItemAsync("gitea_token"),
SecureStore.getItemAsync("gitea_owner"),
SecureStore.getItemAsync("gitea_repo"),
SecureStore.getItemAsync("md_template"),
]);
if (!url || !token || !owner || !repo) {
Alert.alert("Gitea not configured", "Set up Gitea in Settings first.");
return;
}
setPushing(true);
const result = await pushIncidentToGitea(
incident,
{ url, token, owner, repo },
template ?? undefined
);
setPushing(false);
if (result.success) {
Alert.alert("Pushed", result.url ? `${result.url}` : "Push successful.");
} else {
Alert.alert("Push failed", result.error ?? "Unknown error");
}
}
async function handleExport() {
if (!incident) return;
const md = renderMarkdown(incident);
@@ -188,6 +218,29 @@ export default function IncidentDetailScreen() {
</Text>
</Pressable>
<Pressable
onPress={handleGiteaPush}
disabled={pushing}
style={{
backgroundColor: pushing ? Colors.surface2 : Colors.surface,
borderWidth: 1,
borderColor: pushing ? Colors.border : Colors.primary,
borderRadius: 10,
padding: 14,
alignItems: "center",
}}
>
<Text
style={{
color: pushing ? Colors.textDim : Colors.primary,
fontWeight: "600",
fontSize: 15,
}}
>
{pushing ? "Pushing…" : "Push to Gitea"}
</Text>
</Pressable>
<Pressable
onPress={() => router.push({ pathname: "/new", params: { editId: incident.id } })}
style={{