@forthtilliath/react-native-kit 0.9.0 → 0.11.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.
- package/README.md +196 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/settings/AboutSettingsScreen.d.ts +24 -0
- package/dist/components/settings/AboutSettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/AboutSettingsScreen.js +41 -0
- package/dist/components/settings/BackupSettingsScreen.d.ts +52 -0
- package/dist/components/settings/BackupSettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/BackupSettingsScreen.js +120 -0
- package/dist/components/settings/ContactSettingsScreen.d.ts +19 -0
- package/dist/components/settings/ContactSettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/ContactSettingsScreen.js +39 -0
- package/dist/components/settings/PrivacySettingsScreen.d.ts +23 -0
- package/dist/components/settings/PrivacySettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/PrivacySettingsScreen.js +51 -0
- package/dist/components/settings/SettingsMenu.d.ts +55 -0
- package/dist/components/settings/SettingsMenu.d.ts.map +1 -0
- package/dist/components/settings/SettingsMenu.js +66 -0
- package/dist/components/settings/ThemeSettingsScreen.d.ts +51 -0
- package/dist/components/settings/ThemeSettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/ThemeSettingsScreen.js +86 -0
- package/dist/components/settings/UpdateSettingsScreen.d.ts +62 -0
- package/dist/components/settings/UpdateSettingsScreen.d.ts.map +1 -0
- package/dist/components/settings/UpdateSettingsScreen.js +178 -0
- package/dist/components/settings/index.d.ts +8 -0
- package/dist/components/settings/index.d.ts.map +1 -0
- package/dist/components/settings/index.js +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @forthtilliath/react-native-kit
|
|
2
2
|
|
|
3
|
-
Small React Native building blocks — components, hooks, and framework-agnostic utils — with no opinion on your app's theme, distributed one file per export (grouped under `components/{picker,theme,update,list}/`, `hooks/`, `utils/{format,helpers}/`) so consumers only pull in what they use.
|
|
3
|
+
Small React Native building blocks — components, hooks, and framework-agnostic utils — with no opinion on your app's theme, distributed one file per export (grouped under `components/{picker,theme,update,list,settings}/`, `hooks/`, `utils/{format,helpers}/`) so consumers only pull in what they use.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -294,6 +294,201 @@ import { UpdateAvailableBanner } from "@forthtilliath/react-native-kit/component
|
|
|
294
294
|
}
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
+
### Settings screens (`components/settings/`)
|
|
298
|
+
|
|
299
|
+
The building blocks of a typical app "Settings" tab — menu, theme, update check, backup, about, contact, privacy — factored out so each new app stops rewriting the same ~7 screens from scratch. Expo Router (or any file-based router) still needs one physical route file per screen in your app; these just give each route file almost nothing left to write beyond rendering the matching component. None of them add new peer dependencies beyond what's already listed above.
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
// app/(tabs)/settings/index.tsx
|
|
303
|
+
import { router } from "expo-router";
|
|
304
|
+
import { SettingsMenu } from "@forthtilliath/react-native-kit/components/settings/SettingsMenu";
|
|
305
|
+
|
|
306
|
+
export default function SettingsMenuScreen() {
|
|
307
|
+
return (
|
|
308
|
+
<SettingsMenu
|
|
309
|
+
items={[
|
|
310
|
+
// `key` doubles as the lookup into a built-in default icon/label
|
|
311
|
+
// pairing for the 6 sections above — no `emoji`/`icon` needed here.
|
|
312
|
+
{
|
|
313
|
+
key: "backup",
|
|
314
|
+
title: "Sauvegarde",
|
|
315
|
+
hint: "Exporter / importer tes données",
|
|
316
|
+
onPress: () => router.push("/settings/backup"),
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
key: "theme",
|
|
320
|
+
title: "Thème",
|
|
321
|
+
hint: "Clair, sombre, ou automatique",
|
|
322
|
+
onPress: () => router.push("/settings/theme"),
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
key: "update",
|
|
326
|
+
title: "Mise à jour",
|
|
327
|
+
onPress: () => router.push("/settings/update"),
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
key: "about",
|
|
331
|
+
title: "À propos",
|
|
332
|
+
onPress: () => router.push("/settings/about"),
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
key: "contact",
|
|
336
|
+
title: "Contact",
|
|
337
|
+
onPress: () => router.push("/settings/contact"),
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
key: "privacy",
|
|
341
|
+
title: "Confidentialité",
|
|
342
|
+
onPress: () => router.push("/settings/privacy"),
|
|
343
|
+
},
|
|
344
|
+
// An app-specific section: no default exists for an arbitrary key,
|
|
345
|
+
// so `emoji`/`icon` (and `hint`, if you want one) are yours to set.
|
|
346
|
+
{
|
|
347
|
+
key: "profile",
|
|
348
|
+
emoji: "🙋",
|
|
349
|
+
title: "Mon nom",
|
|
350
|
+
onPress: () => router.push("/settings/profile"),
|
|
351
|
+
},
|
|
352
|
+
]}
|
|
353
|
+
/>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
`SettingsMenu` doesn't depend on expo-router (or any other navigator) itself — each item carries its own `onPress`, so it works the same with any navigation setup.
|
|
359
|
+
|
|
360
|
+
Two independent style knobs, both usable on their own:
|
|
361
|
+
|
|
362
|
+
- **Icon flavor** — each item can set its own `emoji` (naturally multicolor) or `icon` (an `Ionicons` glyph, name in `styles.iconColor`) to override its default; `defaultIconKind` (`"emoji"` by default, or `"icon"`) picks which flavor of the _default_ table applies to items that don't set either.
|
|
363
|
+
- **Hints** — set `showHints={false}` to hide every item's `hint` at once, for a denser, icon-only-style menu.
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
<SettingsMenu
|
|
367
|
+
items={[
|
|
368
|
+
{
|
|
369
|
+
key: "backup",
|
|
370
|
+
title: "Sauvegarde",
|
|
371
|
+
onPress: () => router.push("/settings/backup"),
|
|
372
|
+
},
|
|
373
|
+
]}
|
|
374
|
+
defaultIconKind="icon"
|
|
375
|
+
showHints={false}
|
|
376
|
+
styles={{
|
|
377
|
+
row: { backgroundColor: colors.primary, borderWidth: 0 },
|
|
378
|
+
iconColor: "#fff",
|
|
379
|
+
title: { color: "#fff" },
|
|
380
|
+
}}
|
|
381
|
+
/>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### `<ThemeSettingsScreen value onChange />`
|
|
385
|
+
|
|
386
|
+
Same light/dark/system data contract as `ThemeToggle`/`ThemeOptionList`. Two visuals, picked via `variant`: `"emoji"` (default) — emoji + label + a plain-text `✓` on the active row — or `"icon"` — `Ionicons` glyphs in one shared color (`styles.iconColor`/`iconColorActive`) + an `Ionicons` checkmark, matching `SettingsMenu`'s `defaultIconKind="icon"` look. `showHint={false}` hides the hint above the list.
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import { ThemeSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/ThemeSettingsScreen";
|
|
390
|
+
|
|
391
|
+
<ThemeSettingsScreen value={themePreference} onChange={setThemePreference} />;
|
|
392
|
+
|
|
393
|
+
// Or, matching an icon-flavored SettingsMenu:
|
|
394
|
+
<ThemeSettingsScreen
|
|
395
|
+
value={themePreference}
|
|
396
|
+
onChange={setThemePreference}
|
|
397
|
+
variant="icon"
|
|
398
|
+
/>;
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
#### `<UpdateSettingsScreen currentVersion checkForUpdate compareVersions downloadAndInstallApk />`
|
|
402
|
+
|
|
403
|
+
The full "check for update" screen: installed version, a manual check button, the available-update box (changelog + install button) or an up-to-date/error message, download progress, and — if `fetchReleaseHistory` is passed — a list of past releases. Checks automatically once on mount. Pairs naturally with `@forthtilliath/expo-release-updates` and `useUpdateCheck`/`UpdateAvailableBanner` above (same `checkForUpdate`/`compareVersions` you already pass those), but takes plain functions so it isn't tied to that package's exact shape.
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
import { UpdateSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/UpdateSettingsScreen";
|
|
407
|
+
|
|
408
|
+
import {
|
|
409
|
+
compareVersions,
|
|
410
|
+
downloadAndInstallApk,
|
|
411
|
+
fetchLatestRelease,
|
|
412
|
+
fetchReleaseHistory,
|
|
413
|
+
} from "@/lib/appUpdate";
|
|
414
|
+
|
|
415
|
+
<UpdateSettingsScreen
|
|
416
|
+
currentVersion={Constants.expoConfig?.version ?? "?"}
|
|
417
|
+
checkForUpdate={fetchLatestRelease}
|
|
418
|
+
compareVersions={compareVersions}
|
|
419
|
+
downloadAndInstallApk={downloadAndInstallApk}
|
|
420
|
+
fetchReleaseHistory={fetchReleaseHistory}
|
|
421
|
+
/>;
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
#### `<BackupSettingsScreen onExport onImport reminder? />`
|
|
425
|
+
|
|
426
|
+
Export/import buttons plus an optional reminder-notification switch. Deliberately just a shell: what actually gets exported/imported is entirely app-specific (your own database tables), so `onExport`/`onImport` are required — this package can't have a sensible default for "how do I export my schema". Import asks for confirmation first (via `confirmDestructive`, from `utils/helpers/`) since it replaces existing data; `onImport` itself doesn't need to.
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
import { BackupSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/BackupSettingsScreen";
|
|
430
|
+
|
|
431
|
+
<BackupSettingsScreen
|
|
432
|
+
onExport={shareBackup}
|
|
433
|
+
onImport={pickAndImportBackup}
|
|
434
|
+
reminder={{
|
|
435
|
+
enabled: reminderEnabled,
|
|
436
|
+
onToggle: handleToggleReminder,
|
|
437
|
+
intervalDays: BACKUP_REMINDER_INTERVAL_DAYS,
|
|
438
|
+
}}
|
|
439
|
+
/>;
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Omit `reminder` entirely for an app with no backup-reminder notifications.
|
|
443
|
+
|
|
444
|
+
#### `<AboutSettingsScreen appName version description developerName />`
|
|
445
|
+
|
|
446
|
+
App name, version, one or more description paragraphs, and a "developed by" credit. `appName`/`version` are required rather than defaulted internally (keeps this package framework-agnostic) — pass `Constants.expoConfig?.name`/`.version` from the caller.
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
import { AboutSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/AboutSettingsScreen";
|
|
450
|
+
|
|
451
|
+
<AboutSettingsScreen
|
|
452
|
+
appName={Constants.expoConfig?.name ?? "My App"}
|
|
453
|
+
version={Constants.expoConfig?.version ?? "?"}
|
|
454
|
+
description="A personal tool to do the thing."
|
|
455
|
+
developerName="Your Name"
|
|
456
|
+
/>;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### `<ContactSettingsScreen email />`
|
|
460
|
+
|
|
461
|
+
A `mailto:` button plus a short "how to report a bug" hint.
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
import { ContactSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/ContactSettingsScreen";
|
|
465
|
+
|
|
466
|
+
<ContactSettingsScreen email="you@example.com" />;
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
#### `<PrivacySettingsScreen sections? />`
|
|
470
|
+
|
|
471
|
+
Ships with the standard "everything stays on this device" three sections (no data collected, local storage only, sharing is opt-in) as the default — the right fit for most of these small personal-data apps. Pass your own `sections` to fully replace it for an app with different data handling (e.g. one that syncs to a server).
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
import { PrivacySettingsScreen } from "@forthtilliath/react-native-kit/components/settings/PrivacySettingsScreen";
|
|
475
|
+
|
|
476
|
+
// Zero-config, uses the default copy:
|
|
477
|
+
<PrivacySettingsScreen />;
|
|
478
|
+
|
|
479
|
+
// Or fully custom:
|
|
480
|
+
<PrivacySettingsScreen
|
|
481
|
+
sections={[
|
|
482
|
+
{
|
|
483
|
+
title: "What we collect",
|
|
484
|
+
paragraphs: ["Only what you enter, synced to your account."],
|
|
485
|
+
},
|
|
486
|
+
]}
|
|
487
|
+
/>;
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
For all 7 components above, styling works the same way as `ChangelogNotes`: an optional `styles` prop (all fields optional, neutral defaults), and where relevant an optional `labels` prop for the built-in French copy.
|
|
491
|
+
|
|
297
492
|
### Utils (`utils/`)
|
|
298
493
|
|
|
299
494
|
Framework-agnostic pure functions — no React or React Native import, usable from Node/web too.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
package/dist/components/index.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
export interface AboutSettingsScreenStyles {
|
|
3
|
+
container?: StyleProp<ViewStyle>;
|
|
4
|
+
appName?: StyleProp<TextStyle>;
|
|
5
|
+
version?: StyleProp<TextStyle>;
|
|
6
|
+
separator?: StyleProp<ViewStyle>;
|
|
7
|
+
paragraph?: StyleProp<TextStyle>;
|
|
8
|
+
hint?: StyleProp<TextStyle>;
|
|
9
|
+
}
|
|
10
|
+
export interface AboutSettingsScreenLabels {
|
|
11
|
+
version?: (version: string) => string;
|
|
12
|
+
developedBy?: (name: string) => string;
|
|
13
|
+
}
|
|
14
|
+
export interface AboutSettingsScreenProps {
|
|
15
|
+
appName: string;
|
|
16
|
+
version: string;
|
|
17
|
+
/** One or more paragraphs describing the app, rendered in order. */
|
|
18
|
+
description: string | string[];
|
|
19
|
+
developerName: string;
|
|
20
|
+
labels?: AboutSettingsScreenLabels;
|
|
21
|
+
styles?: AboutSettingsScreenStyles;
|
|
22
|
+
}
|
|
23
|
+
export declare function AboutSettingsScreen({ appName, version, description, developerName, labels, styles, }: AboutSettingsScreenProps): import("react").JSX.Element;
|
|
24
|
+
//# sourceMappingURL=AboutSettingsScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AboutSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AboutSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AA8BD,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,WAAW,EACX,aAAa,EACb,MAAM,EACN,MAAM,GACP,EAAE,wBAAwB,+BA8B1B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text, View } from "react-native";
|
|
3
|
+
const defaultLabels = {
|
|
4
|
+
version: (version) => `Version ${version}`,
|
|
5
|
+
developedBy: (name) => `Développée par ${name}.`,
|
|
6
|
+
};
|
|
7
|
+
const defaultStyles = {
|
|
8
|
+
container: {},
|
|
9
|
+
appName: { fontSize: 22, fontWeight: "700", color: "#111827" },
|
|
10
|
+
version: { fontSize: 13, color: "#6b7280", marginTop: 2 },
|
|
11
|
+
separator: {
|
|
12
|
+
height: 1,
|
|
13
|
+
marginVertical: 16,
|
|
14
|
+
backgroundColor: "#e5e7eb",
|
|
15
|
+
},
|
|
16
|
+
paragraph: {
|
|
17
|
+
fontSize: 14,
|
|
18
|
+
lineHeight: 20,
|
|
19
|
+
color: "#111827",
|
|
20
|
+
marginBottom: 12,
|
|
21
|
+
},
|
|
22
|
+
hint: { fontSize: 13, color: "#6b7280" },
|
|
23
|
+
};
|
|
24
|
+
// App name, version, a short description, and a "developed by" credit — the
|
|
25
|
+
// standard "About" section of a settings tab. `appName`/`version` are
|
|
26
|
+
// required rather than defaulted from `expo-constants` so this stays
|
|
27
|
+
// framework-agnostic; pass `Constants.expoConfig?.name`/`.version` from the
|
|
28
|
+
// caller.
|
|
29
|
+
export function AboutSettingsScreen({ appName, version, description, developerName, labels, styles, }) {
|
|
30
|
+
const merged = {
|
|
31
|
+
container: [defaultStyles.container, styles?.container],
|
|
32
|
+
appName: [defaultStyles.appName, styles?.appName],
|
|
33
|
+
version: [defaultStyles.version, styles?.version],
|
|
34
|
+
separator: [defaultStyles.separator, styles?.separator],
|
|
35
|
+
paragraph: [defaultStyles.paragraph, styles?.paragraph],
|
|
36
|
+
hint: [defaultStyles.hint, styles?.hint],
|
|
37
|
+
};
|
|
38
|
+
const t = { ...defaultLabels, ...labels };
|
|
39
|
+
const paragraphs = Array.isArray(description) ? description : [description];
|
|
40
|
+
return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.appName, children: appName }), _jsx(Text, { style: merged.version, children: t.version(version) }), _jsx(View, { style: merged.separator }), paragraphs.map((paragraph) => (_jsx(Text, { style: merged.paragraph, children: paragraph }, paragraph))), _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.developedBy(developerName) })] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
export interface BackupSettingsScreenReminder {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
/** Set while a permission request or the toggle's own persistence is in flight. */
|
|
5
|
+
busy?: boolean;
|
|
6
|
+
onToggle: (value: boolean) => void;
|
|
7
|
+
/** How often the reminder fires, only used to word the hint (e.g. "every 7 days"). */
|
|
8
|
+
intervalDays: number;
|
|
9
|
+
}
|
|
10
|
+
export interface BackupSettingsScreenStyles {
|
|
11
|
+
container?: StyleProp<ViewStyle>;
|
|
12
|
+
hint?: StyleProp<TextStyle>;
|
|
13
|
+
row?: StyleProp<ViewStyle>;
|
|
14
|
+
button?: StyleProp<ViewStyle>;
|
|
15
|
+
buttonDisabled?: StyleProp<ViewStyle>;
|
|
16
|
+
buttonText?: StyleProp<TextStyle>;
|
|
17
|
+
reminderRow?: StyleProp<ViewStyle>;
|
|
18
|
+
reminderTextColumn?: StyleProp<ViewStyle>;
|
|
19
|
+
reminderLabel?: StyleProp<TextStyle>;
|
|
20
|
+
reminderHint?: StyleProp<TextStyle>;
|
|
21
|
+
activityIndicatorColor?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface BackupSettingsScreenLabels {
|
|
24
|
+
hint?: string;
|
|
25
|
+
exportButton?: string;
|
|
26
|
+
importButton?: string;
|
|
27
|
+
importConfirmTitle?: string;
|
|
28
|
+
importConfirmMessage?: string;
|
|
29
|
+
importConfirmLabel?: string;
|
|
30
|
+
importConfirmCancelLabel?: string;
|
|
31
|
+
reminderLabel?: string;
|
|
32
|
+
reminderHint?: (intervalDays: number) => string;
|
|
33
|
+
exportErrorTitle?: string;
|
|
34
|
+
importErrorTitle?: string;
|
|
35
|
+
genericErrorMessage?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BackupSettingsScreenProps {
|
|
38
|
+
/** Exports/shares the app's data however it sees fit (e.g. a JSON file via the native share sheet). */
|
|
39
|
+
onExport: () => Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Imports data from a file the user picks, replacing what's currently
|
|
42
|
+
* stored. Only called after the user confirms the destructive-replace
|
|
43
|
+
* warning — this doesn't need to show its own confirmation.
|
|
44
|
+
*/
|
|
45
|
+
onImport: () => Promise<void>;
|
|
46
|
+
/** Omit entirely to not show a reminder toggle at all. */
|
|
47
|
+
reminder?: BackupSettingsScreenReminder;
|
|
48
|
+
labels?: BackupSettingsScreenLabels;
|
|
49
|
+
styles?: BackupSettingsScreenStyles;
|
|
50
|
+
}
|
|
51
|
+
export declare function BackupSettingsScreen({ onExport, onImport, reminder, labels, styles, }: BackupSettingsScreenProps): import("react").JSX.Element;
|
|
52
|
+
//# sourceMappingURL=BackupSettingsScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BackupSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/BackupSettingsScreen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAYpE,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,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,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,uGAAuG;IACvG,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,4BAA4B,CAAC;IACxC,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,MAAM,CAAC,EAAE,0BAA0B,CAAC;CACrC;AAiED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,GACP,EAAE,yBAAyB,+BA8H3B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { ActivityIndicator, Alert, Pressable, Switch, Text, View, } from "react-native";
|
|
4
|
+
import { confirmDestructive } from "../../utils/helpers/confirmDestructive.js";
|
|
5
|
+
const defaultLabels = {
|
|
6
|
+
hint: "Les données sont stockées uniquement sur cet appareil et sont perdues en cas de réinstallation ou de mise à jour incompatible. Exporte-les régulièrement pour pouvoir les restaurer.",
|
|
7
|
+
exportButton: "⬆️ Exporter",
|
|
8
|
+
importButton: "⬇️ Importer",
|
|
9
|
+
importConfirmTitle: "Importer une sauvegarde ?",
|
|
10
|
+
importConfirmMessage: "Toutes les données actuelles seront remplacées par celles du fichier choisi.",
|
|
11
|
+
importConfirmLabel: "Choisir un fichier",
|
|
12
|
+
importConfirmCancelLabel: "Annuler",
|
|
13
|
+
reminderLabel: "🔔 Rappel de sauvegarde",
|
|
14
|
+
reminderHint: undefined,
|
|
15
|
+
exportErrorTitle: "Échec de l'export",
|
|
16
|
+
importErrorTitle: "Échec de l'import",
|
|
17
|
+
genericErrorMessage: "Une erreur inconnue s'est produite.",
|
|
18
|
+
};
|
|
19
|
+
const defaultStyles = {
|
|
20
|
+
container: {},
|
|
21
|
+
hint: { fontSize: 13, color: "#6b7280", marginBottom: 16 },
|
|
22
|
+
row: { flexDirection: "row", gap: 10 },
|
|
23
|
+
button: {
|
|
24
|
+
flex: 1,
|
|
25
|
+
paddingVertical: 12,
|
|
26
|
+
borderRadius: 8,
|
|
27
|
+
borderWidth: 1,
|
|
28
|
+
borderColor: "#2563eb",
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
justifyContent: "center",
|
|
31
|
+
},
|
|
32
|
+
buttonDisabled: { opacity: 0.5 },
|
|
33
|
+
buttonText: { color: "#2563eb", fontWeight: "700" },
|
|
34
|
+
reminderRow: {
|
|
35
|
+
flexDirection: "row",
|
|
36
|
+
alignItems: "center",
|
|
37
|
+
gap: 12,
|
|
38
|
+
marginTop: 24,
|
|
39
|
+
},
|
|
40
|
+
reminderTextColumn: { flex: 1 },
|
|
41
|
+
reminderLabel: {
|
|
42
|
+
fontSize: 15,
|
|
43
|
+
fontWeight: "600",
|
|
44
|
+
color: "#111827",
|
|
45
|
+
marginBottom: 4,
|
|
46
|
+
},
|
|
47
|
+
reminderHint: { fontSize: 13, color: "#6b7280" },
|
|
48
|
+
activityIndicatorColor: undefined,
|
|
49
|
+
};
|
|
50
|
+
function errorMessage(err, fallback) {
|
|
51
|
+
return err instanceof Error ? err.message : fallback;
|
|
52
|
+
}
|
|
53
|
+
// Export/import buttons plus an optional reminder-notification toggle — the
|
|
54
|
+
// standard "Backup" section of a settings tab. Deliberately just a shell:
|
|
55
|
+
// what gets exported/imported is entirely app-specific (its own database
|
|
56
|
+
// tables), so `onExport`/`onImport` are required props, not something this
|
|
57
|
+
// package could have a sensible default for.
|
|
58
|
+
export function BackupSettingsScreen({ onExport, onImport, reminder, labels, styles, }) {
|
|
59
|
+
const [exporting, setExporting] = useState(false);
|
|
60
|
+
const [importing, setImporting] = useState(false);
|
|
61
|
+
const merged = {
|
|
62
|
+
container: [defaultStyles.container, styles?.container],
|
|
63
|
+
hint: [defaultStyles.hint, styles?.hint],
|
|
64
|
+
row: [defaultStyles.row, styles?.row],
|
|
65
|
+
button: [defaultStyles.button, styles?.button],
|
|
66
|
+
buttonDisabled: [defaultStyles.buttonDisabled, styles?.buttonDisabled],
|
|
67
|
+
buttonText: [defaultStyles.buttonText, styles?.buttonText],
|
|
68
|
+
reminderRow: [defaultStyles.reminderRow, styles?.reminderRow],
|
|
69
|
+
reminderTextColumn: [
|
|
70
|
+
defaultStyles.reminderTextColumn,
|
|
71
|
+
styles?.reminderTextColumn,
|
|
72
|
+
],
|
|
73
|
+
reminderLabel: [defaultStyles.reminderLabel, styles?.reminderLabel],
|
|
74
|
+
reminderHint: [defaultStyles.reminderHint, styles?.reminderHint],
|
|
75
|
+
};
|
|
76
|
+
const t = { ...defaultLabels, ...labels };
|
|
77
|
+
const activityIndicatorColor = styles?.activityIndicatorColor ?? defaultStyles.activityIndicatorColor;
|
|
78
|
+
async function handleExport() {
|
|
79
|
+
if (exporting)
|
|
80
|
+
return;
|
|
81
|
+
setExporting(true);
|
|
82
|
+
try {
|
|
83
|
+
await onExport();
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
Alert.alert(t.exportErrorTitle, errorMessage(err, t.genericErrorMessage));
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
setExporting(false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function handleImportPress() {
|
|
93
|
+
if (importing)
|
|
94
|
+
return;
|
|
95
|
+
confirmDestructive(t.importConfirmTitle, () => {
|
|
96
|
+
void (async () => {
|
|
97
|
+
setImporting(true);
|
|
98
|
+
try {
|
|
99
|
+
await onImport();
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
Alert.alert(t.importErrorTitle, errorMessage(err, t.genericErrorMessage));
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
setImporting(false);
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
}, {
|
|
109
|
+
message: t.importConfirmMessage,
|
|
110
|
+
cancelLabel: t.importConfirmCancelLabel,
|
|
111
|
+
confirmLabel: t.importConfirmLabel,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsxs(View, { style: merged.row, children: [_jsx(Pressable, { style: [merged.button, exporting && merged.buttonDisabled], disabled: exporting, onPress: () => {
|
|
115
|
+
void handleExport();
|
|
116
|
+
}, accessibilityRole: "button", accessibilityLabel: t.exportButton, children: exporting ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Text, { style: merged.buttonText, children: t.exportButton })) }), _jsx(Pressable, { style: [merged.button, importing && merged.buttonDisabled], disabled: importing, onPress: handleImportPress, accessibilityRole: "button", accessibilityLabel: t.importButton, children: importing ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Text, { style: merged.buttonText, children: t.importButton })) })] }), reminder && (_jsxs(Pressable, { style: merged.reminderRow, disabled: reminder.busy, onPress: () => {
|
|
117
|
+
reminder.onToggle(!reminder.enabled);
|
|
118
|
+
}, accessibilityRole: "switch", accessibilityState: { checked: reminder.enabled }, accessibilityLabel: t.reminderLabel, children: [_jsxs(View, { style: merged.reminderTextColumn, children: [_jsx(Text, { style: merged.reminderLabel, children: t.reminderLabel }), _jsx(Text, { style: merged.reminderHint, children: t.reminderHint?.(reminder.intervalDays) ??
|
|
119
|
+
`Une notification tous les ${String(reminder.intervalDays)} jours pour penser à exporter.` })] }), reminder.busy ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Switch, { value: reminder.enabled, onValueChange: reminder.onToggle }))] }))] }));
|
|
120
|
+
}
|
|
@@ -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,55 @@
|
|
|
1
|
+
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
3
|
+
/**
|
|
4
|
+
* Well-known settings sections this package itself ships a screen for
|
|
5
|
+
* (`BackupSettingsScreen`, `ThemeSettingsScreen`, etc.) — matching `key`
|
|
6
|
+
* against one of these gets an item a default icon for free. Any other
|
|
7
|
+
* `key` (e.g. an app-specific "profile"/"notifications" section) needs its
|
|
8
|
+
* own `emoji`/`icon`, since there's no sensible universal default for it.
|
|
9
|
+
*/
|
|
10
|
+
export type SettingsMenuSection = "backup" | "theme" | "update" | "about" | "contact" | "privacy";
|
|
11
|
+
export interface SettingsMenuItem {
|
|
12
|
+
/**
|
|
13
|
+
* Stable React key. Doubles as the lookup into the default icon table
|
|
14
|
+
* above — use `"backup"`/`"theme"`/`"update"`/`"about"`/`"contact"`/
|
|
15
|
+
* `"privacy"` to get a sensible icon for free, any other string for a
|
|
16
|
+
* custom (app-specific) section, which then needs its own `emoji`/`icon`.
|
|
17
|
+
*/
|
|
18
|
+
key: string;
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the default icon for this item (whether that default came
|
|
21
|
+
* from `key` or there simply isn't one). At most one of the two —
|
|
22
|
+
* `emoji` renders as-is (naturally multicolor, varies per item); `icon`
|
|
23
|
+
* renders as an `Ionicons` glyph in one shared color (`styles.iconColor`),
|
|
24
|
+
* for a flatter, more uniform look.
|
|
25
|
+
*/
|
|
26
|
+
emoji?: string;
|
|
27
|
+
icon?: keyof typeof Ionicons.glyphMap;
|
|
28
|
+
title: string;
|
|
29
|
+
hint?: string;
|
|
30
|
+
onPress: () => void;
|
|
31
|
+
}
|
|
32
|
+
export interface SettingsMenuStyles {
|
|
33
|
+
container?: StyleProp<ViewStyle>;
|
|
34
|
+
row?: StyleProp<ViewStyle>;
|
|
35
|
+
titleColumn?: StyleProp<ViewStyle>;
|
|
36
|
+
emoji?: StyleProp<TextStyle>;
|
|
37
|
+
iconColor?: string;
|
|
38
|
+
title?: StyleProp<TextStyle>;
|
|
39
|
+
hint?: StyleProp<TextStyle>;
|
|
40
|
+
chevronColor?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SettingsMenuProps {
|
|
43
|
+
items: SettingsMenuItem[];
|
|
44
|
+
/**
|
|
45
|
+
* Which flavor of the default icon table to use for items that don't set
|
|
46
|
+
* their own `emoji`/`icon`. Doesn't affect items that do. Defaults to
|
|
47
|
+
* `"emoji"`.
|
|
48
|
+
*/
|
|
49
|
+
defaultIconKind?: "emoji" | "icon";
|
|
50
|
+
/** Set to `false` to never render any item's `hint`, for a denser, icon-only-style menu. Defaults to `true`. */
|
|
51
|
+
showHints?: boolean;
|
|
52
|
+
styles?: SettingsMenuStyles;
|
|
53
|
+
}
|
|
54
|
+
export declare function SettingsMenu({ items, defaultIconKind, showHints, styles, }: SettingsMenuProps): import("react").JSX.Element;
|
|
55
|
+
//# 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;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC7B,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAclE,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACtC,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,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,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;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,gHAAgH;IAChH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AA4CD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,eAAyB,EACzB,SAAgB,EAChB,MAAM,GACP,EAAE,iBAAiB,+BAyCnB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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 defaultSectionIcons = {
|
|
5
|
+
backup: { emoji: "💾", icon: "save-outline" },
|
|
6
|
+
theme: { emoji: "🎨", icon: "contrast-outline" },
|
|
7
|
+
update: { emoji: "⬇️", icon: "download-outline" },
|
|
8
|
+
about: { emoji: "ℹ️", icon: "information-circle-outline" },
|
|
9
|
+
contact: { emoji: "✉️", icon: "chatbubble-ellipses-outline" },
|
|
10
|
+
privacy: { emoji: "🔒", icon: "lock-closed-outline" },
|
|
11
|
+
};
|
|
12
|
+
const defaultStyles = {
|
|
13
|
+
container: { gap: 10 },
|
|
14
|
+
row: {
|
|
15
|
+
flexDirection: "row",
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
gap: 12,
|
|
18
|
+
borderWidth: 1,
|
|
19
|
+
borderColor: "#e5e7eb",
|
|
20
|
+
borderRadius: 10,
|
|
21
|
+
padding: 14,
|
|
22
|
+
},
|
|
23
|
+
titleColumn: { flex: 1 },
|
|
24
|
+
emoji: { fontSize: 24 },
|
|
25
|
+
iconColor: "#111827",
|
|
26
|
+
title: {
|
|
27
|
+
fontSize: 16,
|
|
28
|
+
fontWeight: "600",
|
|
29
|
+
color: "#111827",
|
|
30
|
+
},
|
|
31
|
+
hint: { fontSize: 12, color: "#6b7280", marginTop: 2 },
|
|
32
|
+
chevronColor: "#9ca3af",
|
|
33
|
+
};
|
|
34
|
+
function resolveIcon(item, defaultIconKind) {
|
|
35
|
+
if (item.icon)
|
|
36
|
+
return { icon: item.icon, emoji: undefined };
|
|
37
|
+
if (item.emoji)
|
|
38
|
+
return { icon: undefined, emoji: item.emoji };
|
|
39
|
+
const fallback = defaultSectionIcons[item.key];
|
|
40
|
+
if (!fallback)
|
|
41
|
+
return { icon: undefined, emoji: undefined };
|
|
42
|
+
return defaultIconKind === "icon"
|
|
43
|
+
? { icon: fallback.icon, emoji: undefined }
|
|
44
|
+
: { icon: undefined, emoji: fallback.emoji };
|
|
45
|
+
}
|
|
46
|
+
// Menu screen for a settings tab: one row per section (icon + title +
|
|
47
|
+
// optional hint), navigating to it on press. Has no opinion on how
|
|
48
|
+
// navigation works — each item carries its own `onPress` (e.g.
|
|
49
|
+
// `() => router.push("/settings/xxx")`), so this doesn't depend on
|
|
50
|
+
// expo-router or any other specific navigator.
|
|
51
|
+
export function SettingsMenu({ items, defaultIconKind = "emoji", showHints = true, styles, }) {
|
|
52
|
+
const merged = {
|
|
53
|
+
container: [defaultStyles.container, styles?.container],
|
|
54
|
+
row: [defaultStyles.row, styles?.row],
|
|
55
|
+
titleColumn: [defaultStyles.titleColumn, styles?.titleColumn],
|
|
56
|
+
emoji: [defaultStyles.emoji, styles?.emoji],
|
|
57
|
+
title: [defaultStyles.title, styles?.title],
|
|
58
|
+
hint: [defaultStyles.hint, styles?.hint],
|
|
59
|
+
};
|
|
60
|
+
const chevronColor = styles?.chevronColor ?? defaultStyles.chevronColor;
|
|
61
|
+
const iconColor = styles?.iconColor ?? defaultStyles.iconColor;
|
|
62
|
+
return (_jsx(View, { style: merged.container, children: items.map((item) => {
|
|
63
|
+
const { icon, emoji } = resolveIcon(item, defaultIconKind);
|
|
64
|
+
return (_jsxs(Pressable, { style: merged.row, onPress: item.onPress, accessibilityRole: "button", accessibilityLabel: item.title, children: [icon ? (_jsx(Ionicons, { name: icon, size: 24, color: iconColor })) : (_jsx(Text, { style: merged.emoji, children: emoji })), _jsxs(View, { style: merged.titleColumn, children: [_jsx(Text, { style: merged.title, children: item.title }), showHints && item.hint ? (_jsx(Text, { style: merged.hint, children: item.hint })) : null] }), _jsx(Ionicons, { name: "chevron-forward", size: 20, color: chevronColor })] }, item.key));
|
|
65
|
+
}) }));
|
|
66
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
3
|
+
import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
|
|
4
|
+
export interface ThemeSettingsScreenEmojis {
|
|
5
|
+
light?: string;
|
|
6
|
+
dark?: string;
|
|
7
|
+
system?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ThemeSettingsScreenIcons {
|
|
10
|
+
light?: keyof typeof Ionicons.glyphMap;
|
|
11
|
+
dark?: keyof typeof Ionicons.glyphMap;
|
|
12
|
+
system?: keyof typeof Ionicons.glyphMap;
|
|
13
|
+
}
|
|
14
|
+
export interface ThemeSettingsScreenLabels {
|
|
15
|
+
light?: string;
|
|
16
|
+
dark?: string;
|
|
17
|
+
system?: string;
|
|
18
|
+
hint?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ThemeSettingsScreenStyles {
|
|
21
|
+
container?: StyleProp<ViewStyle>;
|
|
22
|
+
hint?: StyleProp<TextStyle>;
|
|
23
|
+
row?: StyleProp<ViewStyle>;
|
|
24
|
+
rowActive?: StyleProp<ViewStyle>;
|
|
25
|
+
emoji?: StyleProp<TextStyle>;
|
|
26
|
+
iconColor?: string;
|
|
27
|
+
iconColorActive?: string;
|
|
28
|
+
label?: StyleProp<TextStyle>;
|
|
29
|
+
labelActive?: StyleProp<TextStyle>;
|
|
30
|
+
check?: StyleProp<TextStyle>;
|
|
31
|
+
checkColor?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ThemeSettingsScreenProps {
|
|
34
|
+
value: ThemePreference;
|
|
35
|
+
onChange: (preference: ThemePreference) => void;
|
|
36
|
+
/**
|
|
37
|
+
* `"emoji"` (default): colorful emoji + a plain-text `✓`. `"icon"`:
|
|
38
|
+
* `Ionicons` glyphs in one shared color (`styles.iconColor`) + an
|
|
39
|
+
* `Ionicons` checkmark, for a flatter, more uniform look — matches
|
|
40
|
+
* `SettingsMenu`'s `defaultIconKind`.
|
|
41
|
+
*/
|
|
42
|
+
variant?: "emoji" | "icon";
|
|
43
|
+
emojis?: ThemeSettingsScreenEmojis;
|
|
44
|
+
icons?: ThemeSettingsScreenIcons;
|
|
45
|
+
labels?: ThemeSettingsScreenLabels;
|
|
46
|
+
/** Set to `false` to hide the hint above the list. Defaults to `true`. */
|
|
47
|
+
showHint?: boolean;
|
|
48
|
+
styles?: ThemeSettingsScreenStyles;
|
|
49
|
+
}
|
|
50
|
+
export declare function ThemeSettingsScreen({ value, onChange, variant, emojis, icons, labels, showHint, styles, }: ThemeSettingsScreenProps): import("react").JSX.Element;
|
|
51
|
+
//# 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;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,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,wBAAwB;IACvC,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,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,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,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;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AA8DD,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,OAAiB,EACjB,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAe,EACf,MAAM,GACP,EAAE,wBAAwB,+BAiE1B"}
|
|
@@ -0,0 +1,86 @@
|
|
|
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 OPTIONS = ["light", "dark", "system"];
|
|
5
|
+
const defaultEmojis = {
|
|
6
|
+
light: "☀️",
|
|
7
|
+
dark: "🌙",
|
|
8
|
+
system: "⚙️",
|
|
9
|
+
};
|
|
10
|
+
const defaultIcons = {
|
|
11
|
+
light: "sunny-outline",
|
|
12
|
+
dark: "moon-outline",
|
|
13
|
+
system: "phone-portrait-outline",
|
|
14
|
+
};
|
|
15
|
+
const defaultLabels = {
|
|
16
|
+
light: "Clair",
|
|
17
|
+
dark: "Sombre",
|
|
18
|
+
system: "Système",
|
|
19
|
+
hint: "« Système » suit automatiquement le réglage clair/sombre de ton téléphone.",
|
|
20
|
+
};
|
|
21
|
+
const defaultStyles = {
|
|
22
|
+
container: { gap: 10 },
|
|
23
|
+
hint: { fontSize: 13, color: "#6b7280", marginBottom: 6 },
|
|
24
|
+
row: {
|
|
25
|
+
flexDirection: "row",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
gap: 12,
|
|
28
|
+
borderWidth: 1,
|
|
29
|
+
borderColor: "#e5e7eb",
|
|
30
|
+
borderRadius: 10,
|
|
31
|
+
padding: 14,
|
|
32
|
+
},
|
|
33
|
+
rowActive: {
|
|
34
|
+
borderColor: "#2563eb",
|
|
35
|
+
backgroundColor: "#2563eb1a",
|
|
36
|
+
},
|
|
37
|
+
emoji: { fontSize: 20 },
|
|
38
|
+
iconColor: "#111827",
|
|
39
|
+
iconColorActive: "#2563eb",
|
|
40
|
+
label: {
|
|
41
|
+
flex: 1,
|
|
42
|
+
fontSize: 15,
|
|
43
|
+
fontWeight: "600",
|
|
44
|
+
color: "#111827",
|
|
45
|
+
},
|
|
46
|
+
labelActive: { color: "#2563eb" },
|
|
47
|
+
check: {
|
|
48
|
+
fontSize: 16,
|
|
49
|
+
fontWeight: "700",
|
|
50
|
+
color: "#2563eb",
|
|
51
|
+
},
|
|
52
|
+
checkColor: "#2563eb",
|
|
53
|
+
};
|
|
54
|
+
// Selectable list of rows (icon + label + checkmark) for a light/dark/system
|
|
55
|
+
// theme preference — same data contract as `ThemeOptionList`, but with a
|
|
56
|
+
// second visual (`variant="emoji"`, the default) alongside the vector-icon
|
|
57
|
+
// one (`variant="icon"`). Pick whichever fits your app; both cover the exact
|
|
58
|
+
// same `light`/`dark`/`system` preference.
|
|
59
|
+
export function ThemeSettingsScreen({ value, onChange, variant = "emoji", emojis, icons, labels, showHint = true, styles, }) {
|
|
60
|
+
// Style fields are merged as arrays (default, then override) so a partial
|
|
61
|
+
// override only changes the properties it specifies instead of replacing
|
|
62
|
+
// the whole default style object.
|
|
63
|
+
const merged = {
|
|
64
|
+
container: [defaultStyles.container, styles?.container],
|
|
65
|
+
hint: [defaultStyles.hint, styles?.hint],
|
|
66
|
+
row: [defaultStyles.row, styles?.row],
|
|
67
|
+
rowActive: [defaultStyles.rowActive, styles?.rowActive],
|
|
68
|
+
emoji: [defaultStyles.emoji, styles?.emoji],
|
|
69
|
+
label: [defaultStyles.label, styles?.label],
|
|
70
|
+
labelActive: [defaultStyles.labelActive, styles?.labelActive],
|
|
71
|
+
check: [defaultStyles.check, styles?.check],
|
|
72
|
+
};
|
|
73
|
+
const e = { ...defaultEmojis, ...emojis };
|
|
74
|
+
const i = { ...defaultIcons, ...icons };
|
|
75
|
+
const t = { ...defaultLabels, ...labels };
|
|
76
|
+
const iconColor = styles?.iconColor ?? defaultStyles.iconColor;
|
|
77
|
+
const iconColorActive = styles?.iconColorActive ?? defaultStyles.iconColorActive;
|
|
78
|
+
const checkColor = styles?.checkColor ?? defaultStyles.checkColor;
|
|
79
|
+
return (_jsxs(View, { style: merged.container, children: [showHint && _jsx(Text, { style: merged.hint, children: t.hint }), OPTIONS.map((option) => {
|
|
80
|
+
const active = option === value;
|
|
81
|
+
return (_jsxs(Pressable, { style: [merged.row, active && merged.rowActive], onPress: () => {
|
|
82
|
+
onChange(option);
|
|
83
|
+
}, accessibilityRole: "radio", accessibilityState: { checked: active }, accessibilityLabel: t[option], children: [variant === "icon" ? (_jsx(Ionicons, { name: i[option], size: 20, color: active ? iconColorActive : iconColor })) : (_jsx(Text, { style: merged.emoji, children: e[option] })), _jsx(Text, { style: [merged.label, active && merged.labelActive], children: t[option] }), active &&
|
|
84
|
+
(variant === "icon" ? (_jsx(Ionicons, { name: "checkmark-circle", size: 20, color: checkColor })) : (_jsx(Text, { style: merged.check, children: "\u2713" })))] }, option));
|
|
85
|
+
})] }));
|
|
86
|
+
}
|
|
@@ -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":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGxE,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAUD,MAAM,WAAW,0BAA0B;IACzC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,+BAA+B,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9D,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,oBAAoB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,cAAc,EAAE,MAAM,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;IAClE,mEAAmE;IACnE,eAAe,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,qBAAqB,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KACpC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,yDAAyD;IACzD,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAAC;IACxE,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,MAAM,CAAC,EAAE,0BAA0B,CAAC;CACrC;AA2FD,wBAAgB,oBAAoB,CAAC,EACnC,cAAc,EACd,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,UAAoB,EACpB,MAAM,EACN,MAAM,GACP,EAAE,yBAAyB,+BA+L3B"}
|
|
@@ -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";
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ export * from "./components/list/Thumbnail.js";
|
|
|
3
3
|
export * from "./components/picker/PhotoPicker.js";
|
|
4
4
|
export * from "./components/picker/PickerModal.js";
|
|
5
5
|
export * from "./components/picker/VoiceSearchButton.js";
|
|
6
|
+
export * from "./components/settings/AboutSettingsScreen.js";
|
|
7
|
+
export * from "./components/settings/BackupSettingsScreen.js";
|
|
8
|
+
export * from "./components/settings/ContactSettingsScreen.js";
|
|
9
|
+
export * from "./components/settings/PrivacySettingsScreen.js";
|
|
10
|
+
export * from "./components/settings/SettingsMenu.js";
|
|
11
|
+
export * from "./components/settings/ThemeSettingsScreen.js";
|
|
12
|
+
export * from "./components/settings/UpdateSettingsScreen.js";
|
|
6
13
|
export * from "./components/theme/ThemeOptionList.js";
|
|
7
14
|
export * from "./components/theme/ThemeToggle.js";
|
|
8
15
|
export * from "./components/update/ChangelogNotes.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,13 @@ export * from "./components/list/Thumbnail.js";
|
|
|
8
8
|
export * from "./components/picker/PhotoPicker.js";
|
|
9
9
|
export * from "./components/picker/PickerModal.js";
|
|
10
10
|
export * from "./components/picker/VoiceSearchButton.js";
|
|
11
|
+
export * from "./components/settings/AboutSettingsScreen.js";
|
|
12
|
+
export * from "./components/settings/BackupSettingsScreen.js";
|
|
13
|
+
export * from "./components/settings/ContactSettingsScreen.js";
|
|
14
|
+
export * from "./components/settings/PrivacySettingsScreen.js";
|
|
15
|
+
export * from "./components/settings/SettingsMenu.js";
|
|
16
|
+
export * from "./components/settings/ThemeSettingsScreen.js";
|
|
17
|
+
export * from "./components/settings/UpdateSettingsScreen.js";
|
|
11
18
|
export * from "./components/theme/ThemeOptionList.js";
|
|
12
19
|
export * from "./components/theme/ThemeToggle.js";
|
|
13
20
|
export * from "./components/update/ChangelogNotes.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forthtilliath/react-native-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"react-native-gesture-handler": ">=2.20.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@expo/vector-icons": "^15.
|
|
36
|
-
"@types/react": "^19.2.
|
|
35
|
+
"@expo/vector-icons": "^15.1.1",
|
|
36
|
+
"@types/react": "^19.2.18",
|
|
37
37
|
"@types/react-test-renderer": "^19.1.0",
|
|
38
38
|
"eslint": "10.7.0",
|
|
39
|
-
"expo-image-picker": "~57.0.
|
|
39
|
+
"expo-image-picker": "~57.0.9",
|
|
40
40
|
"expo-speech-recognition": "^56.0.1",
|
|
41
|
-
"react": "^19.2.
|
|
41
|
+
"react": "^19.2.8",
|
|
42
42
|
"react-native": "0.86.0",
|
|
43
43
|
"react-native-gesture-handler": "~2.32.0",
|
|
44
44
|
"react-test-renderer": "19.2.3",
|