@forthtilliath/react-native-kit 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +183 -23
  2. package/dist/components/index.d.ts +6 -0
  3. package/dist/components/index.d.ts.map +1 -0
  4. package/dist/components/index.js +5 -0
  5. package/dist/components/list/SwipeableRow.d.ts.map +1 -0
  6. package/dist/components/list/Thumbnail.d.ts.map +1 -0
  7. package/dist/components/list/index.d.ts +3 -0
  8. package/dist/components/list/index.d.ts.map +1 -0
  9. package/dist/components/list/index.js +2 -0
  10. package/dist/components/picker/PhotoPicker.d.ts.map +1 -0
  11. package/dist/components/picker/PickerModal.d.ts.map +1 -0
  12. package/dist/components/picker/VoiceSearchButton.d.ts.map +1 -0
  13. package/dist/components/picker/index.d.ts +4 -0
  14. package/dist/components/picker/index.d.ts.map +1 -0
  15. package/dist/components/picker/index.js +3 -0
  16. package/dist/components/settings/AboutSettingsScreen.d.ts +24 -0
  17. package/dist/components/settings/AboutSettingsScreen.d.ts.map +1 -0
  18. package/dist/components/settings/AboutSettingsScreen.js +41 -0
  19. package/dist/components/settings/BackupSettingsScreen.d.ts +52 -0
  20. package/dist/components/settings/BackupSettingsScreen.d.ts.map +1 -0
  21. package/dist/components/settings/BackupSettingsScreen.js +120 -0
  22. package/dist/components/settings/ContactSettingsScreen.d.ts +19 -0
  23. package/dist/components/settings/ContactSettingsScreen.d.ts.map +1 -0
  24. package/dist/components/settings/ContactSettingsScreen.js +39 -0
  25. package/dist/components/settings/PrivacySettingsScreen.d.ts +23 -0
  26. package/dist/components/settings/PrivacySettingsScreen.d.ts.map +1 -0
  27. package/dist/components/settings/PrivacySettingsScreen.js +51 -0
  28. package/dist/components/settings/SettingsMenu.d.ts +24 -0
  29. package/dist/components/settings/SettingsMenu.d.ts.map +1 -0
  30. package/dist/components/settings/SettingsMenu.js +40 -0
  31. package/dist/components/settings/ThemeSettingsScreen.d.ts +32 -0
  32. package/dist/components/settings/ThemeSettingsScreen.d.ts.map +1 -0
  33. package/dist/components/settings/ThemeSettingsScreen.js +71 -0
  34. package/dist/components/settings/UpdateSettingsScreen.d.ts +62 -0
  35. package/dist/components/settings/UpdateSettingsScreen.d.ts.map +1 -0
  36. package/dist/components/settings/UpdateSettingsScreen.js +178 -0
  37. package/dist/components/settings/index.d.ts +8 -0
  38. package/dist/components/settings/index.d.ts.map +1 -0
  39. package/dist/components/settings/index.js +7 -0
  40. package/dist/{ThemeOptionList.d.ts → components/theme/ThemeOptionList.d.ts} +1 -1
  41. package/dist/components/theme/ThemeOptionList.d.ts.map +1 -0
  42. package/dist/{ThemeToggle.d.ts → components/theme/ThemeToggle.d.ts} +1 -1
  43. package/dist/components/theme/ThemeToggle.d.ts.map +1 -0
  44. package/dist/components/theme/index.d.ts +3 -0
  45. package/dist/components/theme/index.d.ts.map +1 -0
  46. package/dist/components/theme/index.js +2 -0
  47. package/dist/components/update/ChangelogNotes.d.ts.map +1 -0
  48. package/dist/components/update/UpdateAvailableBanner.d.ts.map +1 -0
  49. package/dist/components/update/index.d.ts +3 -0
  50. package/dist/components/update/index.d.ts.map +1 -0
  51. package/dist/components/update/index.js +2 -0
  52. package/dist/hooks/index.d.ts +5 -0
  53. package/dist/hooks/index.d.ts.map +1 -0
  54. package/dist/hooks/index.js +4 -0
  55. package/dist/hooks/useDebouncedChange.d.ts.map +1 -0
  56. package/dist/hooks/useEffectiveColorScheme.d.ts.map +1 -0
  57. package/dist/hooks/useSubmitGuard.d.ts.map +1 -0
  58. package/dist/hooks/useUpdateCheck.d.ts.map +1 -0
  59. package/dist/index.d.ts +29 -22
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +33 -25
  62. package/dist/utils/format/escapeCsvField.d.ts.map +1 -0
  63. package/dist/utils/format/escapeHtml.d.ts.map +1 -0
  64. package/dist/utils/format/formatCsvNumber.d.ts.map +1 -0
  65. package/dist/utils/format/index.d.ts +4 -0
  66. package/dist/utils/format/index.d.ts.map +1 -0
  67. package/dist/utils/format/index.js +3 -0
  68. package/dist/utils/helpers/confirmDestructive.d.ts.map +1 -0
  69. package/dist/utils/helpers/getMostRecentIds.d.ts.map +1 -0
  70. package/dist/utils/helpers/getPeriodStartMs.d.ts.map +1 -0
  71. package/dist/utils/helpers/index.d.ts +7 -0
  72. package/dist/utils/helpers/index.d.ts.map +1 -0
  73. package/dist/utils/helpers/index.js +6 -0
  74. package/dist/utils/helpers/nextInCycle.d.ts.map +1 -0
  75. package/dist/utils/helpers/normalizeForSearch.d.ts.map +1 -0
  76. package/dist/utils/helpers/rankByNameMatch.d.ts.map +1 -0
  77. package/dist/utils/index.d.ts +3 -0
  78. package/dist/utils/index.d.ts.map +1 -0
  79. package/dist/utils/index.js +2 -0
  80. package/package.json +7 -7
  81. package/dist/ChangelogNotes.d.ts.map +0 -1
  82. package/dist/PhotoPicker.d.ts.map +0 -1
  83. package/dist/PickerModal.d.ts.map +0 -1
  84. package/dist/SwipeableRow.d.ts.map +0 -1
  85. package/dist/ThemeOptionList.d.ts.map +0 -1
  86. package/dist/ThemeToggle.d.ts.map +0 -1
  87. package/dist/Thumbnail.d.ts.map +0 -1
  88. package/dist/UpdateAvailableBanner.d.ts.map +0 -1
  89. package/dist/VoiceSearchButton.d.ts.map +0 -1
  90. package/dist/confirmDestructive.d.ts.map +0 -1
  91. package/dist/useDebouncedChange.d.ts.map +0 -1
  92. package/dist/useEffectiveColorScheme.d.ts.map +0 -1
  93. package/dist/useSubmitGuard.d.ts.map +0 -1
  94. package/dist/useUpdateCheck.d.ts.map +0 -1
  95. package/dist/utils/escapeCsvField.d.ts.map +0 -1
  96. package/dist/utils/escapeHtml.d.ts.map +0 -1
  97. package/dist/utils/formatCsvNumber.d.ts.map +0 -1
  98. package/dist/utils/getMostRecentIds.d.ts.map +0 -1
  99. package/dist/utils/getPeriodStartMs.d.ts.map +0 -1
  100. package/dist/utils/nextInCycle.d.ts.map +0 -1
  101. package/dist/utils/normalizeForSearch.d.ts.map +0 -1
  102. package/dist/utils/rankByNameMatch.d.ts.map +0 -1
  103. /package/dist/{SwipeableRow.d.ts → components/list/SwipeableRow.d.ts} +0 -0
  104. /package/dist/{SwipeableRow.js → components/list/SwipeableRow.js} +0 -0
  105. /package/dist/{Thumbnail.d.ts → components/list/Thumbnail.d.ts} +0 -0
  106. /package/dist/{Thumbnail.js → components/list/Thumbnail.js} +0 -0
  107. /package/dist/{PhotoPicker.d.ts → components/picker/PhotoPicker.d.ts} +0 -0
  108. /package/dist/{PhotoPicker.js → components/picker/PhotoPicker.js} +0 -0
  109. /package/dist/{PickerModal.d.ts → components/picker/PickerModal.d.ts} +0 -0
  110. /package/dist/{PickerModal.js → components/picker/PickerModal.js} +0 -0
  111. /package/dist/{VoiceSearchButton.d.ts → components/picker/VoiceSearchButton.d.ts} +0 -0
  112. /package/dist/{VoiceSearchButton.js → components/picker/VoiceSearchButton.js} +0 -0
  113. /package/dist/{ThemeOptionList.js → components/theme/ThemeOptionList.js} +0 -0
  114. /package/dist/{ThemeToggle.js → components/theme/ThemeToggle.js} +0 -0
  115. /package/dist/{ChangelogNotes.d.ts → components/update/ChangelogNotes.d.ts} +0 -0
  116. /package/dist/{ChangelogNotes.js → components/update/ChangelogNotes.js} +0 -0
  117. /package/dist/{UpdateAvailableBanner.d.ts → components/update/UpdateAvailableBanner.d.ts} +0 -0
  118. /package/dist/{UpdateAvailableBanner.js → components/update/UpdateAvailableBanner.js} +0 -0
  119. /package/dist/{useDebouncedChange.d.ts → hooks/useDebouncedChange.d.ts} +0 -0
  120. /package/dist/{useDebouncedChange.js → hooks/useDebouncedChange.js} +0 -0
  121. /package/dist/{useEffectiveColorScheme.d.ts → hooks/useEffectiveColorScheme.d.ts} +0 -0
  122. /package/dist/{useEffectiveColorScheme.js → hooks/useEffectiveColorScheme.js} +0 -0
  123. /package/dist/{useSubmitGuard.d.ts → hooks/useSubmitGuard.d.ts} +0 -0
  124. /package/dist/{useSubmitGuard.js → hooks/useSubmitGuard.js} +0 -0
  125. /package/dist/{useUpdateCheck.d.ts → hooks/useUpdateCheck.d.ts} +0 -0
  126. /package/dist/{useUpdateCheck.js → hooks/useUpdateCheck.js} +0 -0
  127. /package/dist/utils/{escapeCsvField.d.ts → format/escapeCsvField.d.ts} +0 -0
  128. /package/dist/utils/{escapeCsvField.js → format/escapeCsvField.js} +0 -0
  129. /package/dist/utils/{escapeHtml.d.ts → format/escapeHtml.d.ts} +0 -0
  130. /package/dist/utils/{escapeHtml.js → format/escapeHtml.js} +0 -0
  131. /package/dist/utils/{formatCsvNumber.d.ts → format/formatCsvNumber.d.ts} +0 -0
  132. /package/dist/utils/{formatCsvNumber.js → format/formatCsvNumber.js} +0 -0
  133. /package/dist/{confirmDestructive.d.ts → utils/helpers/confirmDestructive.d.ts} +0 -0
  134. /package/dist/{confirmDestructive.js → utils/helpers/confirmDestructive.js} +0 -0
  135. /package/dist/utils/{getMostRecentIds.d.ts → helpers/getMostRecentIds.d.ts} +0 -0
  136. /package/dist/utils/{getMostRecentIds.js → helpers/getMostRecentIds.js} +0 -0
  137. /package/dist/utils/{getPeriodStartMs.d.ts → helpers/getPeriodStartMs.d.ts} +0 -0
  138. /package/dist/utils/{getPeriodStartMs.js → helpers/getPeriodStartMs.js} +0 -0
  139. /package/dist/utils/{nextInCycle.d.ts → helpers/nextInCycle.d.ts} +0 -0
  140. /package/dist/utils/{nextInCycle.js → helpers/nextInCycle.js} +0 -0
  141. /package/dist/utils/{normalizeForSearch.d.ts → helpers/normalizeForSearch.d.ts} +0 -0
  142. /package/dist/utils/{normalizeForSearch.js → helpers/normalizeForSearch.js} +0 -0
  143. /package/dist/utils/{rankByNameMatch.d.ts → helpers/rankByNameMatch.d.ts} +0 -0
  144. /package/dist/utils/{rankByNameMatch.js → helpers/rankByNameMatch.js} +0 -0
