forked from WordPress/wordpress-playground
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackup-status-indicator.tsx
More file actions
136 lines (119 loc) · 4.06 KB
/
backup-status-indicator.tsx
File metadata and controls
136 lines (119 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { useEffect, useRef } from 'react';
import css from './save-status-indicator.module.css';
import classNames from 'classnames';
import { useActiveSite, useAppDispatch } from '../../lib/state/redux/store';
import { Icon, Spinner } from '@wordpress/components';
import { backup } from '@wordpress/icons';
import { updateSiteMetadata } from '../../lib/state/redux/slice-sites';
import { useBackup } from '../../lib/hooks/use-backup';
import { useTakeover, useRemoteBackup } from '../../lib/hooks/use-takeover';
function isSameDay(timestamp1: number, timestamp2: number): boolean {
const d1 = new Date(timestamp1);
const d2 = new Date(timestamp2);
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
}
function formatUsageDays(days: number): string {
if (days === 1) return '1 day since backup';
return `${days} days since backup`;
}
type BackupUrgency = 'current' | 'due' | 'overdue';
function getBackupUrgency(daysUsed: number): BackupUrgency {
if (daysUsed <= 1) return 'current';
if (daysUsed <= 4) return 'due';
return 'overdue';
}
export function BackupStatusIndicator() {
const activeSite = useActiveSite();
const dispatch = useAppDispatch();
const { performBackup, isBackingUp } = useBackup();
const { isDependentMode } = useTakeover();
const { requestBackup, isRequestingBackup } = useRemoteBackup();
const lastCheckedDateRef = useRef<string>(new Date().toDateString());
const {
lastAccessDate,
whenCreated,
daysUsedSinceLastBackup = 0,
} = activeSite?.metadata || {};
// Check for day change when tab becomes visible or periodically
useEffect(() => {
if (!activeSite || activeSite.metadata.storage === 'none') {
return;
}
const checkForNewDay = () => {
const today = new Date().toDateString();
if (today !== lastCheckedDateRef.current) {
lastCheckedDateRef.current = today;
// It's a new day - increment the counter
dispatch(
updateSiteMetadata({
slug: activeSite.slug,
changes: {
lastAccessDate: Date.now(),
daysUsedSinceLastBackup:
(activeSite.metadata.daysUsedSinceLastBackup ||
0) + 1,
},
})
);
}
};
// Check when tab becomes visible
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
checkForNewDay();
}
};
// Also check periodically (every minute) in case tab stays visible overnight
const interval = setInterval(checkForNewDay, 60000);
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener(
'visibilitychange',
handleVisibilityChange
);
clearInterval(interval);
};
}, [activeSite, dispatch]);
// Only show backup indicator if user has returned after creation day
const hasReturnedAfterCreation =
whenCreated &&
lastAccessDate &&
!isSameDay(whenCreated, lastAccessDate);
// Hide on first day - no need to prompt for backup yet
if (!hasReturnedAfterCreation) {
return null;
}
// Hide if no usage since last backup (or site is new with 0 days tracked)
if (daysUsedSinceLastBackup === 0) {
return null;
}
const urgency = getBackupUrgency(daysUsedSinceLastBackup);
const isWorking = isBackingUp || isRequestingBackup;
const buttonText = isRequestingBackup
? 'Requesting...'
: isBackingUp
? 'Backing up...'
: formatUsageDays(daysUsedSinceLastBackup);
const tooltipText = isDependentMode
? 'Click to request a backup from the main tab. Your Playground is stored in this browser and may be cleared unexpectedly.'
: 'Your Playground is stored in this browser. Browser data can be cleared unexpectedly. Click to download a backup.';
const handleClick = isDependentMode ? requestBackup : performBackup;
return (
<div className={classNames(css.indicator, css[urgency])}>
<button
className={classNames(css.saveButton, css[`${urgency}Button`])}
onClick={handleClick}
disabled={isWorking}
type="button"
title={tooltipText}
>
{isWorking ? <Spinner /> : <Icon icon={backup} size={16} />}
{buttonText}
</button>
</div>
);
}