Skip to content

Commit 201478b

Browse files
johnfireclaude
andcommitted
Mobile: user-initiated bug report (web parity)
Account now has a 'Report a bug' action opening a description sheet that posts to /bug-reports (which files a GitHub issue) and confirms the issue number. Previously mobile only auto-reported crashes. New api/bugReports; i18n for all 26 locales. Closes mobile-parity item 6/15 (Tier 1 complete). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9949557 commit 201478b

28 files changed

Lines changed: 307 additions & 26 deletions

packages/mobile/app/(tabs)/account.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import {
99
ActivityIndicator,
1010
Alert,
1111
Linking,
12+
Modal,
13+
Platform,
1214
} from "react-native";
15+
import Constants from "expo-constants";
1316
import { SafeAreaView } from "react-native-safe-area-context";
1417
import { useRouter } from "expo-router";
1518
import { useTranslation } from "react-i18next";
1619
import { useAuth } from "../../src/store/auth";
1720
import { changeEmail, changePassword, deleteAccount } from "../../src/api/auth";
21+
import { submitBugReport } from "../../src/api/bugReports";
1822
import { api } from "../../src/api/client";
1923
import { LanguagePicker } from "../../src/components/LanguagePicker";
2024
import { colors, spacing, radius, font } from "../../src/theme";
@@ -33,6 +37,30 @@ export default function AccountScreen() {
3337
const router = useRouter();
3438
const { user, logout, setUser } = useAuth();
3539

40+
const [bugOpen, setBugOpen] = useState(false);
41+
const [bugText, setBugText] = useState("");
42+
const [bugSubmitting, setBugSubmitting] = useState(false);
43+
44+
async function handleSubmitBug() {
45+
const description = bugText.trim();
46+
if (!description || bugSubmitting) return;
47+
setBugSubmitting(true);
48+
try {
49+
const res = await submitBugReport({
50+
description,
51+
page: "mobile-app",
52+
userAgent: `${Platform.OS} app ${Constants.expoConfig?.version ?? "unknown"}`,
53+
});
54+
setBugOpen(false);
55+
setBugText("");
56+
Alert.alert(t("bug.thanks", { number: res.number }));
57+
} catch (err) {
58+
Alert.alert(t("bug.failed"), (err as Error).message);
59+
} finally {
60+
setBugSubmitting(false);
61+
}
62+
}
63+
3664
const [emailForm, setEmailForm] = useState({
3765
email: "",
3866
password: "",
@@ -261,6 +289,16 @@ export default function AccountScreen() {
261289
</Pressable>
262290
</View>
263291

292+
<SectionHeader title={t("account.reportBug")} />
293+
<View style={s.card}>
294+
<Pressable
295+
style={({ pressed }) => [s.btn, pressed && s.pressed]}
296+
onPress={() => setBugOpen(true)}
297+
>
298+
<Text style={s.btnText}>{t("account.reportBug")}</Text>
299+
</Pressable>
300+
</View>
301+
264302
<SectionHeader title={t("account.language")} />
265303
<View style={s.card}>
266304
<LanguagePicker />
@@ -299,6 +337,39 @@ export default function AccountScreen() {
299337
</Pressable>
300338
</View>
301339
</ScrollView>
340+
341+
<Modal
342+
visible={bugOpen}
343+
transparent
344+
animationType="slide"
345+
onRequestClose={() => setBugOpen(false)}
346+
>
347+
<Pressable style={s.bugBackdrop} onPress={() => setBugOpen(false)} />
348+
<View style={s.bugSheet}>
349+
<Text style={s.bugTitle}>{t("account.reportBug")}</Text>
350+
<TextInput
351+
style={s.bugInput}
352+
placeholder={t("bug.placeholder")}
353+
placeholderTextColor={colors.textDim}
354+
value={bugText}
355+
onChangeText={setBugText}
356+
multiline
357+
textAlignVertical="top"
358+
autoFocus
359+
/>
360+
<Pressable
361+
style={({ pressed }) => [s.btn, pressed && s.pressed]}
362+
onPress={handleSubmitBug}
363+
disabled={bugSubmitting || !bugText.trim()}
364+
>
365+
{bugSubmitting ? (
366+
<ActivityIndicator color={colors.text} />
367+
) : (
368+
<Text style={s.btnText}>{t("bug.send")}</Text>
369+
)}
370+
</Pressable>
371+
</View>
372+
</Modal>
302373
</SafeAreaView>
303374
);
304375
}
@@ -412,4 +483,23 @@ const s = StyleSheet.create({
412483
pressed: { opacity: 0.75 },
413484
btnText: { color: colors.text, fontSize: font.md, fontWeight: "600" },
414485
dangerText: { color: colors.danger },
486+
bugBackdrop: { flex: 1, backgroundColor: "rgba(0,0,0,0.5)" },
487+
bugSheet: {
488+
backgroundColor: colors.surface,
489+
borderTopLeftRadius: radius.lg,
490+
borderTopRightRadius: radius.lg,
491+
padding: spacing.md,
492+
gap: spacing.sm,
493+
},
494+
bugTitle: { color: colors.text, fontSize: font.lg, fontWeight: "700" },
495+
bugInput: {
496+
backgroundColor: colors.surfaceHigh,
497+
color: colors.text,
498+
borderRadius: radius.sm,
499+
borderWidth: 1,
500+
borderColor: colors.border,
501+
padding: spacing.sm,
502+
fontSize: font.md,
503+
minHeight: 120,
504+
},
415505
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { api } from "./client";
2+
3+
export function submitBugReport(data: {
4+
description: string;
5+
page?: string;
6+
userAgent?: string;
7+
}): Promise<{ number: number; url: string }> {
8+
return api.post<{ number: number; url: string }>("/bug-reports", data);
9+
}

packages/mobile/src/i18n/locales/bg.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Език",
145145
"selectLanguage": "Изберете език",
146146
"trash": "Кошче",
147-
"viewTrash": "Изтрити бележки"
147+
"viewTrash": "Изтрити бележки",
148+
"reportBug": "Докладвай грешка"
148149
},
149150
"update": {
150151
"available": "Налична е нова версия",
@@ -171,5 +172,11 @@
171172
"add": "Добави зависимост",
172173
"searchPlaceholder": "Търсене на бележки…",
173174
"noResults": "Няма резултати"
175+
},
176+
"bug": {
177+
"placeholder": "Какво се обърка? Какво очаквахте?",
178+
"send": "Изпрати доклад",
179+
"thanks": "Благодарим! Записано като #{{number}}.",
180+
"failed": "Докладът не можа да се изпрати"
174181
}
175182
}

packages/mobile/src/i18n/locales/cs.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Jazyk",
145145
"selectLanguage": "Vybrat jazyk",
146146
"trash": "Koš",
147-
"viewTrash": "Smazané poznámky"
147+
"viewTrash": "Smazané poznámky",
148+
"reportBug": "Nahlásit chybu"
148149
},
149150
"update": {
150151
"available": "Je dostupná nová verze",
@@ -171,5 +172,11 @@
171172
"add": "Přidat závislost",
172173
"searchPlaceholder": "Hledat poznámky…",
173174
"noResults": "Žádné výsledky"
175+
},
176+
"bug": {
177+
"placeholder": "Co se pokazilo? Co jste očekávali?",
178+
"send": "Odeslat hlášení",
179+
"thanks": "Děkujeme! Zapsáno jako #{{number}}.",
180+
"failed": "Hlášení se nepodařilo odeslat"
174181
}
175182
}

packages/mobile/src/i18n/locales/da.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Sprog",
145145
"selectLanguage": "Vælg sprog",
146146
"trash": "Papirkurv",
147-
"viewTrash": "Slettede noter"
147+
"viewTrash": "Slettede noter",
148+
"reportBug": "Rapportér en fejl"
148149
},
149150
"update": {
150151
"available": "En ny version er tilgængelig",
@@ -171,5 +172,11 @@
171172
"add": "Tilføj afhængighed",
172173
"searchPlaceholder": "Søg noter…",
173174
"noResults": "Ingen resultater"
175+
},
176+
"bug": {
177+
"placeholder": "Hvad gik galt? Hvad forventede du?",
178+
"send": "Send rapport",
179+
"thanks": "Tak! Oprettet som issue #{{number}}.",
180+
"failed": "Kunne ikke sende rapporten"
174181
}
175182
}