@@ -0,0 +1,19 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ export interface ContactSettingsScreenStyles {
3
+ container?: StyleProp<ViewStyle>;
4
+ hint?: StyleProp<TextStyle>;
5
+ emailButton?: StyleProp<ViewStyle>;
6
+ emailButtonText?: StyleProp<TextStyle>;
7
+ separator?: StyleProp<ViewStyle>;
8
+ }
9
+ export interface ContactSettingsScreenLabels {
10
+ hint?: string;
11
+ footer?: string;
12
+ }
13
+ export interface ContactSettingsScreenProps {
14
+ email: string;
15
+ labels?: ContactSettingsScreenLabels;
16
+ styles?: ContactSettingsScreenStyles;
17
+ }
18
+ export declare function ContactSettingsScreen({ email, labels, styles, }: ContactSettingsScreenProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=ContactSettingsScreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ContactSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,MAAM,CAAC,EAAE,2BAA2B,CAAC;CACtC;AA6BD,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,MAAM,EACN,MAAM,GACP,EAAE,0BAA0B,+BA8B5B"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Linking, Pressable, Text, View } from "react-native";
3
+ const defaultLabels = {
4
+ hint: "Une question, un bug, une suggestion ?",
5
+ footer: "Si tu remontes un bug, précise si possible ce que tu faisais et ce que tu attendais — ça aide à le reproduire.",
6
+ };
7
+ const defaultStyles = {
8
+ container: {},
9
+ hint: { fontSize: 13, color: "#6b7280", marginBottom: 12 },
10
+ emailButton: {
11
+ paddingVertical: 12,
12
+ paddingHorizontal: 16,
13
+ borderRadius: 8,
14
+ borderWidth: 1,
15
+ borderColor: "#2563eb",
16
+ alignItems: "center",
17
+ },
18
+ emailButtonText: { color: "#2563eb", fontWeight: "700" },
19
+ separator: {
20
+ height: 1,
21
+ marginVertical: 16,
22
+ backgroundColor: "#e5e7eb",
23
+ },
24
+ };
25
+ // A mailto: button plus a short "how to report a bug" hint — the standard
26
+ // "Contact" section of a settings tab.
27
+ export function ContactSettingsScreen({ email, labels, styles, }) {
28
+ const merged = {
29
+ container: [defaultStyles.container, styles?.container],
30
+ hint: [defaultStyles.hint, styles?.hint],
31
+ emailButton: [defaultStyles.emailButton, styles?.emailButton],
32
+ emailButtonText: [defaultStyles.emailButtonText, styles?.emailButtonText],
33
+ separator: [defaultStyles.separator, styles?.separator],
34
+ };
35
+ const t = { ...defaultLabels, ...labels };
36
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsx(Pressable, { style: merged.emailButton, onPress: () => {
37
+ void Linking.openURL(`mailto:${email}`);
38
+ }, accessibilityRole: "button", accessibilityLabel: `Envoyer un email à ${email}`, children: _jsx(Text, { style: merged.emailButtonText, children: `✉️ ${email}` }) }), _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.footer })] }));
39
+ }
@@ -0,0 +1,23 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ export interface PrivacySettingsScreenSection {
3
+ title: string;
4
+ paragraphs: string[];
5
+ }
6
+ export interface PrivacySettingsScreenStyles {
7
+ container?: StyleProp<ViewStyle>;
8
+ title?: StyleProp<TextStyle>;
9
+ paragraph?: StyleProp<TextStyle>;
10
+ separator?: StyleProp<ViewStyle>;
11
+ }
12
+ export interface PrivacySettingsScreenProps {
13
+ /**
14
+ * Defaults to the standard "everything stays on this device" three
15
+ * sections (no data collected, local storage only, sharing is opt-in).
16
+ * Pass your own to describe a different setup (e.g. an app that does sync
17
+ * to a server) — this fully replaces the default, it isn't merged with it.
18
+ */
19
+ sections?: PrivacySettingsScreenSection[];
20
+ styles?: PrivacySettingsScreenStyles;
21
+ }
22
+ export declare function PrivacySettingsScreen({ sections, styles, }: PrivacySettingsScreenProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=PrivacySettingsScreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrivacySettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/PrivacySettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,4BAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,2BAA2B,CAAC;CACtC;AA4CD,wBAAgB,qBAAqB,CAAC,EACpC,QAA0B,EAC1B,MAAM,GACP,EAAE,0BAA0B,+BAuB5B"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, View } from "react-native";
3
+ const defaultSections = [
4
+ {
5
+ title: "Aucune donnée envoyée nulle part",
6
+ paragraphs: [
7
+ "Cette application ne collecte aucune donnée, ne fait appel à aucun serveur, et ne contient aucun outil de suivi ni de publicité.",
8
+ ],
9
+ },
10
+ {
11
+ title: "Stockage local uniquement",
12
+ paragraphs: [
13
+ "Tout ce que tu saisis reste stocké uniquement sur cet appareil, dans le stockage local de l'application. Rien n'est envoyé ailleurs.",
14
+ "Ces données sont perdues si tu désinstalles l'application ou si tu effaces son stockage — pense à utiliser une sauvegarde régulière pour pouvoir les restaurer.",
15
+ ],
16
+ },
17
+ {
18
+ title: "Partage volontaire uniquement",
19
+ paragraphs: [
20
+ "La seule façon pour une donnée de quitter cet appareil, c'est quand tu choisis toi-même de la partager (export d'une sauvegarde...), via le sélecteur de partage natif du téléphone.",
21
+ ],
22
+ },
23
+ ];
24
+ const defaultStyles = {
25
+ container: {},
26
+ title: { fontSize: 15, fontWeight: "700", color: "#111827", marginBottom: 6 },
27
+ paragraph: {
28
+ fontSize: 14,
29
+ lineHeight: 20,
30
+ color: "#374151",
31
+ marginBottom: 8,
32
+ },
33
+ separator: {
34
+ height: 1,
35
+ marginVertical: 16,
36
+ backgroundColor: "#e5e7eb",
37
+ },
38
+ };
39
+ // Privacy section of a settings tab: a list of titled paragraph sections,
40
+ // separated by a thin rule. Ships with the standard "everything stays local"
41
+ // copy as a default — override `sections` entirely for an app with different
42
+ // data handling (e.g. one that does sync to a server).
43
+ export function PrivacySettingsScreen({ sections = defaultSections, styles, }) {
44
+ const merged = {
45
+ container: [defaultStyles.container, styles?.container],
46
+ title: [defaultStyles.title, styles?.title],
47
+ paragraph: [defaultStyles.paragraph, styles?.paragraph],
48
+ separator: [defaultStyles.separator, styles?.separator],
49
+ };
50
+ return (_jsx(View, { style: merged.container, children: sections.map((section, index) => (_jsxs(View, { children: [index > 0 && _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.title, children: section.title }), section.paragraphs.map((paragraph) => (_jsx(Text, { style: merged.paragraph, children: paragraph }, paragraph)))] }, section.title))) }));
51
+ }
@@ -0,0 +1,24 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ export interface SettingsMenuItem {
3
+ /** Stable React key — also what distinguishes otherwise-identical items. */
4
+ key: string;
5
+ emoji: string;
6
+ title: string;
7
+ hint?: string;
8
+ onPress: () => void;
9
+ }
10
+ export interface SettingsMenuStyles {
11
+ container?: StyleProp<ViewStyle>;
12
+ row?: StyleProp<ViewStyle>;
13
+ titleColumn?: StyleProp<ViewStyle>;
14
+ emoji?: StyleProp<TextStyle>;
15
+ title?: StyleProp<TextStyle>;
16
+ hint?: StyleProp<TextStyle>;
17
+ chevronColor?: string;
18
+ }
19
+ export interface SettingsMenuProps {
20
+ items: SettingsMenuItem[];
21
+ styles?: SettingsMenuStyles;
22
+ }
23
+ export declare function SettingsMenu({ items, styles }: SettingsMenuProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=SettingsMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsMenu.d.ts","sourceRoot":"","sources":["../../../src/components/settings/SettingsMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AA4BD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,iBAAiB,+BA+BhE"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, Text, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ const defaultStyles = {
5
+ container: { gap: 10 },
6
+ row: {
7
+ flexDirection: "row",
8
+ alignItems: "center",
9
+ gap: 12,
10
+ borderWidth: 1,
11
+ borderColor: "#e5e7eb",
12
+ borderRadius: 10,
13
+ padding: 14,
14
+ },
15
+ titleColumn: { flex: 1 },
16
+ emoji: { fontSize: 24 },
17
+ title: {
18
+ fontSize: 16,
19
+ fontWeight: "600",
20
+ color: "#111827",
21
+ },
22
+ hint: { fontSize: 12, color: "#6b7280", marginTop: 2 },
23
+ chevronColor: "#9ca3af",
24
+ };
25
+ // Menu screen for a settings tab: one row per section (emoji + title + hint),
26
+ // navigating to it on press. Has no opinion on how navigation works — each
27
+ // item carries its own `onPress` (e.g. `() => router.push("/settings/xxx")`),
28
+ // so this doesn't depend on expo-router or any other specific navigator.
29
+ export function SettingsMenu({ items, styles }) {
30
+ const merged = {
31
+ container: [defaultStyles.container, styles?.container],
32
+ row: [defaultStyles.row, styles?.row],
33
+ titleColumn: [defaultStyles.titleColumn, styles?.titleColumn],
34
+ emoji: [defaultStyles.emoji, styles?.emoji],
35
+ title: [defaultStyles.title, styles?.title],
36
+ hint: [defaultStyles.hint, styles?.hint],
37
+ };
38
+ const chevronColor = styles?.chevronColor ?? defaultStyles.chevronColor;
39
+ return (_jsx(View, { style: merged.container, children: items.map((item) => (_jsxs(Pressable, { style: merged.row, onPress: item.onPress, accessibilityRole: "button", accessibilityLabel: item.title, children: [_jsx(Text, { style: merged.emoji, children: item.emoji }), _jsxs(View, { style: merged.titleColumn, children: [_jsx(Text, { style: merged.title, children: item.title }), item.hint ? _jsx(Text, { style: merged.hint, children: item.hint }) : null] }), _jsx(Ionicons, { name: "chevron-forward", size: 20, color: chevronColor })] }, item.key))) }));
40
+ }
@@ -0,0 +1,32 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
3
+ export interface ThemeSettingsScreenEmojis {
4
+ light?: string;
5
+ dark?: string;
6
+ system?: string;
7
+ }
8
+ export interface ThemeSettingsScreenLabels {
9
+ light?: string;
10
+ dark?: string;
11
+ system?: string;
12
+ hint?: string;
13
+ }
14
+ export interface ThemeSettingsScreenStyles {
15
+ container?: StyleProp<ViewStyle>;
16
+ hint?: StyleProp<TextStyle>;
17
+ row?: StyleProp<ViewStyle>;
18
+ rowActive?: StyleProp<ViewStyle>;
19
+ emoji?: StyleProp<TextStyle>;
20
+ label?: StyleProp<TextStyle>;
21
+ labelActive?: StyleProp<TextStyle>;
22
+ check?: StyleProp<TextStyle>;
23
+ }
24
+ export interface ThemeSettingsScreenProps {
25
+ value: ThemePreference;
26
+ onChange: (preference: ThemePreference) => void;
27
+ emojis?: ThemeSettingsScreenEmojis;
28
+ labels?: ThemeSettingsScreenLabels;
29
+ styles?: ThemeSettingsScreenStyles;
30
+ }
31
+ export declare function ThemeSettingsScreen({ value, onChange, emojis, labels, styles, }: ThemeSettingsScreenProps): import("react").JSX.Element;
32
+ //# sourceMappingURL=ThemeSettingsScreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ThemeSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AAoDD,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE,wBAAwB,+BA2C1B"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, Text, View } from "react-native";
3
+ const OPTIONS = ["light", "dark", "system"];
4
+ const defaultEmojis = {
5
+ light: "☀️",
6
+ dark: "🌙",
7
+ system: "⚙️",
8
+ };
9
+ const defaultLabels = {
10
+ light: "Clair",
11
+ dark: "Sombre",
12
+ system: "Système",
13
+ hint: "« Système » suit automatiquement le réglage clair/sombre de ton téléphone.",
14
+ };
15
+ const defaultStyles = {
16
+ container: { gap: 10 },
17
+ hint: { fontSize: 13, color: "#6b7280", marginBottom: 6 },
18
+ row: {
19
+ flexDirection: "row",
20
+ alignItems: "center",
21
+ gap: 12,
22
+ borderWidth: 1,
23
+ borderColor: "#e5e7eb",
24
+ borderRadius: 10,
25
+ padding: 14,
26
+ },
27
+ rowActive: {
28
+ borderColor: "#2563eb",
29
+ backgroundColor: "#2563eb1a",
30
+ },
31
+ emoji: { fontSize: 20 },
32
+ label: {
33
+ flex: 1,
34
+ fontSize: 15,
35
+ fontWeight: "600",
36
+ color: "#111827",
37
+ },
38
+ labelActive: { color: "#2563eb" },
39
+ check: {
40
+ fontSize: 16,
41
+ fontWeight: "700",
42
+ color: "#2563eb",
43
+ },
44
+ };
45
+ // Selectable list of rows (emoji + label + checkmark) for a light/dark/system
46
+ // theme preference — same data contract as `ThemeOptionList`, but an emoji +
47
+ // plain-text checkmark instead of vector icons. Pick whichever visual fits
48
+ // your app; both cover the exact same `light`/`dark`/`system` preference.
49
+ export function ThemeSettingsScreen({ value, onChange, emojis, labels, styles, }) {
50
+ // Style fields are merged as arrays (default, then override) so a partial
51
+ // override only changes the properties it specifies instead of replacing
52
+ // the whole default style object.
53
+ const merged = {
54
+ container: [defaultStyles.container, styles?.container],
55
+ hint: [defaultStyles.hint, styles?.hint],
56
+ row: [defaultStyles.row, styles?.row],
57
+ rowActive: [defaultStyles.rowActive, styles?.rowActive],
58
+ emoji: [defaultStyles.emoji, styles?.emoji],
59
+ label: [defaultStyles.label, styles?.label],
60
+ labelActive: [defaultStyles.labelActive, styles?.labelActive],
61
+ check: [defaultStyles.check, styles?.check],
62
+ };
63
+ const e = { ...defaultEmojis, ...emojis };
64
+ const t = { ...defaultLabels, ...labels };
65
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), OPTIONS.map((option) => {
66
+ const active = option === value;
67
+ return (_jsxs(Pressable, { style: [merged.row, active && merged.rowActive], onPress: () => {
68
+ onChange(option);
69
+ }, accessibilityRole: "radio", accessibilityState: { checked: active }, accessibilityLabel: t[option], children: [_jsx(Text, { style: merged.emoji, children: e[option] }), _jsx(Text, { style: [merged.label, active && merged.labelActive], children: t[option] }), active && _jsx(Text, { style: merged.check, children: "\u2713" })] }, option));
70
+ })] }));
71
+ }
@@ -0,0 +1,62 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import type { ChangelogNotesStyles } from "../update/ChangelogNotes.js";
3
+ export interface UpdateSettingsScreenRelease {
4
+ version: string;
5
+ notes: string;
6
+ apkUrl: string;
7
+ }
8
+ export interface UpdateSettingsScreenHistoryEntry {
9
+ version: string;
10
+ notes: string;
11
+ publishedAt?: string | null;
12
+ }
13
+ export interface UpdateSettingsScreenLabels {
14
+ installedVersionLabel?: string;
15
+ checkButton?: string;
16
+ upToDate?: string;
17
+ checkError?: string;
18
+ downloadError?: string;
19
+ availableTitle?: (version: string) => string;
20
+ installButton?: (version: string) => string;
21
+ installButtonAccessibilityLabel?: (version: string) => string;
22
+ downloadingLabel?: (percent: number) => string;
23
+ downloadingHint?: string;
24
+ historyTitle?: string;
25
+ }
26
+ export interface UpdateSettingsScreenStyles {
27
+ container?: StyleProp<ViewStyle>;
28
+ infoBox?: StyleProp<ViewStyle>;
29
+ infoLabel?: StyleProp<TextStyle>;
30
+ infoValue?: StyleProp<TextStyle>;
31
+ helpText?: StyleProp<TextStyle>;
32
+ errorText?: StyleProp<TextStyle>;
33
+ button?: StyleProp<ViewStyle>;
34
+ buttonDisabled?: StyleProp<ViewStyle>;
35
+ buttonText?: StyleProp<TextStyle>;
36
+ activityIndicatorColor?: string;
37
+ updateAvailableBox?: StyleProp<ViewStyle>;
38
+ updateAvailableTitle?: StyleProp<TextStyle>;
39
+ changelog?: StyleProp<ViewStyle>;
40
+ changelogTitle?: StyleProp<TextStyle>;
41
+ changelogEntry?: StyleProp<ViewStyle>;
42
+ changelogEntryHeader?: StyleProp<ViewStyle>;
43
+ changelogVersion?: StyleProp<TextStyle>;
44
+ changelogDate?: StyleProp<TextStyle>;
45
+ notes?: ChangelogNotesStyles;
46
+ }
47
+ export interface UpdateSettingsScreenProps {
48
+ currentVersion: string;
49
+ /** Fetches the latest available release, or `null` if there is none. */
50
+ checkForUpdate: () => Promise<UpdateSettingsScreenRelease | null>;
51
+ /** Positive when `a` is newer than `b`, e.g. semver comparison. */
52
+ compareVersions: (a: string, b: string) => number;
53
+ downloadAndInstallApk: (apkUrl: string, onProgress?: (fraction: number) => void) => Promise<void>;
54
+ /** Omit to not show a version history section at all. */
55
+ fetchReleaseHistory?: () => Promise<UpdateSettingsScreenHistoryEntry[]>;
56
+ /** Locale for formatting each history entry's date. Defaults to `"fr-FR"`. */
57
+ dateLocale?: string;
58
+ labels?: UpdateSettingsScreenLabels;
59
+ styles?: UpdateSettingsScreenStyles;
60
+ }
61
+ export declare function UpdateSettingsScreen({ currentVersion, checkForUpdate, compareVersions, downloadAndInstallApk, fetchReleaseHistory, dateLocale, labels, styles, }: UpdateSettingsScreenProps): import("react").JSX.Element;
62
+ //# sourceMappingURL=UpdateSettingsScreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/UpdateSettingsScreen.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { ActivityIndicator, Pressable, Text, View } from "react-native";
4
+ import { ChangelogNotes } from "../update/ChangelogNotes.js";
5
+ const defaultLabels = {
6
+ installedVersionLabel: "Version installée",
7
+ checkButton: "Rechercher une mise à jour",
8
+ upToDate: "Tu as déjà la dernière version.",
9
+ checkError: "Impossible de vérifier les mises à jour.",
10
+ downloadError: "Le téléchargement a échoué.",
11
+ availableTitle: (version) => `Version ${version} disponible`,
12
+ installButton: () => "Télécharger et installer",
13
+ installButtonAccessibilityLabel: (version) => `Télécharger et installer la version ${version}`,
14
+ downloadingLabel: (percent) => `Téléchargement… ${String(percent)}%`,
15
+ downloadingHint: "Ton téléphone va ensuite te demander confirmation pour installer la mise à jour.",
16
+ historyTitle: "Historique des versions",
17
+ };
18
+ const defaultStyles = {
19
+ container: { gap: 4 },
20
+ infoBox: {
21
+ backgroundColor: "#ffffff",
22
+ borderWidth: 1,
23
+ borderColor: "#e5e7eb",
24
+ borderRadius: 10,
25
+ padding: 14,
26
+ marginBottom: 8,
27
+ },
28
+ infoLabel: { fontSize: 13, fontWeight: "600", color: "#6b7280" },
29
+ infoValue: {
30
+ fontSize: 18,
31
+ fontWeight: "700",
32
+ color: "#111827",
33
+ marginTop: 2,
34
+ },
35
+ helpText: { fontSize: 12, color: "#6b7280", marginTop: 8 },
36
+ errorText: { fontSize: 12, color: "#dc2626", marginTop: 8 },
37
+ button: {
38
+ backgroundColor: "#2563eb",
39
+ borderRadius: 10,
40
+ paddingVertical: 14,
41
+ alignItems: "center",
42
+ marginTop: 8,
43
+ },
44
+ buttonDisabled: { opacity: 0.6 },
45
+ buttonText: { color: "#ffffff", fontSize: 16, fontWeight: "700" },
46
+ activityIndicatorColor: "#ffffff",
47
+ updateAvailableBox: {
48
+ backgroundColor: "#ffffff",
49
+ borderWidth: 1,
50
+ borderColor: "#e5e7eb",
51
+ borderRadius: 10,
52
+ padding: 14,
53
+ marginTop: 12,
54
+ gap: 4,
55
+ },
56
+ updateAvailableTitle: { fontSize: 15, fontWeight: "700", color: "#111827" },
57
+ changelog: { marginTop: 28 },
58
+ changelogTitle: {
59
+ fontSize: 15,
60
+ fontWeight: "700",
61
+ color: "#111827",
62
+ marginBottom: 8,
63
+ },
64
+ changelogEntry: {
65
+ backgroundColor: "#ffffff",
66
+ borderWidth: 1,
67
+ borderColor: "#e5e7eb",
68
+ borderRadius: 10,
69
+ padding: 14,
70
+ marginBottom: 8,
71
+ },
72
+ changelogEntryHeader: {
73
+ flexDirection: "row",
74
+ justifyContent: "space-between",
75
+ alignItems: "center",
76
+ },
77
+ changelogVersion: { fontSize: 14, fontWeight: "700", color: "#111827" },
78
+ changelogDate: { fontSize: 12, color: "#6b7280" },
79
+ notes: undefined,
80
+ };
81
+ // Full "check for update" screen: installed version, a manual check button,
82
+ // the available-update box (with changelog + install button) or an
83
+ // up-to-date/error message, download progress, and — if
84
+ // `fetchReleaseHistory` is passed — a list of past releases. Checks
85
+ // automatically once on mount (the caller decided to navigate here, no need
86
+ // to make them tap "check" again).
87
+ export function UpdateSettingsScreen({ currentVersion, checkForUpdate, compareVersions, downloadAndInstallApk, fetchReleaseHistory, dateLocale = "fr-FR", labels, styles, }) {
88
+ const merged = {
89
+ container: [defaultStyles.container, styles?.container],
90
+ infoBox: [defaultStyles.infoBox, styles?.infoBox],
91
+ infoLabel: [defaultStyles.infoLabel, styles?.infoLabel],
92
+ infoValue: [defaultStyles.infoValue, styles?.infoValue],
93
+ helpText: [defaultStyles.helpText, styles?.helpText],
94
+ errorText: [defaultStyles.errorText, styles?.errorText],
95
+ button: [defaultStyles.button, styles?.button],
96
+ buttonDisabled: [defaultStyles.buttonDisabled, styles?.buttonDisabled],
97
+ buttonText: [defaultStyles.buttonText, styles?.buttonText],
98
+ updateAvailableBox: [
99
+ defaultStyles.updateAvailableBox,
100
+ styles?.updateAvailableBox,
101
+ ],
102
+ updateAvailableTitle: [
103
+ defaultStyles.updateAvailableTitle,
104
+ styles?.updateAvailableTitle,
105
+ ],
106
+ changelog: [defaultStyles.changelog, styles?.changelog],
107
+ changelogTitle: [defaultStyles.changelogTitle, styles?.changelogTitle],
108
+ changelogEntry: [defaultStyles.changelogEntry, styles?.changelogEntry],
109
+ changelogEntryHeader: [
110
+ defaultStyles.changelogEntryHeader,
111
+ styles?.changelogEntryHeader,
112
+ ],
113
+ changelogVersion: [
114
+ defaultStyles.changelogVersion,
115
+ styles?.changelogVersion,
116
+ ],
117
+ changelogDate: [defaultStyles.changelogDate, styles?.changelogDate],
118
+ notes: styles?.notes ?? defaultStyles.notes,
119
+ };
120
+ const activityIndicatorColor = styles?.activityIndicatorColor ?? defaultStyles.activityIndicatorColor;
121
+ const t = { ...defaultLabels, ...labels };
122
+ const [updateState, setUpdateState] = useState({
123
+ status: "idle",
124
+ });
125
+ const [releaseHistory, setReleaseHistory] = useState(null);
126
+ useEffect(() => {
127
+ if (!fetchReleaseHistory)
128
+ return;
129
+ fetchReleaseHistory()
130
+ .then(setReleaseHistory)
131
+ .catch(() => {
132
+ setReleaseHistory([]);
133
+ });
134
+ }, [fetchReleaseHistory]);
135
+ const handleCheckForUpdate = useCallback(async () => {
136
+ setUpdateState({ status: "checking" });
137
+ try {
138
+ const release = await checkForUpdate();
139
+ if (!release || compareVersions(release.version, currentVersion) <= 0) {
140
+ setUpdateState({ status: "up-to-date" });
141
+ return;
142
+ }
143
+ setUpdateState({ status: "available", release });
144
+ }
145
+ catch {
146
+ setUpdateState({ status: "error", message: t.checkError });
147
+ }
148
+ }, [checkForUpdate, compareVersions, currentVersion, t.checkError]);
149
+ useEffect(() => {
150
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- the caller navigated here specifically to check, so this fires a real check on mount rather than waiting for a manual tap; the actual state update happens after the async checkForUpdate() call, not synchronously.
151
+ void handleCheckForUpdate();
152
+ // eslint-disable-next-line react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps -- runs once on mount by design; handleCheckForUpdate is expected to be stable enough for the screen's lifetime.
153
+ }, []);
154
+ async function handleInstall(apkUrl) {
155
+ setUpdateState({ status: "downloading", progress: 0 });
156
+ try {
157
+ await downloadAndInstallApk(apkUrl, (progress) => {
158
+ setUpdateState({ status: "downloading", progress });
159
+ });
160
+ setUpdateState({ status: "idle" });
161
+ }
162
+ catch {
163
+ setUpdateState({ status: "error", message: t.downloadError });
164
+ }
165
+ }
166
+ const isBusy = updateState.status === "checking" || updateState.status === "downloading";
167
+ const notesStyles = useMemo(() => ({
168
+ heading: merged.updateAvailableTitle,
169
+ text: merged.helpText,
170
+ ...merged.notes,
171
+ }), [merged.helpText, merged.notes, merged.updateAvailableTitle]);
172
+ return (_jsxs(View, { style: merged.container, children: [_jsxs(View, { style: merged.infoBox, children: [_jsx(Text, { style: merged.infoLabel, children: t.installedVersionLabel }), _jsx(Text, { style: merged.infoValue, children: currentVersion })] }), updateState.status !== "available" &&
173
+ updateState.status !== "downloading" && (_jsx(Pressable, { style: [merged.button, isBusy && merged.buttonDisabled], onPress: () => {
174
+ void handleCheckForUpdate();
175
+ }, disabled: isBusy, accessibilityRole: "button", accessibilityLabel: t.checkButton, children: updateState.status === "checking" ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Text, { style: merged.buttonText, children: t.checkButton })) })), updateState.status === "up-to-date" && (_jsx(Text, { style: merged.helpText, children: t.upToDate })), updateState.status === "error" && (_jsx(Text, { style: merged.errorText, children: updateState.message })), updateState.status === "available" && (_jsxs(View, { style: merged.updateAvailableBox, children: [_jsx(Text, { style: merged.updateAvailableTitle, children: t.availableTitle(updateState.release.version) }), updateState.release.notes ? (_jsx(ChangelogNotes, { notes: updateState.release.notes, styles: merged.notes })) : null, _jsx(Pressable, { style: merged.button, onPress: () => {
176
+ void handleInstall(updateState.release.apkUrl);
177
+ }, accessibilityRole: "button", accessibilityLabel: t.installButtonAccessibilityLabel(updateState.release.version), children: _jsx(Text, { style: merged.buttonText, children: t.installButton(updateState.release.version) }) })] })), updateState.status === "downloading" && (_jsxs(View, { style: merged.updateAvailableBox, children: [_jsx(Text, { style: merged.helpText, children: t.downloadingLabel(Math.round(updateState.progress * 100)) }), _jsx(Text, { style: merged.helpText, children: t.downloadingHint })] })), releaseHistory && releaseHistory.length > 0 && (_jsxs(View, { style: merged.changelog, children: [_jsx(Text, { style: merged.changelogTitle, children: t.historyTitle }), releaseHistory.map((release) => (_jsxs(View, { style: merged.changelogEntry, children: [_jsxs(View, { style: merged.changelogEntryHeader, children: [_jsxs(Text, { style: merged.changelogVersion, children: ["v", release.version] }), release.publishedAt ? (_jsx(Text, { style: merged.changelogDate, children: new Date(release.publishedAt).toLocaleDateString(dateLocale) })) : null] }), release.notes ? (_jsx(ChangelogNotes, { notes: release.notes, styles: notesStyles })) : null] }, release.version)))] }))] }));
178
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./AboutSettingsScreen";
2
+ export * from "./BackupSettingsScreen";
3
+ export * from "./ContactSettingsScreen";
4
+ export * from "./PrivacySettingsScreen";
5
+ export * from "./SettingsMenu";
6
+ export * from "./ThemeSettingsScreen";
7
+ export * from "./UpdateSettingsScreen";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/settings/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from "./AboutSettingsScreen";
2
+ export * from "./BackupSettingsScreen";
3
+ export * from "./ContactSettingsScreen";
4
+ export * from "./PrivacySettingsScreen";
5
+ export * from "./SettingsMenu";
6
+ export * from "./ThemeSettingsScreen";
7
+ export * from "./UpdateSettingsScreen";
@@ -1,7 +1,7 @@
1
1
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
2
  import { Ionicons } from "@expo/vector-icons";
