feat: safe area fix, delete from repo, add Mistral provider
Release APK / build (push) Has been cancelled

- Wrap root layout with SafeAreaProvider, use useSafeAreaInsets in all
  screens so FAB/action bar/scroll content clear Android nav bar
- Add deleteIncidentFromGit() (Gitea/GitHub/GitLab) — detail screen now
  offers Cancel / Local only / Local + Repo when incident was pushed
- Add Mistral as AI provider (OpenAI-compat, api.mistral.ai/v1,
  mistral-small-latest default)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 09:54:02 +02:00
parent 83fe089ea5
commit 9f163d3b76
7 changed files with 172 additions and 21 deletions
+53 -17
View File
@@ -8,11 +8,13 @@ import {
Share,
} from "react-native";
import { useLocalSearchParams, router, useNavigation } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import * as Clipboard from "expo-clipboard";
import * as SecureStore from "expo-secure-store";
import { getIncidentById, updateIncident, deleteIncident, markIncidentPushed } from "@/lib/db";
import { renderMarkdown } from "@/lib/markdown";
import { pushIncidentToGit, loadGitConfig } from "@/lib/git";
import { pushIncidentToGit, loadGitConfig, deleteIncidentFromGit } from "@/lib/git";
import type { GitConfig } from "@/lib/git";
import type { Incident } from "@/types/incident";
import { Colors } from "@/constants/theme";
@@ -41,11 +43,14 @@ export default function IncidentDetailScreen() {
const navigation = useNavigation();
const [incident, setIncident] = useState<Incident | null>(null);
const [pushing, setPushing] = useState(false);
const [gitConfig, setGitConfig] = useState<GitConfig | null>(null);
const insets = useSafeAreaInsets();
useEffect(() => {
if (id) {
getIncidentById(id).then((inc) => {
Promise.all([getIncidentById(id), loadGitConfig()]).then(([inc, config]) => {
setIncident(inc);
setGitConfig(config);
if (inc) {
navigation.setOptions({ title: inc.title || "Incident" });
}
@@ -55,21 +60,52 @@ export default function IncidentDetailScreen() {
async function handleDelete() {
if (!incident) return;
Alert.alert(
"Delete incident?",
"This action cannot be undone.",
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: async () => {
await deleteIncident(incident.id);
router.replace("/");
const canDeleteFromRepo = !!incident.gitPushedAt && gitConfig !== null;
const doDelete = async (deleteFromRepo: boolean) => {
if (deleteFromRepo && gitConfig) {
const result = await deleteIncidentFromGit(incident, gitConfig);
if (!result.success) {
Alert.alert("Repo deletion failed", result.error ?? "Unknown error");
return;
}
}
await deleteIncident(incident.id);
router.replace("/");
};
if (canDeleteFromRepo) {
Alert.alert(
"Delete incident?",
"This incident was pushed to the repository. Do you also want to delete the file from the repo?",
[
{ text: "Cancel", style: "cancel" },
{
text: "Local only",
onPress: () => doDelete(false),
},
},
]
);
{
text: "Local + Repo",
style: "destructive",
onPress: () => doDelete(true),
},
]
);
} else {
Alert.alert(
"Delete incident?",
"This action cannot be undone.",
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: () => doDelete(false),
},
]
);
}
}
async function handleResolve() {
@@ -125,7 +161,7 @@ export default function IncidentDetailScreen() {
return (
<ScrollView
style={{ flex: 1, backgroundColor: Colors.bg }}
contentContainerStyle={{ padding: 16, paddingBottom: 32 }}
contentContainerStyle={{ padding: 16, paddingBottom: 32 + insets.bottom }}
>
{/* Status badge */}
<View style={{ flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 4 }}>