packages/mobile/src/i18n/locales/de.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Sprache",
145145
"selectLanguage": "Sprache auswählen",
146146
"trash": "Papierkorb",
147-
"viewTrash": "Gelöschte Notizen"
147+
"viewTrash": "Gelöschte Notizen",
148+
"reportBug": "Fehler melden"
148149
},
149150
"update": {
150151
"available": "Eine neue Version ist verfügbar",
@@ -171,5 +172,11 @@
171172
"add": "Abhängigkeit hinzufügen",
172173
"searchPlaceholder": "Notizen suchen…",
173174
"noResults": "Keine Treffer"
175+
},
176+
"bug": {
177+
"placeholder": "Was ist passiert? Was hast du erwartet?",
178+
"send": "Bericht senden",
179+
"thanks": "Danke! Erfasst als Issue #{{number}}.",
180+
"failed": "Bericht konnte nicht gesendet werden"
174181
}
175182
}

packages/mobile/src/i18n/locales/el.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Γλώσσα",
145145
"selectLanguage": "Επιλογή γλώσσας",
146146
"trash": "Κάδος",
147-
"viewTrash": "Διαγραμμένες σημειώσεις"
147+
"viewTrash": "Διαγραμμένες σημειώσεις",
148+
"reportBug": "Αναφορά σφάλματος"
148149
},
149150
"update": {
150151
"available": "Μια νέα έκδοση είναι διαθέσιμη",
@@ -171,5 +172,11 @@
171172
"add": "Προσθήκη εξάρτησης",
172173
"searchPlaceholder": "Αναζήτηση σημειώσεων…",
173174
"noResults": "Κανένα αποτέλεσμα"
175+
},
176+
"bug": {
177+
"placeholder": "Τι πήγε στραβά; Τι περιμένατε;",
178+
"send": "Αποστολή αναφοράς",
179+
"thanks": "Ευχαριστούμε! Καταχωρήθηκε ως #{{number}}.",
180+
"failed": "Αποτυχία αποστολής αναφοράς"
174181
}
175182
}