3
+ import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
3
4
  import type { ThemeToggleLabels } from "./ThemeToggle.js";
4
- import type { ThemePreference } from "./useEffectiveColorScheme.js";
5
5
  export interface ThemeOptionListIcons {
6
6
  light?: keyof typeof Ionicons.glyphMap;
7
7
  dark?: keyof typeof Ionicons.glyphMap;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeOptionList.d.ts","sourceRoot":"","sources":["../../../src/components/theme/ThemeOptionList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;CACzC;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AA+CD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,oBAAoB,+BAiDtB"}
@@ -1,5 +1,5 @@
1
1
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
- import type { ThemePreference } from "./useEffectiveColorScheme.js";
2
+ import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
3
3
  export interface ThemeToggleStyles {
4
4
  container?: StyleProp<ViewStyle>;
5
5
  option?: StyleProp<ViewStyle>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../../src/components/theme/ThemeToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AA2BD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,GACP,EAAE,gBAAgB,+BAyClB"}
@@ -0,0 +1,3 @@
1
+ export * from "./ThemeOptionList";
2
+ export * from "./ThemeToggle";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./ThemeOptionList";
2
+ export * from "./ThemeToggle";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChangelogNotes.d.ts","sourceRoot":"","sources":["../../../src/components/update/ChangelogNotes.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMpE,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AA0BD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,mBAAmB,+BA+CpE"}