@agent-native/core-corpus 0.210.2 → 0.211.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- package/package.json +1 -1
|
@@ -165,8 +165,7 @@ import {
|
|
|
165
165
|
loadRecordFirstFiles,
|
|
166
166
|
RecordFirstFileMissingError,
|
|
167
167
|
recordFirstFilesKey,
|
|
168
|
-
|
|
169
|
-
recordFirstMediaFlags,
|
|
168
|
+
shouldPollVideoStorageStatus,
|
|
170
169
|
stagedIdAfterFailure,
|
|
171
170
|
transferRecordFirstFiles,
|
|
172
171
|
type RecordFirstFile,
|
|
@@ -209,11 +208,13 @@ import {
|
|
|
209
208
|
ScreenRecordingPermissionError,
|
|
210
209
|
} from "./lib/recording-preflight";
|
|
211
210
|
import {
|
|
211
|
+
mapRecordFirstFilesForRecovery,
|
|
212
212
|
reconcileRecordingRecovery,
|
|
213
213
|
recordingRecoveryKey,
|
|
214
214
|
shouldShowRecordingRecoveryBanner,
|
|
215
215
|
type PendingDesktopUpload,
|
|
216
216
|
type PendingNativeUpload,
|
|
217
|
+
type RecordFirstFileUploadError,
|
|
217
218
|
type RecoverySnapshot,
|
|
218
219
|
} from "./lib/recording-recovery";
|
|
219
220
|
import {
|
|
@@ -1391,7 +1392,7 @@ export function App({
|
|
|
1391
1392
|
}, [videoStorageIdentity, serverUrl, signedInAs]);
|
|
1392
1393
|
|
|
1393
1394
|
const refreshVideoStorageStatus = useCallback(async () => {
|
|
1394
|
-
if (authStatus !== "authed"
|
|
1395
|
+
if (authStatus !== "authed") {
|
|
1395
1396
|
hostedClipLimitMsRef.current = null;
|
|
1396
1397
|
setVideoStorageStatus("configured");
|
|
1397
1398
|
return true;
|
|
@@ -1410,13 +1411,7 @@ export function App({
|
|
|
1410
1411
|
hostedClipLimitMsRef.current = maxClipDurationMs;
|
|
1411
1412
|
setVideoStorageStatus(probe);
|
|
1412
1413
|
return probe === "configured";
|
|
1413
|
-
}, [
|
|
1414
|
-
authStatus,
|
|
1415
|
-
localRecordingMode,
|
|
1416
|
-
serverUrl,
|
|
1417
|
-
signedInAs,
|
|
1418
|
-
videoStorageIdentity,
|
|
1419
|
-
]);
|
|
1414
|
+
}, [authStatus, serverUrl, signedInAs, videoStorageIdentity]);
|
|
1420
1415
|
|
|
1421
1416
|
useEffect(() => {
|
|
1422
1417
|
void refreshVideoStorageStatus();
|
|
@@ -1424,14 +1419,9 @@ export function App({
|
|
|
1424
1419
|
|
|
1425
1420
|
useEffect(() => {
|
|
1426
1421
|
if (
|
|
1427
|
-
authStatus
|
|
1428
|
-
|
|
1429
|
-
(videoStorageStatus !== "missing" &&
|
|
1430
|
-
videoStorageStatus !== "checking" &&
|
|
1431
|
-
videoStorageStatus !== "unavailable")
|
|
1432
|
-
) {
|
|
1422
|
+
!shouldPollVideoStorageStatus(authStatus === "authed", videoStorageStatus)
|
|
1423
|
+
)
|
|
1433
1424
|
return;
|
|
1434
|
-
}
|
|
1435
1425
|
|
|
1436
1426
|
let inFlight = false;
|
|
1437
1427
|
const tick = () => {
|
|
@@ -1450,12 +1440,7 @@ export function App({
|
|
|
1450
1440
|
window.clearInterval(interval);
|
|
1451
1441
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1452
1442
|
};
|
|
1453
|
-
}, [
|
|
1454
|
-
authStatus,
|
|
1455
|
-
localRecordingMode,
|
|
1456
|
-
refreshVideoStorageStatus,
|
|
1457
|
-
videoStorageStatus,
|
|
1458
|
-
]);
|
|
1443
|
+
}, [authStatus, refreshVideoStorageStatus, videoStorageStatus]);
|
|
1459
1444
|
|
|
1460
1445
|
// Mirrors the web recorder: a hosted take stops itself at the clip cap
|
|
1461
1446
|
// instead of failing the upload past the server's size limit. The cap the
|
|
@@ -3201,9 +3186,6 @@ export function App({
|
|
|
3201
3186
|
const recorderStopListenerPromiseRef = useRef<Promise<() => void> | null>(
|
|
3202
3187
|
null,
|
|
3203
3188
|
);
|
|
3204
|
-
const recordedWithoutStorageRef = useRef(false);
|
|
3205
|
-
// The record-first list a capture belongs to, fixed when capture starts.
|
|
3206
|
-
const recordFirstKeyAtStartRef = useRef<string | null>(null);
|
|
3207
3189
|
const finishRecordingStopRef = useRef<
|
|
3208
3190
|
(handle: RecorderHandle, recordingId?: string | null) => Promise<void>
|
|
3209
3191
|
>(async () => {});
|
|
@@ -3597,7 +3579,7 @@ export function App({
|
|
|
3597
3579
|
>([]);
|
|
3598
3580
|
const [recordFirstError, setRecordFirstError] = useState<string | null>(null);
|
|
3599
3581
|
const [recordFirstFileErrors, setRecordFirstFileErrors] = useState<
|
|
3600
|
-
Record<string,
|
|
3582
|
+
Record<string, RecordFirstFileUploadError>
|
|
3601
3583
|
>({});
|
|
3602
3584
|
const [recordFirstUploading, setRecordFirstUploading] = useState(false);
|
|
3603
3585
|
useEffect(() => {
|
|
@@ -3636,17 +3618,6 @@ export function App({
|
|
|
3636
3618
|
},
|
|
3637
3619
|
[recordFirstKey, unclaimedRecordFirstKey],
|
|
3638
3620
|
);
|
|
3639
|
-
const rememberRecordFirstFile = useCallback(
|
|
3640
|
-
(file: RecordFirstFile, key: string) => {
|
|
3641
|
-
// `key` is the account signed in when capture started, never whoever
|
|
3642
|
-
// is signed in at Stop, so a switch mid-recording cannot reassign it.
|
|
3643
|
-
changeQueuedFiles(key, (files) => [
|
|
3644
|
-
...files.filter((f) => f.path !== file.path),
|
|
3645
|
-
file,
|
|
3646
|
-
]);
|
|
3647
|
-
},
|
|
3648
|
-
[changeQueuedFiles],
|
|
3649
|
-
);
|
|
3650
3621
|
const forgetRecordFirstFile = useCallback(
|
|
3651
3622
|
(path: string) => {
|
|
3652
3623
|
// Removes the list entry only; the file on disk is never touched.
|
|
@@ -3828,24 +3799,6 @@ export function App({
|
|
|
3828
3799
|
folderPath: stopResult.localFolder,
|
|
3829
3800
|
files: stopResult.localFiles ?? [],
|
|
3830
3801
|
});
|
|
3831
|
-
const queueKey = recordFirstKeyAtStartRef.current;
|
|
3832
|
-
if (recordedWithoutStorageRef.current && queueKey) {
|
|
3833
|
-
for (const file of recordFirstFilesToQueue(
|
|
3834
|
-
stopResult.localFiles ?? [],
|
|
3835
|
-
)) {
|
|
3836
|
-
rememberRecordFirstFile(
|
|
3837
|
-
{
|
|
3838
|
-
...file,
|
|
3839
|
-
...recordFirstMediaFlags(file.role, {
|
|
3840
|
-
hasAudio: micOn || systemAudioOn,
|
|
3841
|
-
hasCamera: mode !== "screen",
|
|
3842
|
-
}),
|
|
3843
|
-
savedAt: new Date().toISOString(),
|
|
3844
|
-
},
|
|
3845
|
-
queueKey,
|
|
3846
|
-
);
|
|
3847
|
-
}
|
|
3848
|
-
}
|
|
3849
3802
|
emit("clips:native-upload-finished", {
|
|
3850
3803
|
recordingId: stopResult.recordingId,
|
|
3851
3804
|
ok: true,
|
|
@@ -3867,7 +3820,7 @@ export function App({
|
|
|
3867
3820
|
ok: false,
|
|
3868
3821
|
error: err instanceof Error ? err.message : String(err),
|
|
3869
3822
|
},
|
|
3870
|
-
localRecordingMode !== "off"
|
|
3823
|
+
localRecordingMode !== "off",
|
|
3871
3824
|
);
|
|
3872
3825
|
emit("clips:native-upload-finished", {
|
|
3873
3826
|
recordingId: stoppingRecordingId ?? undefined,
|
|
@@ -4242,19 +4195,7 @@ export function App({
|
|
|
4242
4195
|
bubbleStreamRef.current = null;
|
|
4243
4196
|
setBubbleSessionEpoch((epoch) => epoch + 1);
|
|
4244
4197
|
}
|
|
4245
|
-
|
|
4246
|
-
// clip is written to Movies/Clips as it records, then uploads once
|
|
4247
|
-
// storage connects.
|
|
4248
|
-
const recordingLocalMode = effectiveLocalRecordingMode(
|
|
4249
|
-
localRecordingMode,
|
|
4250
|
-
videoStorageStatus,
|
|
4251
|
-
);
|
|
4252
|
-
const recordLocallyUntilStorage = recordingLocalMode !== localRecordingMode;
|
|
4253
|
-
recordedWithoutStorageRef.current = recordLocallyUntilStorage;
|
|
4254
|
-
recordFirstKeyAtStartRef.current = recordFirstFilesKey(
|
|
4255
|
-
originForServer(serverUrl),
|
|
4256
|
-
signedInAs,
|
|
4257
|
-
);
|
|
4198
|
+
const recordingLocalMode = effectiveLocalRecordingMode(localRecordingMode);
|
|
4258
4199
|
setRecError(null);
|
|
4259
4200
|
setLocalRecordingNotice(null);
|
|
4260
4201
|
setShareLinkNotice(null);
|
|
@@ -4264,6 +4205,7 @@ export function App({
|
|
|
4264
4205
|
mode,
|
|
4265
4206
|
source,
|
|
4266
4207
|
localRecordingMode,
|
|
4208
|
+
videoStorageStatus,
|
|
4267
4209
|
cameraOn,
|
|
4268
4210
|
micOn,
|
|
4269
4211
|
});
|
|
@@ -4875,8 +4817,16 @@ export function App({
|
|
|
4875
4817
|
? "Start recording"
|
|
4876
4818
|
: "Start local recording";
|
|
4877
4819
|
|
|
4820
|
+
const localRecoveryFiles = mapRecordFirstFilesForRecovery(
|
|
4821
|
+
[...recordFirstFiles, ...unclaimedRecordFirstFiles],
|
|
4822
|
+
recordFirstFileErrors,
|
|
4823
|
+
);
|
|
4878
4824
|
const recoveryProps: RecordingRecoveryProps = {
|
|
4879
4825
|
uploads: pendingUploads,
|
|
4826
|
+
localFiles: localRecoveryFiles,
|
|
4827
|
+
localFilesError: recordFirstError,
|
|
4828
|
+
localFilesUploading: recordFirstUploading,
|
|
4829
|
+
localFilesStorageStatus: videoStorageStatus,
|
|
4880
4830
|
lookupErrors: recoverySnapshot.errors,
|
|
4881
4831
|
actionErrors: recoveryActionErrors,
|
|
4882
4832
|
refreshing: recoveryRefreshing,
|
|
@@ -4897,6 +4847,11 @@ export function App({
|
|
|
4897
4847
|
onRetry: retryPendingUpload,
|
|
4898
4848
|
onCancelRetry: cancelPendingUploadRetry,
|
|
4899
4849
|
onOpenFolder: openPendingUploadFolder,
|
|
4850
|
+
onRetryLocalFiles: () =>
|
|
4851
|
+
void uploadRecordFirstFilesRef.current({ includeUnclaimed: true }),
|
|
4852
|
+
onRefreshStorage: () => void refreshVideoStorageStatus(),
|
|
4853
|
+
onConnectLocalStorage: () => openVideoStorageSetup(serverUrl),
|
|
4854
|
+
onForgetLocalFile: forgetRecordFirstFile,
|
|
4900
4855
|
onReviewFiles: (key) => {
|
|
4901
4856
|
void invoke("native_fullscreen_open_drafts_folder").catch((error) => {
|
|
4902
4857
|
setRecoveryActionErrors((errors) => ({
|
|
@@ -4921,7 +4876,9 @@ export function App({
|
|
|
4921
4876
|
delete next[key];
|
|
4922
4877
|
return next;
|
|
4923
4878
|
});
|
|
4924
|
-
void openExternal(
|
|
4879
|
+
void openExternal(
|
|
4880
|
+
`${targetServerUrl.replace(/\/+$/, "")}/record?connectStorage=1`,
|
|
4881
|
+
).catch((error) => {
|
|
4925
4882
|
setRecoveryActionErrors((errors) => ({
|
|
4926
4883
|
...errors,
|
|
4927
4884
|
[key]: error instanceof Error ? error.message : String(error),
|
|
@@ -4932,6 +4889,7 @@ export function App({
|
|
|
4932
4889
|
const pendingUploadBanner = shouldShowRecordingRecoveryBanner(
|
|
4933
4890
|
popoverView,
|
|
4934
4891
|
authStatus === "authed",
|
|
4892
|
+
localRecoveryFiles.length > 0 || !!recordFirstError,
|
|
4935
4893
|
) ? (
|
|
4936
4894
|
<RecordingRecovery
|
|
4937
4895
|
{...recoveryProps}
|
|
@@ -5492,31 +5450,6 @@ export function App({
|
|
|
5492
5450
|
</button>
|
|
5493
5451
|
) : null}
|
|
5494
5452
|
|
|
5495
|
-
{recordFirstFiles.length + unclaimedRecordFirstFiles.length > 0 ||
|
|
5496
|
-
recordFirstError ? (
|
|
5497
|
-
<RecordFirstUploadsBanner
|
|
5498
|
-
count={recordFirstFiles.length + unclaimedRecordFirstFiles.length}
|
|
5499
|
-
storageStatus={videoStorageStatus}
|
|
5500
|
-
signedIn={!!signedInAs}
|
|
5501
|
-
uploading={recordFirstUploading}
|
|
5502
|
-
error={recordFirstError}
|
|
5503
|
-
failedFiles={[
|
|
5504
|
-
...recordFirstFiles,
|
|
5505
|
-
...unclaimedRecordFirstFiles,
|
|
5506
|
-
].flatMap((file) =>
|
|
5507
|
-
recordFirstFileErrors[file.path]
|
|
5508
|
-
? [{ file, ...recordFirstFileErrors[file.path]! }]
|
|
5509
|
-
: [],
|
|
5510
|
-
)}
|
|
5511
|
-
onUpload={() =>
|
|
5512
|
-
void uploadRecordFirstFilesRef.current({
|
|
5513
|
-
includeUnclaimed: true,
|
|
5514
|
-
})
|
|
5515
|
-
}
|
|
5516
|
-
onCheckStorage={() => void refreshVideoStorageStatus()}
|
|
5517
|
-
onForget={forgetRecordFirstFile}
|
|
5518
|
-
/>
|
|
5519
|
-
) : null}
|
|
5520
5453
|
{recError ? (
|
|
5521
5454
|
recError === MACOS_UPDATE_RESTART_MESSAGE ? (
|
|
5522
5455
|
<UpdateRestartBanner message={recError} />
|
|
@@ -5719,96 +5652,6 @@ function UpdateRestartBanner({ message }: { message: string }) {
|
|
|
5719
5652
|
const RECORD_FIRST_LIST_UNREADABLE =
|
|
5720
5653
|
"Clips couldn't read its list of recordings waiting to upload. They are still in Movies/Clips.";
|
|
5721
5654
|
|
|
5722
|
-
function RecordFirstUploadsBanner({
|
|
5723
|
-
count,
|
|
5724
|
-
storageStatus,
|
|
5725
|
-
signedIn,
|
|
5726
|
-
uploading,
|
|
5727
|
-
error,
|
|
5728
|
-
failedFiles,
|
|
5729
|
-
onUpload,
|
|
5730
|
-
onCheckStorage,
|
|
5731
|
-
onForget,
|
|
5732
|
-
}: {
|
|
5733
|
-
count: number;
|
|
5734
|
-
storageStatus: VideoStorageStatus;
|
|
5735
|
-
signedIn: boolean;
|
|
5736
|
-
uploading: boolean;
|
|
5737
|
-
error: string | null;
|
|
5738
|
-
failedFiles: Array<{
|
|
5739
|
-
file: RecordFirstFile;
|
|
5740
|
-
message: string;
|
|
5741
|
-
missing: boolean;
|
|
5742
|
-
}>;
|
|
5743
|
-
onUpload: () => void;
|
|
5744
|
-
onCheckStorage: () => void;
|
|
5745
|
-
onForget: (path: string) => void;
|
|
5746
|
-
}) {
|
|
5747
|
-
const recordings = count === 1 ? "1 recording" : `${count} recordings`;
|
|
5748
|
-
const storageCopy =
|
|
5749
|
-
storageStatus === "configured"
|
|
5750
|
-
? "Saved in Movies/Clips. Upload them to get share links."
|
|
5751
|
-
: storageStatus === "missing"
|
|
5752
|
-
? "Saved in Movies/Clips. Connect storage and they upload."
|
|
5753
|
-
: storageStatus === "unavailable"
|
|
5754
|
-
? "Storage authorization could not be checked. Retry to check again; recordings remain in Movies/Clips."
|
|
5755
|
-
: "Checking whether storage can upload clips.";
|
|
5756
|
-
const checkingStorage =
|
|
5757
|
-
storageStatus === "checking" || storageStatus === "unavailable";
|
|
5758
|
-
return (
|
|
5759
|
-
<>
|
|
5760
|
-
<div className="storage-flow-banner" role="status">
|
|
5761
|
-
<div className="storage-flow-icon" aria-hidden>
|
|
5762
|
-
<IconUpload size={17} stroke={1.8} />
|
|
5763
|
-
</div>
|
|
5764
|
-
<div className="storage-flow-copy">
|
|
5765
|
-
<div className="storage-flow-title">
|
|
5766
|
-
{uploading
|
|
5767
|
-
? `Uploading ${recordings}…`
|
|
5768
|
-
: `${recordings} saved on this Mac, not uploaded`}
|
|
5769
|
-
</div>
|
|
5770
|
-
<div className="storage-flow-sub">
|
|
5771
|
-
{error ? <div>{error}</div> : null}
|
|
5772
|
-
<div>{storageCopy}</div>
|
|
5773
|
-
</div>
|
|
5774
|
-
</div>
|
|
5775
|
-
{count > 0 ? (
|
|
5776
|
-
<button
|
|
5777
|
-
type="button"
|
|
5778
|
-
className="storage-flow-connect"
|
|
5779
|
-
disabled={uploading || !signedIn}
|
|
5780
|
-
title={signedIn ? undefined : "Sign in to upload"}
|
|
5781
|
-
onClick={checkingStorage ? onCheckStorage : onUpload}
|
|
5782
|
-
>
|
|
5783
|
-
<IconUpload size={14} stroke={2} />
|
|
5784
|
-
{storageStatus === "configured"
|
|
5785
|
-
? "Upload now"
|
|
5786
|
-
: storageStatus === "missing"
|
|
5787
|
-
? "Connect"
|
|
5788
|
-
: storageStatus === "unavailable"
|
|
5789
|
-
? "Retry"
|
|
5790
|
-
: "Check storage"}
|
|
5791
|
-
</button>
|
|
5792
|
-
) : null}
|
|
5793
|
-
</div>
|
|
5794
|
-
{failedFiles.map(({ file, message, missing }) => (
|
|
5795
|
-
<div key={file.path} className="error-banner" role="alert">
|
|
5796
|
-
{file.fileName}: {message}
|
|
5797
|
-
{missing ? (
|
|
5798
|
-
<button
|
|
5799
|
-
type="button"
|
|
5800
|
-
className="storage-flow-connect"
|
|
5801
|
-
onClick={() => onForget(file.path)}
|
|
5802
|
-
>
|
|
5803
|
-
Remove from list
|
|
5804
|
-
</button>
|
|
5805
|
-
) : null}
|
|
5806
|
-
</div>
|
|
5807
|
-
))}
|
|
5808
|
-
</>
|
|
5809
|
-
);
|
|
5810
|
-
}
|
|
5811
|
-
|
|
5812
5655
|
function StorageConnectionBanner({ onConnect }: { onConnect: () => void }) {
|
|
5813
5656
|
return (
|
|
5814
5657
|
<div className="storage-flow-banner">
|
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
IconChevronRight,
|
|
4
4
|
IconDots,
|
|
5
5
|
IconRefresh,
|
|
6
|
+
IconUpload,
|
|
6
7
|
IconX,
|
|
7
8
|
} from "@tabler/icons-react";
|
|
8
9
|
import { useLayoutEffect, useRef, type RefObject, type ReactNode } from "react";
|
|
9
10
|
|
|
10
11
|
import { desktopRecoveryCopy as copy } from "../i18n/en-US";
|
|
12
|
+
import type { VideoStorageStatus } from "../lib/record-first";
|
|
11
13
|
import {
|
|
12
14
|
classifyRecordingRecoveryError,
|
|
13
15
|
recordingRecoveryKey,
|
|
@@ -39,6 +41,16 @@ import { Spinner } from "./ui/spinner";
|
|
|
39
41
|
|
|
40
42
|
export interface RecordingRecoveryState {
|
|
41
43
|
uploads: PendingDesktopUpload[];
|
|
44
|
+
localFiles: Array<{
|
|
45
|
+
path: string;
|
|
46
|
+
fileName: string;
|
|
47
|
+
savedAt: string;
|
|
48
|
+
error?: string;
|
|
49
|
+
missing?: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
localFilesError: string | null;
|
|
52
|
+
localFilesUploading: boolean;
|
|
53
|
+
localFilesStorageStatus: VideoStorageStatus;
|
|
42
54
|
lookupErrors: RecoveryLookupError[];
|
|
43
55
|
actionErrors: Record<string, string>;
|
|
44
56
|
refreshing: boolean;
|
|
@@ -64,6 +76,10 @@ export interface RecordingRecoveryProps extends RecordingRecoveryState {
|
|
|
64
76
|
onExport: (upload: PendingDesktopUpload) => void;
|
|
65
77
|
onOpenFolder: (upload: PendingDesktopUpload) => void;
|
|
66
78
|
onConnectStorage: (upload: PendingDesktopUpload) => void;
|
|
79
|
+
onRetryLocalFiles: () => void;
|
|
80
|
+
onRefreshStorage: () => void;
|
|
81
|
+
onConnectLocalStorage: () => void;
|
|
82
|
+
onForgetLocalFile: (path: string) => void;
|
|
67
83
|
onReviewFiles: (key: string) => void;
|
|
68
84
|
onOpenLogs: (key: string) => void;
|
|
69
85
|
}
|
|
@@ -117,19 +133,28 @@ function recoveryPresentation(props: RecordingRecoveryState) {
|
|
|
117
133
|
messages: [...(orphanErrors.get(id)?.messages || []), message],
|
|
118
134
|
});
|
|
119
135
|
}
|
|
120
|
-
const attentionCount =
|
|
121
|
-
(upload) => uploadSeverity(upload, props) !== "neutral"
|
|
122
|
-
|
|
136
|
+
const attentionCount =
|
|
137
|
+
uploads.filter((upload) => uploadSeverity(upload, props) !== "neutral")
|
|
138
|
+
.length + (props.localFilesUploading ? 0 : props.localFiles.length);
|
|
123
139
|
const unknownFailures =
|
|
124
|
-
props.lookupErrors.length > 0 ||
|
|
140
|
+
props.lookupErrors.length > 0 ||
|
|
141
|
+
orphanErrors.size > 0 ||
|
|
142
|
+
!!props.localFilesError;
|
|
143
|
+
const localFileSeverities = props.localFiles.map((file) =>
|
|
144
|
+
props.localFilesUploading ? "neutral" : file.missing ? "error" : "warning",
|
|
145
|
+
);
|
|
125
146
|
const severities = [
|
|
126
147
|
...uploads.map((upload) => uploadSeverity(upload, props)),
|
|
148
|
+
...localFileSeverities,
|
|
127
149
|
...[...orphanErrors.values()].map((error) =>
|
|
128
150
|
recordingRecoverySeverity(error.messages),
|
|
129
151
|
),
|
|
130
152
|
...(props.lookupErrors.length
|
|
131
153
|
? [recordingRecoverySeverity([lookupDiagnostic(props.lookupErrors)])]
|
|
132
154
|
: []),
|
|
155
|
+
...(props.localFilesError
|
|
156
|
+
? [recordingRecoverySeverity([props.localFilesError])]
|
|
157
|
+
: []),
|
|
133
158
|
];
|
|
134
159
|
const severity = severities.includes("error")
|
|
135
160
|
? "error"
|
|
@@ -138,6 +163,7 @@ function recoveryPresentation(props: RecordingRecoveryState) {
|
|
|
138
163
|
: "neutral";
|
|
139
164
|
return {
|
|
140
165
|
uploads,
|
|
166
|
+
localFiles: props.localFiles,
|
|
141
167
|
orphanErrors: [...orphanErrors.values()],
|
|
142
168
|
attentionCount,
|
|
143
169
|
unknownFailures,
|
|
@@ -151,21 +177,29 @@ export function RecordingRecovery(
|
|
|
151
177
|
triggerRef?: RefObject<HTMLButtonElement | null>;
|
|
152
178
|
},
|
|
153
179
|
) {
|
|
154
|
-
const { uploads, attentionCount, unknownFailures, severity } =
|
|
180
|
+
const { uploads, localFiles, attentionCount, unknownFailures, severity } =
|
|
155
181
|
recoveryPresentation(props);
|
|
156
182
|
const showFinalizing = props.finalizing && props.showFinalizing !== false;
|
|
157
|
-
if (
|
|
183
|
+
if (
|
|
184
|
+
uploads.length === 0 &&
|
|
185
|
+
localFiles.length === 0 &&
|
|
186
|
+
!unknownFailures &&
|
|
187
|
+
!showFinalizing
|
|
188
|
+
)
|
|
189
|
+
return null;
|
|
158
190
|
const label = unknownFailures
|
|
159
191
|
? copy.recordingsNeedAttention
|
|
160
|
-
:
|
|
161
|
-
? copy.
|
|
162
|
-
: attentionCount
|
|
163
|
-
? copy.
|
|
164
|
-
:
|
|
165
|
-
? copy.
|
|
166
|
-
:
|
|
167
|
-
? copy.
|
|
168
|
-
:
|
|
192
|
+
: props.localFilesUploading
|
|
193
|
+
? copy.retrying
|
|
194
|
+
: attentionCount === 1
|
|
195
|
+
? copy.attentionOne
|
|
196
|
+
: attentionCount > 1
|
|
197
|
+
? copy.attentionMany.replace("{count}", String(attentionCount))
|
|
198
|
+
: showFinalizing
|
|
199
|
+
? copy.finishing
|
|
200
|
+
: props.exportingUploadId
|
|
201
|
+
? copy.exporting
|
|
202
|
+
: copy.retrying;
|
|
169
203
|
return (
|
|
170
204
|
<div data-tw-surface className="recording-recovery-entry">
|
|
171
205
|
<Button
|
|
@@ -293,7 +327,7 @@ export function RecordingRecoveryPage(
|
|
|
293
327
|
useLayoutEffect(() => {
|
|
294
328
|
backRef.current?.focus();
|
|
295
329
|
}, []);
|
|
296
|
-
const { uploads, orphanErrors } = recoveryPresentation(props);
|
|
330
|
+
const { uploads, localFiles, orphanErrors } = recoveryPresentation(props);
|
|
297
331
|
const busy = !!props.retryingUploadId || !!props.exportingUploadId;
|
|
298
332
|
return (
|
|
299
333
|
<section
|
|
@@ -344,6 +378,125 @@ export function RecordingRecoveryPage(
|
|
|
344
378
|
</ItemActions>
|
|
345
379
|
</Item>
|
|
346
380
|
) : null}
|
|
381
|
+
{props.localFilesError ? (
|
|
382
|
+
<Item size="recovery" role="listitem">
|
|
383
|
+
<ItemMedia data-recovery-tone="warning">
|
|
384
|
+
<IconAlertTriangle size={20} stroke={1.75} aria-hidden />
|
|
385
|
+
</ItemMedia>
|
|
386
|
+
<ItemContent>
|
|
387
|
+
<ItemTitle>{copy.localFiles}</ItemTitle>
|
|
388
|
+
<RecoveryDescription
|
|
389
|
+
text={copy.lookupFailed}
|
|
390
|
+
diagnostic={props.localFilesError}
|
|
391
|
+
/>
|
|
392
|
+
</ItemContent>
|
|
393
|
+
<ItemActions>
|
|
394
|
+
<RecoveryMore
|
|
395
|
+
onOpenLogs={() => props.onOpenLogs("record-first-list")}
|
|
396
|
+
/>
|
|
397
|
+
</ItemActions>
|
|
398
|
+
</Item>
|
|
399
|
+
) : null}
|
|
400
|
+
{localFiles.map((file) => {
|
|
401
|
+
const failureKind = file.error
|
|
402
|
+
? classifyRecordingRecoveryError(file.error, {
|
|
403
|
+
storageRequired: props.needsStorage(file.error),
|
|
404
|
+
incomplete: file.missing,
|
|
405
|
+
actionError: true,
|
|
406
|
+
})
|
|
407
|
+
: null;
|
|
408
|
+
const status = props.localFilesUploading
|
|
409
|
+
? copy.retrying
|
|
410
|
+
: !props.authenticated
|
|
411
|
+
? copy.signInToRetry
|
|
412
|
+
: file.error
|
|
413
|
+
? copy[failureKind!]
|
|
414
|
+
: file.missing
|
|
415
|
+
? copy.localSaveFailed
|
|
416
|
+
: props.localFilesStorageStatus === "missing"
|
|
417
|
+
? copy.storageRequired
|
|
418
|
+
: props.localFilesStorageStatus === "unavailable"
|
|
419
|
+
? copy.storageStatusUnavailable
|
|
420
|
+
: copy.uploadPending;
|
|
421
|
+
const severity = props.localFilesUploading
|
|
422
|
+
? "neutral"
|
|
423
|
+
: file.missing
|
|
424
|
+
? "error"
|
|
425
|
+
: "warning";
|
|
426
|
+
return (
|
|
427
|
+
<Item
|
|
428
|
+
key={`local:${file.path}`}
|
|
429
|
+
size="recovery"
|
|
430
|
+
role="listitem"
|
|
431
|
+
aria-label={file.fileName}
|
|
432
|
+
>
|
|
433
|
+
<ItemMedia data-recovery-tone={severity}>
|
|
434
|
+
{props.localFilesUploading ? (
|
|
435
|
+
<Spinner />
|
|
436
|
+
) : (
|
|
437
|
+
<IconAlertTriangle size={16} stroke={1.75} aria-hidden />
|
|
438
|
+
)}
|
|
439
|
+
</ItemMedia>
|
|
440
|
+
<ItemContent>
|
|
441
|
+
<ItemTitle title={file.path}>{file.fileName}</ItemTitle>
|
|
442
|
+
<RecoveryDescription text={status} diagnostic={file.error} />
|
|
443
|
+
</ItemContent>
|
|
444
|
+
<ItemActions>
|
|
445
|
+
{!file.missing ? (
|
|
446
|
+
<RecoveryAction
|
|
447
|
+
label={
|
|
448
|
+
!props.authenticated
|
|
449
|
+
? copy.signInToRetry
|
|
450
|
+
: props.localFilesStorageStatus === "configured"
|
|
451
|
+
? copy.retry
|
|
452
|
+
: props.localFilesStorageStatus === "missing"
|
|
453
|
+
? copy.connectStorage
|
|
454
|
+
: copy.refresh
|
|
455
|
+
}
|
|
456
|
+
disabled={
|
|
457
|
+
!props.authenticated ||
|
|
458
|
+
props.localFilesUploading ||
|
|
459
|
+
props.finalizing ||
|
|
460
|
+
props.localFilesStorageStatus === "checking"
|
|
461
|
+
}
|
|
462
|
+
busy={
|
|
463
|
+
props.localFilesUploading ||
|
|
464
|
+
props.localFilesStorageStatus === "checking"
|
|
465
|
+
}
|
|
466
|
+
onClick={() => {
|
|
467
|
+
if (props.localFilesStorageStatus === "configured") {
|
|
468
|
+
props.onRetryLocalFiles();
|
|
469
|
+
} else if (props.localFilesStorageStatus === "missing") {
|
|
470
|
+
props.onConnectLocalStorage();
|
|
471
|
+
} else {
|
|
472
|
+
props.onRefreshStorage();
|
|
473
|
+
}
|
|
474
|
+
}}
|
|
475
|
+
>
|
|
476
|
+
{props.localFilesStorageStatus === "missing" ? (
|
|
477
|
+
<IconUpload aria-hidden />
|
|
478
|
+
) : (
|
|
479
|
+
<IconRefresh aria-hidden />
|
|
480
|
+
)}
|
|
481
|
+
</RecoveryAction>
|
|
482
|
+
) : null}
|
|
483
|
+
<RecoveryMore
|
|
484
|
+
localAction={
|
|
485
|
+
file.missing
|
|
486
|
+
? {
|
|
487
|
+
label: "Remove from list",
|
|
488
|
+
onSelect: () => props.onForgetLocalFile(file.path),
|
|
489
|
+
}
|
|
490
|
+
: undefined
|
|
491
|
+
}
|
|
492
|
+
onOpenLogs={() =>
|
|
493
|
+
props.onOpenLogs(`record-first:${file.path}`)
|
|
494
|
+
}
|
|
495
|
+
/>
|
|
496
|
+
</ItemActions>
|
|
497
|
+
</Item>
|
|
498
|
+
);
|
|
499
|
+
})}
|
|
347
500
|
{uploads.map((upload) => {
|
|
348
501
|
const key = recordingRecoveryKey(upload);
|
|
349
502
|
const retrying = props.retryingUploadId === key;
|
|
@@ -486,6 +639,8 @@ export function RecordingRecoveryPage(
|
|
|
486
639
|
})}
|
|
487
640
|
</ItemGroup>
|
|
488
641
|
{!uploads.length &&
|
|
642
|
+
!localFiles.length &&
|
|
643
|
+
!props.localFilesError &&
|
|
489
644
|
!orphanErrors.length &&
|
|
490
645
|
!props.lookupErrors.length &&
|
|
491
646
|
!props.finalizing ? (
|