packages/mobile/src/i18n/locales/en.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Language",
145145
"selectLanguage": "Select language",
146146
"trash": "Trash",
147-
"viewTrash": "Deleted notes"
147+
"viewTrash": "Deleted notes",
148+
"reportBug": "Report a bug"
148149
},
149150
"update": {
150151
"available": "A new version is available",
@@ -171,5 +172,11 @@
171172
"add": "Add dependency",
172173
"searchPlaceholder": "Search notes…",
173174
"noResults": "No matches"
175+
},
176+
"bug": {
177+
"placeholder": "What went wrong? What did you expect?",
178+
"send": "Send report",
179+
"thanks": "Thanks! Filed as issue #{{number}}.",
180+
"failed": "Couldn’t send report"
174181
}
175182
}

packages/mobile/src/i18n/locales/es.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Idioma",
145145
"selectLanguage": "Seleccionar idioma",
146146
"trash": "Papelera",
147-
"viewTrash": "Notas eliminadas"
147+
"viewTrash": "Notas eliminadas",
148+
"reportBug": "Informar de un error"
148149
},
149150
"update": {
150151
"available": "Hay una nueva versión disponible",
@@ -171,5 +172,11 @@
171172
"add": "Añadir dependencia",
172173
"searchPlaceholder": "Buscar notas…",
173174
"noResults": "Sin resultados"
175+
},
176+
"bug": {
177+
"placeholder": "¿Qué falló? ¿Qué esperabas?",
178+
"send": "Enviar informe",
179+
"thanks": "¡Gracias! Registrado como incidencia #{{number}}.",
180+
"failed": "No se pudo enviar el informe"
174181
}
175182
}

packages/mobile/src/i18n/locales/et.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"language": "Keel",
145145
"selectLanguage": "Vali keel",
146146
"trash": "Prügikast",
147-
"viewTrash": "Kustutatud märkmed"
147+
"viewTrash": "Kustutatud märkmed",
148+
"reportBug": "Teata veast"
148149
},
149150
"update": {
150151
"available": "Saadaval on uus versioon",
@@ -171,5 +172,11 @@
171172
"add": "Lisa sõltuvus",
172173
"searchPlaceholder": "Otsi märkmeid…",
173174
"noResults": "Tulemusi pole"
175+
},
176+
"bug": {
177+
"placeholder": "Mis läks valesti? Mida ootasid?",
178+
"send": "Saada raport",
179+
"thanks": "Aitäh! Registreeritud kui #{{number}}.",
180+
"failed": "Raporti saatmine ebaõnnestus"
174181
}
175182
}

0 commit comments

Comments
 (0)