@buoy-gg/time-machine 7.0.36 → 7.0.38
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 +40 -35
- package/lib/commonjs/components/HeaderActions.js +91 -0
- package/lib/commonjs/components/HeaderReloadButton.js +22 -25
- package/lib/commonjs/components/TimeMachineBody.js +68 -13
- package/lib/commonjs/components/floating/NamePromptModal.js +193 -0
- package/lib/commonjs/components/floating/TimeMachineFloatingTool.js +715 -0
- package/lib/commonjs/components/floating/armedPoint.js +92 -0
- package/lib/commonjs/components/floating/barSettings.js +66 -0
- package/lib/commonjs/components/list/CaptureBar.js +2 -0
- package/lib/commonjs/components/list/RestoreOutcomeCard.js +2 -0
- package/lib/commonjs/components/list/SnapshotRow.js +37 -13
- package/lib/commonjs/components/list/SourcesStrip.js +127 -83
- package/lib/commonjs/components/list/UndoDeleteBar.js +3 -1
- package/lib/commonjs/components/preview/RestorePreviewScreen.js +7 -2
- package/lib/commonjs/components/sources.js +19 -8
- package/lib/commonjs/index.js +42 -0
- package/lib/commonjs/preset.js +6 -4
- package/lib/commonjs/store/sourceIds.js +9 -0
- package/lib/commonjs/store/sourceSettings.js +79 -0
- package/lib/commonjs/store/timeMachineStore.js +75 -10
- package/lib/commonjs/sync/timeMachineSyncAdapter.js +5 -1
- package/lib/commonjs/ui/SwipeActionsRow.js +14 -3
- package/lib/commonjs/ui/primitives.js +2 -0
- package/lib/commonjs/ui/tmTheme.js +18 -5
- package/lib/module/components/HeaderActions.js +85 -0
- package/lib/module/components/HeaderReloadButton.js +24 -27
- package/lib/module/components/TimeMachineBody.js +71 -16
- package/lib/module/components/floating/NamePromptModal.js +188 -0
- package/lib/module/components/floating/TimeMachineFloatingTool.js +708 -0
- package/lib/module/components/floating/armedPoint.js +82 -0
- package/lib/module/components/floating/barSettings.js +57 -0
- package/lib/module/components/list/CaptureBar.js +2 -0
- package/lib/module/components/list/RestoreOutcomeCard.js +2 -0
- package/lib/module/components/list/SnapshotRow.js +37 -13
- package/lib/module/components/list/SourcesStrip.js +130 -84
- package/lib/module/components/list/UndoDeleteBar.js +3 -1
- package/lib/module/components/preview/RestorePreviewScreen.js +7 -2
- package/lib/module/components/sources.js +13 -6
- package/lib/module/index.js +7 -1
- package/lib/module/preset.js +6 -4
- package/lib/module/store/sourceIds.js +5 -0
- package/lib/module/store/sourceSettings.js +69 -0
- package/lib/module/store/timeMachineStore.js +74 -9
- package/lib/module/sync/timeMachineSyncAdapter.js +5 -1
- package/lib/module/ui/SwipeActionsRow.js +14 -3
- package/lib/module/ui/primitives.js +2 -0
- package/lib/module/ui/tmTheme.js +18 -5
- package/lib/typescript/components/HeaderActions.d.ts +7 -0
- package/lib/typescript/components/floating/NamePromptModal.d.ts +9 -0
- package/lib/typescript/components/floating/TimeMachineFloatingTool.d.ts +6 -0
- package/lib/typescript/components/floating/armedPoint.d.ts +14 -0
- package/lib/typescript/components/floating/barSettings.d.ts +7 -0
- package/lib/typescript/components/list/SnapshotRow.d.ts +5 -1
- package/lib/typescript/components/list/SourcesStrip.d.ts +3 -1
- package/lib/typescript/components/sources.d.ts +7 -2
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/preset.d.ts +4 -2
- package/lib/typescript/store/snapshotVault.d.ts +6 -0
- package/lib/typescript/store/sourceIds.d.ts +3 -0
- package/lib/typescript/store/sourceSettings.d.ts +9 -0
- package/lib/typescript/store/timeMachineStore.d.ts +16 -1
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts +4 -0
- package/lib/typescript/ui/tmTheme.d.ts +17 -4
- package/package.json +10 -7
- package/lib/commonjs/components/HeaderReloadButton.js.map +0 -1
- package/lib/commonjs/components/TimeMachineBody.js.map +0 -1
- package/lib/commonjs/components/TimeMachineModal.js.map +0 -1
- package/lib/commonjs/components/confirm.js.map +0 -1
- package/lib/commonjs/components/format.js.map +0 -1
- package/lib/commonjs/components/list/CaptureBar.js.map +0 -1
- package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +0 -1
- package/lib/commonjs/components/list/SnapshotRow.js.map +0 -1
- package/lib/commonjs/components/list/SourcesStrip.js.map +0 -1
- package/lib/commonjs/components/list/UndoDeleteBar.js.map +0 -1
- package/lib/commonjs/components/list/WipeAllRow.js.map +0 -1
- package/lib/commonjs/components/preview/ItemDiffPanel.js.map +0 -1
- package/lib/commonjs/components/preview/PreviewFlow.js.map +0 -1
- package/lib/commonjs/components/preview/RestorePointNameRow.js.map +0 -1
- package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +0 -1
- package/lib/commonjs/components/preview/verdictMeta.js.map +0 -1
- package/lib/commonjs/components/sources.js.map +0 -1
- package/lib/commonjs/hooks/useTimeMachine.js.map +0 -1
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/preset.js.map +0 -1
- package/lib/commonjs/store/routeRestore.js.map +0 -1
- package/lib/commonjs/store/snapshotVault.js.map +0 -1
- package/lib/commonjs/store/timeMachineStore.js.map +0 -1
- package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +0 -1
- package/lib/commonjs/ui/SwipeActionsRow.js.map +0 -1
- package/lib/commonjs/ui/primitives.js.map +0 -1
- package/lib/commonjs/ui/tmTheme.js.map +0 -1
- package/lib/module/components/HeaderReloadButton.js.map +0 -1
- package/lib/module/components/TimeMachineBody.js.map +0 -1
- package/lib/module/components/TimeMachineModal.js.map +0 -1
- package/lib/module/components/confirm.js.map +0 -1
- package/lib/module/components/format.js.map +0 -1
- package/lib/module/components/list/CaptureBar.js.map +0 -1
- package/lib/module/components/list/RestoreOutcomeCard.js.map +0 -1
- package/lib/module/components/list/SnapshotRow.js.map +0 -1
- package/lib/module/components/list/SourcesStrip.js.map +0 -1
- package/lib/module/components/list/UndoDeleteBar.js.map +0 -1
- package/lib/module/components/list/WipeAllRow.js.map +0 -1
- package/lib/module/components/preview/ItemDiffPanel.js.map +0 -1
- package/lib/module/components/preview/PreviewFlow.js.map +0 -1
- package/lib/module/components/preview/RestorePointNameRow.js.map +0 -1
- package/lib/module/components/preview/RestorePreviewScreen.js.map +0 -1
- package/lib/module/components/preview/verdictMeta.js.map +0 -1
- package/lib/module/components/sources.js.map +0 -1
- package/lib/module/hooks/useTimeMachine.js.map +0 -1
- package/lib/module/index.js.map +0 -1
- package/lib/module/preset.js.map +0 -1
- package/lib/module/store/routeRestore.js.map +0 -1
- package/lib/module/store/snapshotVault.js.map +0 -1
- package/lib/module/store/timeMachineStore.js.map +0 -1
- package/lib/module/sync/timeMachineSyncAdapter.js.map +0 -1
- package/lib/module/ui/SwipeActionsRow.js.map +0 -1
- package/lib/module/ui/primitives.js.map +0 -1
- package/lib/module/ui/tmTheme.js.map +0 -1
- package/lib/typescript/components/HeaderReloadButton.d.ts.map +0 -1
- package/lib/typescript/components/TimeMachineBody.d.ts.map +0 -1
- package/lib/typescript/components/TimeMachineModal.d.ts.map +0 -1
- package/lib/typescript/components/confirm.d.ts.map +0 -1
- package/lib/typescript/components/format.d.ts.map +0 -1
- package/lib/typescript/components/list/CaptureBar.d.ts.map +0 -1
- package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +0 -1
- package/lib/typescript/components/list/SnapshotRow.d.ts.map +0 -1
- package/lib/typescript/components/list/SourcesStrip.d.ts.map +0 -1
- package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +0 -1
- package/lib/typescript/components/list/WipeAllRow.d.ts.map +0 -1
- package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +0 -1
- package/lib/typescript/components/preview/PreviewFlow.d.ts.map +0 -1
- package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +0 -1
- package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +0 -1
- package/lib/typescript/components/preview/verdictMeta.d.ts.map +0 -1
- package/lib/typescript/components/sources.d.ts.map +0 -1
- package/lib/typescript/hooks/useTimeMachine.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/store/routeRestore.d.ts.map +0 -1
- package/lib/typescript/store/snapshotVault.d.ts.map +0 -1
- package/lib/typescript/store/timeMachineStore.d.ts.map +0 -1
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/ui/SwipeActionsRow.d.ts.map +0 -1
- package/lib/typescript/ui/primitives.d.ts.map +0 -1
- package/lib/typescript/ui/tmTheme.d.ts.map +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ensurePickedPointLoaded = ensurePickedPointLoaded;
|
|
7
|
+
exports.getPickedPointId = getPickedPointId;
|
|
8
|
+
exports.resetPickedPointForTests = resetPickedPointForTests;
|
|
9
|
+
exports.resolveArmedPoint = resolveArmedPoint;
|
|
10
|
+
exports.setPickedPointId = setPickedPointId;
|
|
11
|
+
exports.subscribePickedPoint = subscribePickedPoint;
|
|
12
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
13
|
+
/**
|
|
14
|
+
* Which restore point the floating restore bar restores on tap.
|
|
15
|
+
*
|
|
16
|
+
* The bar needs an answer before the user picks anything, or its first tap is
|
|
17
|
+
* a picker instead of a restore. So the armed point is resolved, not just
|
|
18
|
+
* stored: an explicit pick wins, then the point restored most recently, then
|
|
19
|
+
* the newest capture. Baselines never resolve; restoring one wipes storage
|
|
20
|
+
* and reloads behind a confirm dialog, which is not a one-tap action. The
|
|
21
|
+
* safety slot never resolves either: Undo is its way in.
|
|
22
|
+
*
|
|
23
|
+
* The explicit pick is persisted so it survives the reloads the loop itself
|
|
24
|
+
* causes (RELOAD APP, reload-mode restores).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const KEY = _sharedUi.devToolsStorageKeys.timeMachine.armedPoint();
|
|
28
|
+
let pickedId = null;
|
|
29
|
+
let loaded = false;
|
|
30
|
+
let loadPromise = null;
|
|
31
|
+
const listeners = new Set();
|
|
32
|
+
const notify = () => listeners.forEach(listener => listener());
|
|
33
|
+
function getPickedPointId() {
|
|
34
|
+
return pickedId;
|
|
35
|
+
}
|
|
36
|
+
function subscribePickedPoint(listener) {
|
|
37
|
+
listeners.add(listener);
|
|
38
|
+
return () => {
|
|
39
|
+
listeners.delete(listener);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Read the saved pick once. Later calls return the same promise. */
|
|
44
|
+
function ensurePickedPointLoaded() {
|
|
45
|
+
if (loaded) return Promise.resolve();
|
|
46
|
+
if (!loadPromise) {
|
|
47
|
+
loadPromise = _sharedUi.persistentStorage.getItem(KEY).then(saved => {
|
|
48
|
+
// A pick made while the read was in flight wins over the disk copy.
|
|
49
|
+
if (pickedId === null && saved) {
|
|
50
|
+
pickedId = saved;
|
|
51
|
+
notify();
|
|
52
|
+
}
|
|
53
|
+
}).catch(() => {}).finally(() => {
|
|
54
|
+
loaded = true;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return loadPromise;
|
|
58
|
+
}
|
|
59
|
+
function setPickedPointId(id) {
|
|
60
|
+
if (pickedId === id) return;
|
|
61
|
+
pickedId = id;
|
|
62
|
+
notify();
|
|
63
|
+
void (id === null ? _sharedUi.persistentStorage.removeItem(KEY) : _sharedUi.persistentStorage.setItem(KEY, id)).catch(() => {});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Test seam: forget the pick and the load, without touching storage. */
|
|
67
|
+
function resetPickedPointForTests() {
|
|
68
|
+
pickedId = null;
|
|
69
|
+
loaded = false;
|
|
70
|
+
loadPromise = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The point the bar restores: the saved pick if it still exists, else the
|
|
75
|
+
* most recently restored point, else the newest capture. Never a baseline.
|
|
76
|
+
*/
|
|
77
|
+
function resolveArmedPoint(snapshots, pickedId) {
|
|
78
|
+
const candidates = snapshots.filter(meta => !meta.baseline && !meta.safety);
|
|
79
|
+
if (candidates.length === 0) return null;
|
|
80
|
+
if (pickedId) {
|
|
81
|
+
const picked = candidates.find(meta => meta.id === pickedId);
|
|
82
|
+
if (picked) return picked;
|
|
83
|
+
}
|
|
84
|
+
let best = null;
|
|
85
|
+
for (const meta of candidates) {
|
|
86
|
+
if (meta.lastRestoredAt === undefined) continue;
|
|
87
|
+
if (!best || meta.lastRestoredAt > (best.lastRestoredAt ?? 0)) best = meta;
|
|
88
|
+
}
|
|
89
|
+
if (best) return best;
|
|
90
|
+
return candidates.reduce((newest, meta) => meta.createdAt > newest.createdAt ? meta : newest);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=armedPoint.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ensureBarSettingsLoaded = ensureBarSettingsLoaded;
|
|
7
|
+
exports.getBarReloadMode = getBarReloadMode;
|
|
8
|
+
exports.resetBarSettingsForTests = resetBarSettingsForTests;
|
|
9
|
+
exports.setBarReloadMode = setBarReloadMode;
|
|
10
|
+
exports.subscribeBarSettings = subscribeBarSettings;
|
|
11
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
|
+
/**
|
|
13
|
+
* The action bar's one setting: restore live, or restore and reload.
|
|
14
|
+
*
|
|
15
|
+
* Live restore replaces stores in place and is right for state that lives in
|
|
16
|
+
* Redux, Zustand, Jotai or the query cache. It cannot reach a React context
|
|
17
|
+
* or `useState` that hydrates itself from storage once at mount: the
|
|
18
|
+
* storage key comes back, the screen keeps showing what it already had, and
|
|
19
|
+
* "restore did nothing" is the report. A restore that reloads the app makes
|
|
20
|
+
* those screens rebuild from the restored storage. Persisted, because the
|
|
21
|
+
* reload itself is what brings the bar back.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const KEY = _sharedUi.devToolsStorageKeys.timeMachine.barReloadMode();
|
|
25
|
+
let reloadMode = false;
|
|
26
|
+
let loaded = false;
|
|
27
|
+
let loadPromise = null;
|
|
28
|
+
const listeners = new Set();
|
|
29
|
+
const notify = () => listeners.forEach(listener => listener());
|
|
30
|
+
function getBarReloadMode() {
|
|
31
|
+
return reloadMode;
|
|
32
|
+
}
|
|
33
|
+
function subscribeBarSettings(listener) {
|
|
34
|
+
listeners.add(listener);
|
|
35
|
+
return () => {
|
|
36
|
+
listeners.delete(listener);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function ensureBarSettingsLoaded() {
|
|
40
|
+
if (loaded) return Promise.resolve();
|
|
41
|
+
if (!loadPromise) {
|
|
42
|
+
loadPromise = _sharedUi.persistentStorage.getItem(KEY).then(saved => {
|
|
43
|
+
if (!reloadMode && saved === "1") {
|
|
44
|
+
reloadMode = true;
|
|
45
|
+
notify();
|
|
46
|
+
}
|
|
47
|
+
}).catch(() => {}).finally(() => {
|
|
48
|
+
loaded = true;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return loadPromise;
|
|
52
|
+
}
|
|
53
|
+
function setBarReloadMode(on) {
|
|
54
|
+
if (on === reloadMode) return;
|
|
55
|
+
reloadMode = on;
|
|
56
|
+
notify();
|
|
57
|
+
void _sharedUi.persistentStorage.setItem(KEY, on ? "1" : "0").catch(() => {});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Test seam. */
|
|
61
|
+
function resetBarSettingsForTests() {
|
|
62
|
+
reloadMode = false;
|
|
63
|
+
loaded = false;
|
|
64
|
+
loadPromise = null;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=barSettings.js.map
|
|
@@ -61,6 +61,8 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
61
61
|
marginHorizontal: _tmTheme.tm.gutter,
|
|
62
62
|
backgroundColor: _tmTheme.tm.color.group,
|
|
63
63
|
borderRadius: _tmTheme.tm.radius.group,
|
|
64
|
+
borderWidth: 1,
|
|
65
|
+
borderColor: _tmTheme.tm.color.groupBorder,
|
|
64
66
|
paddingLeft: _tmTheme.tm.textInset,
|
|
65
67
|
padding: 6
|
|
66
68
|
},
|
|
@@ -136,6 +136,8 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
136
136
|
marginTop: 12,
|
|
137
137
|
backgroundColor: _tmTheme.tm.color.group,
|
|
138
138
|
borderRadius: _tmTheme.tm.radius.group,
|
|
139
|
+
borderWidth: 1,
|
|
140
|
+
borderColor: _tmTheme.tm.color.groupBorder,
|
|
139
141
|
padding: 14,
|
|
140
142
|
gap: 4
|
|
141
143
|
},
|
|
@@ -32,20 +32,29 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
32
32
|
// A saved selection is worth stating on the row — this restore point no
|
|
33
33
|
// longer does the obvious thing, and one tap on RESTORE honors it.
|
|
34
34
|
if (meta.excludedKeys?.length) parts.push(`skips ${meta.excludedKeys.length}`);else if (meta.scope?.length) parts.push(`scoped (${meta.scope.length})`);
|
|
35
|
+
// The bar's own undo slot: kept for one restore, not a point you chose.
|
|
36
|
+
if (meta.safety) parts.unshift("safety copy");
|
|
35
37
|
return parts.join(" · ");
|
|
36
38
|
}
|
|
37
39
|
function SnapshotRow({
|
|
38
40
|
meta,
|
|
39
41
|
busy,
|
|
42
|
+
armed = false,
|
|
40
43
|
onOpen,
|
|
41
44
|
onRestore,
|
|
42
45
|
onDelete,
|
|
43
|
-
onDuplicate
|
|
46
|
+
onDuplicate,
|
|
47
|
+
onArm
|
|
44
48
|
}) {
|
|
45
49
|
const testID = `time-machine-snap-${meta.id}`;
|
|
46
50
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_SwipeActionsRow.SwipeActionsRow, {
|
|
47
51
|
testID: testID,
|
|
48
52
|
actions: [
|
|
53
|
+
// "Bar": put this point on the floating action bar.
|
|
54
|
+
{
|
|
55
|
+
label: "Bar",
|
|
56
|
+
onPress: onArm
|
|
57
|
+
},
|
|
49
58
|
// "Dup" over "Copy": on a row full of text, "Copy" reads as
|
|
50
59
|
// copy-to-clipboard.
|
|
51
60
|
{
|
|
@@ -57,21 +66,26 @@ function SnapshotRow({
|
|
|
57
66
|
background: _tmTheme.tm.color.danger,
|
|
58
67
|
color: _tmTheme.tm.color.onDanger
|
|
59
68
|
}],
|
|
60
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.
|
|
69
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
61
70
|
style: styles.row,
|
|
62
|
-
|
|
63
|
-
activeOpacity: 0.7,
|
|
64
|
-
accessibilityRole: "button",
|
|
65
|
-
accessibilityLabel: `${meta.name}. ${describe(meta)}. Open restore preview`,
|
|
66
|
-
testID: testID,
|
|
67
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
71
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
68
72
|
style: styles.text,
|
|
73
|
+
onPress: onOpen,
|
|
74
|
+
activeOpacity: 0.7,
|
|
75
|
+
accessibilityRole: "button",
|
|
76
|
+
accessibilityLabel: `${meta.name}. ${describe(meta)}. Open restore preview`,
|
|
77
|
+
testID: testID,
|
|
69
78
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
70
79
|
style: styles.name,
|
|
71
80
|
numberOfLines: 1,
|
|
72
81
|
testID: `${testID}-name`,
|
|
73
82
|
children: meta.name
|
|
74
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
83
|
+
}), armed ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
84
|
+
style: styles.armed,
|
|
85
|
+
numberOfLines: 1,
|
|
86
|
+
testID: `${testID}-armed`,
|
|
87
|
+
children: "On the action bar"
|
|
88
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
75
89
|
style: styles.meta,
|
|
76
90
|
numberOfLines: 1,
|
|
77
91
|
testID: `${testID}-meta`,
|
|
@@ -90,7 +104,7 @@ function SnapshotRow({
|
|
|
90
104
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
91
105
|
onPress: onRestore,
|
|
92
106
|
disabled: busy,
|
|
93
|
-
style: busy ? styles.actionDisabled : null,
|
|
107
|
+
style: [styles.restore, busy ? styles.actionDisabled : null],
|
|
94
108
|
accessibilityRole: "button",
|
|
95
109
|
accessibilityLabel: meta.baseline ? "Wipe and reload" : "Restore",
|
|
96
110
|
testID: `${testID}-restore`,
|
|
@@ -109,12 +123,17 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
109
123
|
flexDirection: "row",
|
|
110
124
|
alignItems: "center",
|
|
111
125
|
gap: 10,
|
|
112
|
-
paddingHorizontal: _tmTheme.tm.textInset
|
|
113
|
-
paddingVertical: 11
|
|
126
|
+
paddingHorizontal: _tmTheme.tm.textInset
|
|
114
127
|
},
|
|
128
|
+
// The text block is the tallest child, so its padding sets the row height
|
|
129
|
+
// and the open target spans the full row.
|
|
115
130
|
text: {
|
|
116
131
|
flex: 1,
|
|
117
|
-
minWidth: 0
|
|
132
|
+
minWidth: 0,
|
|
133
|
+
paddingVertical: 11
|
|
134
|
+
},
|
|
135
|
+
restore: {
|
|
136
|
+
justifyContent: "center"
|
|
118
137
|
},
|
|
119
138
|
name: {
|
|
120
139
|
color: _tmTheme.tm.color.label,
|
|
@@ -133,6 +152,11 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
133
152
|
fontSize: _tmTheme.tm.font.footnote,
|
|
134
153
|
marginTop: 2
|
|
135
154
|
},
|
|
155
|
+
armed: {
|
|
156
|
+
color: _tmTheme.tm.color.accent,
|
|
157
|
+
fontSize: _tmTheme.tm.font.footnote,
|
|
158
|
+
marginTop: 2
|
|
159
|
+
},
|
|
136
160
|
route: {
|
|
137
161
|
color: _tmTheme.tm.color.labelTertiary,
|
|
138
162
|
fontFamily: _tmTheme.TM_MONO,
|
|
@@ -5,124 +5,168 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.SourcesStrip = SourcesStrip;
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
+
var _primitives = require("../../ui/primitives");
|
|
8
10
|
var _tmTheme = require("../../ui/tmTheme");
|
|
9
11
|
var _sources = require("../sources");
|
|
10
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
13
|
/**
|
|
12
|
-
* Sources — one
|
|
14
|
+
* Sources — one settings row per source, each with a switch.
|
|
13
15
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* The iOS Settings shape the rest of the tool uses, because that is what a
|
|
17
|
+
* list of things you can turn on and off looks like: icon, name, a line of
|
|
18
|
+
* status, and a switch at the trailing edge. Off means every capture and
|
|
19
|
+
* restore leaves that source alone, for the store whose full replace the
|
|
20
|
+
* app cannot survive.
|
|
21
|
+
*
|
|
22
|
+
* Only sources the app has wired are listed; a library the app does not use
|
|
23
|
+
* is not a choice to make. Icons keep full brightness in every state. Under
|
|
24
|
+
* each name is what the source holds, in plain words; a short state line
|
|
25
|
+
* appears only when something is off or capture-only.
|
|
18
26
|
*/
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
/**
|
|
29
|
+
* What each source holds, in words for someone who has never heard of the
|
|
30
|
+
* library. The library name is the row title; this line is the meaning.
|
|
31
|
+
*/const DESCRIPTIONS = {
|
|
32
|
+
storage: "Settings, sign-in and other things saved on the device",
|
|
33
|
+
redux: "Data the whole app shares between screens",
|
|
34
|
+
zustand: "Data the whole app shares between screens",
|
|
35
|
+
jotai: "Small pieces of data screens share",
|
|
36
|
+
query: "Data the app loaded from the server",
|
|
37
|
+
route: "Which screen you were on"
|
|
38
|
+
};
|
|
39
|
+
function describe(source) {
|
|
40
|
+
return DESCRIPTIONS[source.id] ?? "App data";
|
|
24
41
|
}
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The short state under the description, when there is one worth saying.
|
|
45
|
+
* "Working" is the default and needs no line.
|
|
46
|
+
*/
|
|
47
|
+
function stateLine(source) {
|
|
48
|
+
if (!source.enabled) return {
|
|
49
|
+
text: "Off · left as it is",
|
|
50
|
+
tone: "off"
|
|
51
|
+
};
|
|
52
|
+
if (!source.canRestore) return {
|
|
53
|
+
text: "Can be saved but not put back",
|
|
54
|
+
tone: "warn"
|
|
55
|
+
};
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function SourceRow({
|
|
59
|
+
source,
|
|
60
|
+
onToggle
|
|
27
61
|
}) {
|
|
28
62
|
const Icon = (0, _sources.sourceIcon)(source.id);
|
|
29
|
-
const
|
|
63
|
+
const description = describe(source);
|
|
64
|
+
const state = stateLine(source);
|
|
30
65
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
31
|
-
style: styles.
|
|
66
|
+
style: styles.row,
|
|
32
67
|
testID: `time-machine-source-${source.id}`,
|
|
33
|
-
accessibilityLabel: `${source.label}: ${
|
|
34
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
35
|
-
style:
|
|
36
|
-
children:
|
|
37
|
-
size:
|
|
38
|
-
bgColor: _tmTheme.tm.color.
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
68
|
+
accessibilityLabel: `${source.label}: ${description}${state ? `. ${state.text}` : ""}`,
|
|
69
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
70
|
+
style: styles.disc,
|
|
71
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Icon, {
|
|
72
|
+
size: 22,
|
|
73
|
+
bgColor: _tmTheme.tm.color.disc
|
|
74
|
+
})
|
|
75
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
76
|
+
style: styles.text,
|
|
77
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
78
|
+
style: styles.label,
|
|
79
|
+
numberOfLines: 1,
|
|
80
|
+
testID: `time-machine-source-${source.id}-label`,
|
|
81
|
+
children: source.label
|
|
82
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
83
|
+
style: styles.description,
|
|
84
|
+
numberOfLines: 2,
|
|
85
|
+
testID: `time-machine-source-${source.id}-description`,
|
|
86
|
+
children: description
|
|
87
|
+
}), state ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
88
|
+
style: [styles.state, styles[`state_${state.tone}`]],
|
|
89
|
+
numberOfLines: 1,
|
|
90
|
+
testID: state.tone === "off" ? `time-machine-source-${source.id}-off` : `time-machine-source-${source.id}-detail`,
|
|
91
|
+
children: state.text
|
|
43
92
|
}) : null]
|
|
44
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
93
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
94
|
+
testID: `time-machine-source-${source.id}-switch`,
|
|
95
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.NightSwitch, {
|
|
96
|
+
value: source.enabled
|
|
97
|
+
// The desktop mirror shows the device's setting and cannot flip it.
|
|
98
|
+
,
|
|
99
|
+
disabled: !onToggle,
|
|
100
|
+
onValueChange: next => onToggle?.(source.id, next)
|
|
101
|
+
})
|
|
49
102
|
})]
|
|
50
103
|
});
|
|
51
104
|
}
|
|
52
105
|
function SourcesStrip({
|
|
53
|
-
sources
|
|
106
|
+
sources,
|
|
107
|
+
onToggle
|
|
54
108
|
}) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
children: "No state tools
|
|
68
|
-
})
|
|
69
|
-
})
|
|
109
|
+
// A source the app has not wired (no Redux Provider, no Zustand stores)
|
|
110
|
+
// is not listed: there is nothing to capture and nothing to switch off.
|
|
111
|
+
const wired = sources.filter(source => source.canCapture);
|
|
112
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_primitives.Group, {
|
|
113
|
+
children: [wired.map(source => /*#__PURE__*/(0, _jsxRuntime.jsx)(SourceRow, {
|
|
114
|
+
source: source,
|
|
115
|
+
onToggle: onToggle
|
|
116
|
+
}, source.id)), wired.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
117
|
+
style: styles.row,
|
|
118
|
+
testID: "time-machine-sources-empty",
|
|
119
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
120
|
+
style: styles.description,
|
|
121
|
+
children: "No state tools are set up in this app."
|
|
122
|
+
})
|
|
123
|
+
}) : null]
|
|
70
124
|
});
|
|
71
125
|
}
|
|
72
126
|
const styles = _reactNative.StyleSheet.create({
|
|
73
|
-
|
|
74
|
-
marginHorizontal: _tmTheme.tm.gutter,
|
|
75
|
-
backgroundColor: _tmTheme.tm.color.group,
|
|
76
|
-
borderRadius: _tmTheme.tm.radius.group,
|
|
77
|
-
paddingVertical: 12
|
|
78
|
-
},
|
|
79
|
-
strip: {
|
|
127
|
+
row: {
|
|
80
128
|
flexDirection: "row",
|
|
81
|
-
alignItems: "flex-start",
|
|
82
|
-
paddingHorizontal: _tmTheme.tm.textInset,
|
|
83
|
-
gap: 18
|
|
84
|
-
},
|
|
85
|
-
tile: {
|
|
86
129
|
alignItems: "center",
|
|
87
|
-
gap:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
width: 78
|
|
130
|
+
gap: 12,
|
|
131
|
+
minHeight: _tmTheme.tm.rowMinHeight,
|
|
132
|
+
paddingHorizontal: _tmTheme.tm.textInset,
|
|
133
|
+
paddingVertical: 10
|
|
92
134
|
},
|
|
93
135
|
disc: {
|
|
94
|
-
width:
|
|
95
|
-
height:
|
|
136
|
+
width: 36,
|
|
137
|
+
height: 36,
|
|
96
138
|
borderRadius: _tmTheme.tm.radius.pill,
|
|
97
|
-
backgroundColor: _tmTheme.tm.color.
|
|
139
|
+
backgroundColor: _tmTheme.tm.color.disc,
|
|
98
140
|
alignItems: "center",
|
|
99
141
|
justifyContent: "center"
|
|
100
142
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
position: "absolute",
|
|
106
|
-
top: 2,
|
|
107
|
-
right: 2,
|
|
108
|
-
width: 10,
|
|
109
|
-
height: 10,
|
|
110
|
-
borderRadius: 5,
|
|
111
|
-
// Ring it in the card color so it reads as a badge, not a smudge.
|
|
112
|
-
borderWidth: 2,
|
|
113
|
-
borderColor: _tmTheme.tm.color.group
|
|
143
|
+
text: {
|
|
144
|
+
flex: 1,
|
|
145
|
+
minWidth: 0,
|
|
146
|
+
gap: 2
|
|
114
147
|
},
|
|
115
148
|
label: {
|
|
116
|
-
color: _tmTheme.tm.color.
|
|
149
|
+
color: _tmTheme.tm.color.label,
|
|
150
|
+
fontSize: _tmTheme.tm.font.row,
|
|
151
|
+
fontWeight: "500",
|
|
152
|
+
letterSpacing: _tmTheme.tm.tracking.row
|
|
153
|
+
},
|
|
154
|
+
description: {
|
|
155
|
+
fontSize: _tmTheme.tm.font.footnote,
|
|
156
|
+
lineHeight: _tmTheme.tm.font.footnote * 1.3,
|
|
157
|
+
color: _tmTheme.tm.color.labelSecondary
|
|
158
|
+
},
|
|
159
|
+
state: {
|
|
117
160
|
fontSize: _tmTheme.tm.font.mono,
|
|
118
|
-
|
|
161
|
+
lineHeight: _tmTheme.tm.font.mono * 1.3,
|
|
162
|
+
fontWeight: "600",
|
|
163
|
+
marginTop: 1
|
|
119
164
|
},
|
|
120
|
-
|
|
165
|
+
state_off: {
|
|
121
166
|
color: _tmTheme.tm.color.labelTertiary
|
|
122
167
|
},
|
|
123
|
-
|
|
124
|
-
color: _tmTheme.tm.color.
|
|
125
|
-
fontSize: _tmTheme.tm.font.footnote
|
|
168
|
+
state_warn: {
|
|
169
|
+
color: _tmTheme.tm.color.warning
|
|
126
170
|
}
|
|
127
171
|
});
|
|
128
172
|
//# sourceMappingURL=SourcesStrip.js.map
|
|
@@ -65,7 +65,9 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
65
65
|
minHeight: 46,
|
|
66
66
|
paddingHorizontal: _tmTheme.tm.textInset,
|
|
67
67
|
backgroundColor: _tmTheme.tm.color.group,
|
|
68
|
-
borderRadius: _tmTheme.tm.radius.group
|
|
68
|
+
borderRadius: _tmTheme.tm.radius.group,
|
|
69
|
+
borderWidth: 1,
|
|
70
|
+
borderColor: _tmTheme.tm.color.groupBorder
|
|
69
71
|
},
|
|
70
72
|
text: {
|
|
71
73
|
flex: 1,
|
|
@@ -8,6 +8,7 @@ var _react = require("react");
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
10
|
var _core = require("@buoy-gg/core");
|
|
11
|
+
var _HeaderActions = require("../HeaderActions");
|
|
11
12
|
var _primitives = require("../../ui/primitives");
|
|
12
13
|
var _tmTheme = require("../../ui/tmTheme");
|
|
13
14
|
var _timeMachineStore = require("../../store/timeMachineStore");
|
|
@@ -384,10 +385,14 @@ function RestorePreviewScreen({
|
|
|
384
385
|
(0, _core.useToolHeader)(hosted ? {
|
|
385
386
|
title: "Restore Preview",
|
|
386
387
|
subtitle: rightMeta?.name ?? "preview",
|
|
387
|
-
onBack
|
|
388
|
+
onBack,
|
|
389
|
+
// BAR from here arms the point being previewed.
|
|
390
|
+
actions: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HeaderActions.TimeMachineHeaderActions, {
|
|
391
|
+
armId: rightMeta?.id
|
|
392
|
+
})
|
|
388
393
|
} : null,
|
|
389
394
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
390
|
-
[hosted, rightMeta?.name]);
|
|
395
|
+
[hosted, rightMeta?.name, rightMeta?.id]);
|
|
391
396
|
const {
|
|
392
397
|
rows,
|
|
393
398
|
blockedSources
|
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
Object.defineProperty(exports, "ROUTE_SOURCE_ID", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _sourceIds.ROUTE_SOURCE_ID;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
7
12
|
exports.isAttached = isAttached;
|
|
8
13
|
exports.sourceIcon = sourceIcon;
|
|
9
14
|
exports.useSourceStatuses = useSourceStatuses;
|
|
10
15
|
var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
|
|
16
|
+
var _react = require("react");
|
|
11
17
|
var _useTimeMachine = require("../hooks/useTimeMachine");
|
|
12
18
|
var _routeRestore = require("../store/routeRestore");
|
|
19
|
+
var _sourceIds = require("../store/sourceIds");
|
|
20
|
+
var _sourceSettings = require("../store/sourceSettings");
|
|
13
21
|
/**
|
|
14
22
|
* What Time Machine can capture from, and how healthy each source is.
|
|
15
23
|
*
|
|
@@ -38,26 +46,29 @@ const ICONS = {
|
|
|
38
46
|
function sourceIcon(id) {
|
|
39
47
|
return ICONS[id] ?? _floatingToolsCore.TimeMachineIcon;
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
/** Id of the route tile — a device capability, not a snapshot provider. */
|
|
43
|
-
const ROUTE_SOURCE_ID = exports.ROUTE_SOURCE_ID = "route";
|
|
44
49
|
function useSourceStatuses() {
|
|
45
50
|
const {
|
|
46
51
|
remoteProviders,
|
|
47
52
|
remoteRoute
|
|
48
53
|
} = (0, _useTimeMachine.useTimeMachineState)();
|
|
49
54
|
const localProviders = (0, _useTimeMachine.useSnapshotProviders)();
|
|
50
|
-
const
|
|
55
|
+
const disabled = (0, _react.useSyncExternalStore)(_sourceSettings.subscribeSourceSettings, _sourceSettings.getDisabledSources, _sourceSettings.getDisabledSources);
|
|
56
|
+
const providers = remoteProviders ? remoteProviders.map(p => ({
|
|
57
|
+
...p,
|
|
58
|
+
enabled: p.enabled !== false
|
|
59
|
+
})) : localProviders.map(p => ({
|
|
51
60
|
id: p.id,
|
|
52
61
|
label: p.label,
|
|
53
|
-
...p.status()
|
|
62
|
+
...p.status(),
|
|
63
|
+
enabled: !disabled.has(p.id)
|
|
54
64
|
}));
|
|
55
65
|
const routeAvailable = remoteProviders ? remoteRoute?.available === true : (0, _routeRestore.canNavigate)();
|
|
56
66
|
return [...providers, {
|
|
57
|
-
id: ROUTE_SOURCE_ID,
|
|
67
|
+
id: _sourceIds.ROUTE_SOURCE_ID,
|
|
58
68
|
label: "Route",
|
|
59
69
|
canCapture: routeAvailable,
|
|
60
70
|
canRestore: routeAvailable,
|
|
71
|
+
enabled: remoteProviders ? remoteRoute?.enabled !== false : !disabled.has(_sourceIds.ROUTE_SOURCE_ID),
|
|
61
72
|
// No live pathname here: these screens only re-render on store changes,
|
|
62
73
|
// so a route printed as status goes stale the moment you navigate with
|
|
63
74
|
// the tool still open. Each restore point shows the route it holds.
|
|
@@ -67,6 +78,6 @@ function useSourceStatuses() {
|
|
|
67
78
|
|
|
68
79
|
/** A source is "attached" when a snapshot would actually pick it up. */
|
|
69
80
|
function isAttached(source) {
|
|
70
|
-
return source.canCapture;
|
|
81
|
+
return source.canCapture && source.enabled;
|
|
71
82
|
}
|
|
72
83
|
//# sourceMappingURL=sources.js.map
|