@forthtilliath/expo-backup-ui 0.2.0 → 0.3.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 CHANGED
@@ -54,4 +54,6 @@ Every action runs one at a time (a double tap is ignored) and shows errors in an
54
54
 
55
55
  The encryption section only appears when the manager's encryption isn't `off`: a switch in `optional` mode, a status line in `required` mode, and — on demand only, since it is the key — the recovery code, shown selectable so it can be copied into a password manager.
56
56
 
57
- Also exported: `useBackupState(manager)` (the manager's state, re-rendering on change) and `errorMessage(error, labels)`.
57
+ A failing automatic backup is spelled out on the screen: what went wrong and what to do, since when uploads fail, and the technical cause (selectable, to send to the developer). Linking an account whose first upload fails says so instead of "enabled", and "Back up now" confirms or explains.
58
+
59
+ Also exported: `useBackupState(manager)` (the manager's state, re-rendering on change), `useCloudBackupProblem(manager)` (a failure to warn about outside the screen, see `getCloudBackupProblem` in expo-backup), `errorMessage(error, labels)` and `codeMessage(labels, code, detail)`.
@@ -1 +1 @@
1
- {"version":3,"file":"CloudSection.d.ts","sourceRoot":"","sources":["../src/CloudSection.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;CAAE,+BAgG1D"}
1
+ {"version":3,"file":"CloudSection.d.ts","sourceRoot":"","sources":["../src/CloudSection.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;CAAE,+BA6H1D"}
@@ -1,11 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Alert, Text, View } from "react-native";
3
3
  import { ActionButton } from "./ActionButton.js";
4
- import { askConfirmation, askConflictChoice } from "./dialogs.js";
4
+ import { askConfirmation, askConflictChoice, codeMessage } from "./dialogs.js";
5
5
  /** Linking a Google account, then the automatic backup's status and actions. */
6
6
  export function CloudSection({ ui }) {
7
7
  const { manager, state, t, styles, busy, run } = ui;
8
8
  const { cloudAccount } = state;
9
+ /** The failure of the upload that just ran (kept in the state, never thrown), if any. */
10
+ function lastUploadError() {
11
+ const { cloudError, cloudErrorDetail } = manager.getState();
12
+ return cloudError ? codeMessage(t, cloudError, cloudErrorDetail) : null;
13
+ }
9
14
  const handleConnect = () => run(async () => {
10
15
  const result = await manager.connectCloud();
11
16
  if (result.status === "cancelled")
@@ -18,11 +23,23 @@ export function CloudSection({ ui }) {
18
23
  }
19
24
  await manager.resolveConflict(result, keep);
20
25
  }
21
- Alert.alert(t.connectedTitle, t.connectedConfirm(result.email));
26
+ // Linking succeeds even when its first upload fails (Drive full…):
27
+ // saying "enabled" then would hide the problem.
28
+ const uploadError = lastUploadError();
29
+ if (uploadError)
30
+ Alert.alert(t.connectedButFailedTitle, uploadError);
31
+ else
32
+ Alert.alert(t.connectedTitle, t.connectedConfirm(result.email));
22
33
  });
23
34
  const handleSyncNow = () => run(async () => {
24
- await manager.syncNow();
25
- // Errors are kept in the state (shown below) rather than thrown.
35
+ if (await manager.syncNow()) {
36
+ Alert.alert(t.syncDoneTitle, t.syncDoneMessage);
37
+ return;
38
+ }
39
+ // False also when an upload was already running: it reports itself.
40
+ const uploadError = lastUploadError();
41
+ if (uploadError)
42
+ Alert.alert(t.syncFailedTitle, uploadError);
26
43
  });
27
44
  const handleRestore = () => run(async () => {
28
45
  const remote = await manager.fetchCloudBackup();
@@ -40,5 +57,5 @@ export function CloudSection({ ui }) {
40
57
  if (!cloudAccount) {
41
58
  return (_jsxs(View, { style: styles.section, children: [_jsx(Text, { style: styles.sectionTitle, children: t.cloudTitle }), _jsx(Text, { style: styles.hint, children: t.cloudHint }), _jsx(ActionButton, { label: t.connectButton, onPress: () => void handleConnect(), busy: busy, styles: styles })] }));
42
59
  }
43
- return (_jsxs(View, { style: styles.section, children: [_jsx(Text, { style: styles.sectionTitle, children: t.cloudTitle }), _jsx(Text, { style: styles.status, children: cloudAccount.email }), _jsxs(Text, { style: styles.status, children: [t.lastCloudBackup(ui.formatDate(state.lastCloudBackupAt)), state.cloudPending && !state.cloudError ? t.cloudPending : ""] }), state.cloudError && (_jsx(Text, { style: styles.errorText, children: t.errors[state.cloudError] })), _jsx(Text, { style: styles.hint, children: t.autoHint }), _jsx(ActionButton, { label: t.syncNowButton, variant: "secondary", onPress: () => void handleSyncNow(), disabled: busy, styles: styles }), _jsx(ActionButton, { label: t.restoreCloudButton, variant: "secondary", onPress: () => void handleRestore(), disabled: busy, styles: styles }), _jsx(ActionButton, { label: t.disconnectButton, variant: "ghost", onPress: () => void handleDisconnect(), disabled: busy, styles: styles })] }));
60
+ return (_jsxs(View, { style: styles.section, children: [_jsx(Text, { style: styles.sectionTitle, children: t.cloudTitle }), _jsx(Text, { style: styles.status, children: cloudAccount.email }), _jsxs(Text, { style: styles.status, children: [t.lastCloudBackup(ui.formatDate(state.lastCloudBackupAt)), state.cloudPending && !state.cloudError ? t.cloudPending : ""] }), state.cloudError && (_jsxs(_Fragment, { children: [_jsx(Text, { style: styles.errorText, children: codeMessage(t, state.cloudError) }), state.cloudFailingSince !== null && (_jsx(Text, { style: styles.hint, children: t.failingSince(ui.formatDate(state.cloudFailingSince)) })), state.cloudErrorDetail && (_jsx(Text, { style: styles.hint, selectable: true, children: t.errorDetail(state.cloudErrorDetail) }))] })), _jsx(Text, { style: styles.hint, children: t.autoHint }), _jsx(ActionButton, { label: t.syncNowButton, variant: "secondary", onPress: () => void handleSyncNow(), disabled: busy, styles: styles }), _jsx(ActionButton, { label: t.restoreCloudButton, variant: "secondary", onPress: () => void handleRestore(), disabled: busy, styles: styles }), _jsx(ActionButton, { label: t.disconnectButton, variant: "ghost", onPress: () => void handleDisconnect(), disabled: busy, styles: styles })] }));
44
61
  }
package/dist/dialogs.d.ts CHANGED
@@ -1,4 +1,11 @@
1
+ import { type BackupErrorCode } from "@forthtilliath/expo-backup/errors";
1
2
  import type { BackupScreenLabels } from "./labels.js";
3
+ /**
4
+ * Message of an error code, followed by its technical cause when known. The
5
+ * code may come from a state saved by another version: unknown ones fall
6
+ * back to the generic message rather than to nothing.
7
+ */
8
+ export declare function codeMessage(t: BackupScreenLabels, code: BackupErrorCode, detail?: string | null): string;
2
9
  /** User-facing text of any error: localized for the package's own, as is for the app's. */
3
10
  export declare function errorMessage(error: unknown, t: BackupScreenLabels): string;
4
11
  export declare function showError(error: unknown, t: BackupScreenLabels): void;
@@ -1 +1 @@
1
- {"version":3,"file":"dialogs.d.ts","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAI1E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAErE;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAC7B,CAAC,EAAE,kBAAkB,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC,CAmCpC"}
1
+ {"version":3,"file":"dialogs.d.ts","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,kBAAkB,EACrB,IAAI,EAAE,eAAe,EACrB,MAAM,GAAE,MAAM,GAAG,IAAW,GAC3B,MAAM,CAGR;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAM1E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAErE;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAC7B,CAAC,EAAE,kBAAkB,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC,CAmCpC"}
package/dist/dialogs.js CHANGED
@@ -1,10 +1,20 @@
1
1
  import { Alert } from "react-native";
2
2
  // Subpath: errors.js loads no native module, unlike the package root.
3
- import { BackupError } from "@forthtilliath/expo-backup/errors";
3
+ import { BackupError, backupErrorDetail, } from "@forthtilliath/expo-backup/errors";
4
+ /**
5
+ * Message of an error code, followed by its technical cause when known. The
6
+ * code may come from a state saved by another version: unknown ones fall
7
+ * back to the generic message rather than to nothing.
8
+ */
9
+ export function codeMessage(t, code, detail = null) {
10
+ const message = t.errors[code] ?? t.errors.unknown;
11
+ return detail ? `${message}\n\n${t.errorDetail(detail)}` : message;
12
+ }
4
13
  /** User-facing text of any error: localized for the package's own, as is for the app's. */
5
14
  export function errorMessage(error, t) {
15
+ // The app's own errors (`parse`…) are already meant for the user: no detail.
6
16
  if (error instanceof BackupError)
7
- return t.errors[error.code];
17
+ return codeMessage(t, error.code, backupErrorDetail(error));
8
18
  if (error instanceof Error && error.message)
9
19
  return error.message;
10
20
  return t.errors.unknown;
package/dist/hooks.d.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import type { BackupManager, BackupState } from "@forthtilliath/expo-backup";
2
+ import { type CloudBackupProblem, type CloudBackupProblemOptions } from "@forthtilliath/expo-backup/cloudBackupProblem";
2
3
  /** The manager's state (linked account, last backup, pending upload…), re-rendering on change. */
3
4
  export declare function useBackupState<T>(manager: BackupManager<T>): BackupState;
5
+ /**
6
+ * The automatic backup's failure to tell the user about outside the backup
7
+ * screen (banner, alert, tab badge), or null — see `getCloudBackupProblem`.
8
+ * Turn its code into text with `codeMessage`.
9
+ */
10
+ export declare function useCloudBackupProblem<T>(manager: BackupManager<T>, options?: CloudBackupProblemOptions): CloudBackupProblem | null;
4
11
  /**
5
12
  * Starts the manager for the component's lifetime: reads the persisted state,
6
13
  * retries a pending upload, flushes when the app goes to the background.
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE7E,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,WAAW,CAExE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAEhE"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAE/B,MAAM,+CAA+C,CAAC;AAEvD,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,WAAW,CAExE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE,yBAAyB,GAClC,kBAAkB,GAAG,IAAI,CAE3B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAEhE"}
package/dist/hooks.js CHANGED
@@ -1,8 +1,18 @@
1
1
  import { useEffect, useSyncExternalStore } from "react";
2
+ // Subpath: pure module, no native peer loaded.
3
+ import { getCloudBackupProblem, } from "@forthtilliath/expo-backup/cloudBackupProblem";
2
4
  /** The manager's state (linked account, last backup, pending upload…), re-rendering on change. */
3
5
  export function useBackupState(manager) {
4
6
  return useSyncExternalStore(manager.subscribe, manager.getState);
5
7
  }
8
+ /**
9
+ * The automatic backup's failure to tell the user about outside the backup
10
+ * screen (banner, alert, tab badge), or null — see `getCloudBackupProblem`.
11
+ * Turn its code into text with `codeMessage`.
12
+ */
13
+ export function useCloudBackupProblem(manager, options) {
14
+ return getCloudBackupProblem(useBackupState(manager), options);
15
+ }
6
16
  /**
7
17
  * Starts the manager for the component's lifetime: reads the persisted state,
8
18
  * retries a pending upload, flushes when the app goes to the background.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./BackupScreen.js";
2
- export { errorMessage } from "./dialogs.js";
2
+ export { codeMessage, errorMessage } from "./dialogs.js";
3
3
  export * from "./hooks.js";
4
4
  export * from "./labels.js";
5
5
  export type { BackupScreenStyles } from "./styles.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzD,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from "./BackupScreen.js";
2
- export { errorMessage } from "./dialogs.js";
2
+ export { codeMessage, errorMessage } from "./dialogs.js";
3
3
  export * from "./hooks.js";
4
4
  export * from "./labels.js";
package/dist/labels.d.ts CHANGED
@@ -8,8 +8,14 @@ export interface BackupScreenLabels {
8
8
  connectButton: string;
9
9
  connectedTitle: string;
10
10
  connectedConfirm: (email: string) => string;
11
+ /** Account linked, but its first upload failed: shown instead of `connectedTitle`. */
12
+ connectedButFailedTitle: string;
11
13
  lastCloudBackup: (date: string) => string;
12
14
  cloudPending: string;
15
+ failingSince: (date: string) => string;
16
+ syncDoneTitle: string;
17
+ syncDoneMessage: string;
18
+ syncFailedTitle: string;
13
19
  autoHint: string;
14
20
  syncNowButton: string;
15
21
  restoreCloudButton: string;
@@ -56,6 +62,8 @@ export interface BackupScreenLabels {
56
62
  confirm: string;
57
63
  close: string;
58
64
  errorTitle: string;
65
+ /** Technical cause under an error message ("HTTP 403 · storageQuotaExceeded"). */
66
+ errorDetail: (detail: string) => string;
59
67
  errors: Record<BackupErrorCode, string>;
60
68
  }
61
69
  /** What `labels` accepts: any subset of the texts, error messages included. */
@@ -1 +1 @@
1
- {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"labels.en.d.ts","sourceRoot":"","sources":["../src/labels.en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,QAAQ,EAAE,kBAkGtB,CAAC"}
1
+ {"version":3,"file":"labels.en.d.ts","sourceRoot":"","sources":["../src/labels.en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,QAAQ,EAAE,kBAkHtB,CAAC"}
package/dist/labels.en.js CHANGED
@@ -4,8 +4,13 @@ export const enLabels = {
4
4
  connectButton: "Connect Google Drive",
5
5
  connectedTitle: "Automatic backup enabled",
6
6
  connectedConfirm: (email) => `Your data is now sent to the Google Drive of ${email} after every change.`,
7
+ connectedButFailedTitle: "Account linked, but the upload failed",
7
8
  lastCloudBackup: (date) => `Last upload: ${date}`,
8
9
  cloudPending: " · upload pending…",
10
+ failingSince: (date) => `Uploads have been failing since ${date}.`,
11
+ syncDoneTitle: "Backup uploaded",
12
+ syncDoneMessage: "Your data is up to date on Google Drive.",
13
+ syncFailedTitle: "The backup couldn't be uploaded",
9
14
  autoHint: "Uploaded automatically after every change.",
10
15
  syncNowButton: "Back up now",
11
16
  restoreCloudButton: "Restore from Google Drive",
@@ -51,17 +56,21 @@ export const enLabels = {
51
56
  confirm: "OK",
52
57
  close: "Close",
53
58
  errorTitle: "Error",
59
+ errorDetail: (detail) => `Technical detail: ${detail}`,
54
60
  errors: {
55
- "google-unavailable": "Google Drive backup isn't available in this version of the app.",
56
- "play-services-unavailable": "Google Play services aren't available on this phone.",
57
- "oauth-misconfigured": "Google doesn't recognize this version of the app (missing OAuth client or different SHA-1).",
58
- "sign-in-failed": "Signing in to Google failed.",
59
- "reconnect-required": "Reconnect your Google account to resume backups.",
60
- "drive-api-disabled": "The Google Drive API isn't enabled in the app's Google Cloud project.",
61
- "drive-permission-missing": "Access to Google Drive wasn't granted. Reconnect your account and allow Drive access.",
62
- "drive-access-denied": "Google Drive denied access. Reconnect your Google account.",
63
- "drive-unavailable": "Google Drive is temporarily unavailable.",
64
- network: "No internet connection.",
61
+ "google-unavailable": "Google Drive backup isn't available in this version of the app. Use the manual backup (Export my data).",
62
+ "play-services-unavailable": "Google Play services aren't available on this phone, so Google Drive backup can't work. Use the manual backup (Export my data).",
63
+ "oauth-misconfigured": "Google doesn't recognize this version of the app (missing OAuth client or different SHA-1). Nothing you can do: let the app's developer know.",
64
+ "sign-in-failed": "Signing in to Google failed. Check your internet connection, then try again.",
65
+ "reconnect-required": "Google no longer lets the app reach your Drive (access removed or expired). Tap “Disconnect”, then reconnect your Google account.",
66
+ "drive-storage-full": "Your Google storage is full, so the backup can't be uploaded anymore. Free up some space (Gmail, Drive and Photos share it, see one.google.com/storage), then tap “Back up now”.",
67
+ "drive-rate-limited": "Google Drive is receiving too many requests right now. The upload will be retried automatically later.",
68
+ "drive-blocked-by-admin": "The administrator of this Google account (work, school…) doesn't let apps use Drive. Disconnect it and link a personal Google account instead.",
69
+ "drive-api-disabled": "The Google Drive API isn't enabled in the app's Google Cloud project. Nothing you can do: let the app's developer know.",
70
+ "drive-permission-missing": "Access to Google Drive wasn't granted. Tap “Disconnect”, then reconnect your account and tick Drive access.",
71
+ "drive-access-denied": "Google Drive refused the upload for an unexpected reason. If it happens again, tap “Disconnect” and reconnect your Google account; otherwise let the app's developer know, with the technical detail.",
72
+ "drive-unavailable": "Google Drive is temporarily unavailable. The upload will be retried automatically.",
73
+ network: "No internet connection. The upload will be retried automatically once you're online.",
65
74
  "missing-key": "This backup is encrypted with a key this phone doesn't know. Enter the recovery code of the device that created it.",
66
75
  "invalid-recovery-code": "This recovery code contains a typo.",
67
76
  "recovery-code-mismatch": "This recovery code is valid, but it isn't the one that encrypted this file.",
@@ -1 +1 @@
1
- {"version":3,"file":"labels.fr.d.ts","sourceRoot":"","sources":["../src/labels.fr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,QAAQ,EAAE,kBAuGtB,CAAC"}
1
+ {"version":3,"file":"labels.fr.d.ts","sourceRoot":"","sources":["../src/labels.fr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,QAAQ,EAAE,kBAsHtB,CAAC"}
package/dist/labels.fr.js CHANGED
@@ -4,8 +4,13 @@ export const frLabels = {
4
4
  connectButton: "Connecter Google Drive",
5
5
  connectedTitle: "Sauvegarde automatique activée",
6
6
  connectedConfirm: (email) => `Tes données sont désormais envoyées sur le Google Drive de ${email} après chaque modification.`,
7
+ connectedButFailedTitle: "Compte relié, mais l'envoi a échoué",
7
8
  lastCloudBackup: (date) => `Dernier envoi : ${date}`,
8
9
  cloudPending: " · envoi en attente…",
10
+ failingSince: (date) => `Les envois échouent depuis le ${date}.`,
11
+ syncDoneTitle: "Sauvegarde envoyée",
12
+ syncDoneMessage: "Tes données sont à jour sur Google Drive.",
13
+ syncFailedTitle: "La sauvegarde n'a pas pu être envoyée",
9
14
  autoHint: "Envoi automatique après chaque modification.",
10
15
  syncNowButton: "Sauvegarder maintenant",
11
16
  restoreCloudButton: "Restaurer depuis Google Drive",
@@ -51,17 +56,21 @@ export const frLabels = {
51
56
  confirm: "Valider",
52
57
  close: "Fermer",
53
58
  errorTitle: "Erreur",
59
+ errorDetail: (detail) => `Détail technique : ${detail}`,
54
60
  errors: {
55
- "google-unavailable": "La sauvegarde Google Drive n'est pas disponible dans cette version de l'app.",
56
- "play-services-unavailable": "Les services Google Play sont indisponibles sur ce téléphone.",
57
- "oauth-misconfigured": "Google ne reconnaît pas cette version de l'app (client OAuth absent ou empreinte SHA-1 différente).",
58
- "sign-in-failed": "La connexion à Google a échoué.",
59
- "reconnect-required": "Reconnecte ton compte Google pour reprendre la sauvegarde.",
60
- "drive-api-disabled": "L'API Google Drive n'est pas activée dans le projet Google Cloud de l'app.",
61
- "drive-permission-missing": "L'accès à Google Drive n'a pas été autorisé. Reconnecte ton compte en acceptant l'accès à Drive.",
62
- "drive-access-denied": "Google Drive a refusé l'accès. Reconnecte ton compte Google.",
63
- "drive-unavailable": "Google Drive est momentanément indisponible.",
64
- network: "Pas de connexion internet.",
61
+ "google-unavailable": "La sauvegarde Google Drive n'est pas disponible dans cette version de l'app. Utilise la sauvegarde manuelle (Exporter mes données).",
62
+ "play-services-unavailable": "Les services Google Play sont indisponibles sur ce téléphone : la sauvegarde Google Drive ne peut pas fonctionner. Utilise la sauvegarde manuelle (Exporter mes données).",
63
+ "oauth-misconfigured": "Google ne reconnaît pas cette version de l'app (client OAuth absent ou empreinte SHA-1 différente). Tu n'y peux rien : préviens le développeur de l'app.",
64
+ "sign-in-failed": "La connexion à Google a échoué. Vérifie ta connexion internet, puis réessaie.",
65
+ "reconnect-required": "Google n'autorise plus l'app à accéder à ton Drive (accès retiré ou expiré). Appuie sur « Déconnecter », puis reconnecte ton compte Google.",
66
+ "drive-storage-full": "Ton espace de stockage Google est plein : la sauvegarde ne peut plus être envoyée. Libère de la place (Gmail, Drive et Photos partagent le même espace, voir one.google.com/storage), puis appuie sur « Sauvegarder maintenant ».",
67
+ "drive-rate-limited": "Google Drive reçoit trop de demandes pour le moment. L'envoi sera retenté automatiquement plus tard.",
68
+ "drive-blocked-by-admin": "L'administrateur de ce compte Google (travail, école…) interdit aux apps d'utiliser Drive. Déconnecte-le et relie plutôt un compte Google personnel.",
69
+ "drive-api-disabled": "L'API Google Drive n'est pas activée dans le projet Google Cloud de l'app. Tu n'y peux rien : préviens le développeur de l'app.",
70
+ "drive-permission-missing": "L'accès à Google Drive n'a pas été autorisé. Appuie sur « Déconnecter », puis reconnecte ton compte en cochant l'accès à Drive.",
71
+ "drive-access-denied": "Google Drive a refusé l'envoi pour une raison inattendue. Si ça se reproduit, appuie sur « Déconnecter » puis reconnecte ton compte Google, et sinon préviens le développeur de l'app avec le détail technique.",
72
+ "drive-unavailable": "Google Drive est momentanément indisponible. L'envoi sera retenté automatiquement.",
73
+ network: "Pas de connexion internet. L'envoi sera retenté automatiquement une fois connecté.",
65
74
  "missing-key": "Cette sauvegarde est chiffrée avec une clé inconnue de ce téléphone. Saisis la clé de secours de l'appareil qui l'a créée.",
66
75
  "invalid-recovery-code": "Cette clé de secours contient une faute de frappe.",
67
76
  "recovery-code-mismatch": "Cette clé de secours est valide, mais ce n'est pas celle qui a chiffré ce fichier.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forthtilliath/expo-backup-ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "./*": "./dist/*.js"
23
23
  },
24
24
  "dependencies": {
25
- "@forthtilliath/expo-backup": "^0.2.0"
25
+ "@forthtilliath/expo-backup": "^0.3.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=19",