mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
fix(startup): restore keychain consent barrier
This commit is contained in:
+3
-2
@@ -698,7 +698,7 @@ function App() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!coreReady || !autoAddClipboardLinks) return;
|
||||
if (!coreReady || showKeychainModal || !autoAddClipboardLinks) return;
|
||||
|
||||
let active = true;
|
||||
let wasForeground = false;
|
||||
@@ -725,6 +725,7 @@ function App() {
|
||||
// read was pending. Let that newer Add-modal request win unchanged.
|
||||
if (
|
||||
!currentSettings.autoAddClipboardLinks ||
|
||||
currentSettings.showKeychainModal ||
|
||||
currentStore.pendingAddRequestVersion !== requestVersionBeforeRead
|
||||
) {
|
||||
return;
|
||||
@@ -772,7 +773,7 @@ function App() {
|
||||
window.removeEventListener('blur', handleForegroundChange);
|
||||
document.removeEventListener('visibilitychange', handleForegroundChange);
|
||||
};
|
||||
}, [autoAddClipboardLinks, coreReady]);
|
||||
}, [autoAddClipboardLinks, coreReady, showKeychainModal]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
|
||||
@@ -99,10 +99,10 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
||||
<span
|
||||
className="tabular-nums"
|
||||
title={hasDownloadedAmount
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? '~' : ''}${sizeDisplay.total}`
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? '~' : ''}${sizeDisplay.total} ${sizeDisplay.unit}`
|
||||
: sizeDisplay.fallback}
|
||||
aria-label={hasDownloadedAmount
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? 'approximately ' : ''}${sizeDisplay.total}`
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? 'approximately ' : ''}${sizeDisplay.total} ${sizeDisplay.unit}`
|
||||
: sizeDisplay.fallback}
|
||||
>
|
||||
{hasDownloadedAmount ? (
|
||||
@@ -110,7 +110,7 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
||||
<span className={downloadProgressColorClass(download.status)}>{sizeDisplay.downloaded}</span>
|
||||
<span className="text-text-muted"> / </span>
|
||||
<span>
|
||||
{sizeDisplay.totalIsEstimate ? '~' : ''}{sizeDisplay.total}
|
||||
{sizeDisplay.totalIsEstimate ? '~' : ''}{sizeDisplay.total} {sizeDisplay.unit}
|
||||
</span>
|
||||
</>
|
||||
) : sizeDisplay.fallback}
|
||||
|
||||
@@ -237,7 +237,7 @@ export const PropertiesModal = () => {
|
||||
<span
|
||||
className="truncate"
|
||||
title={hasDownloadedAmount
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? 'approximately ' : ''}${sizeDisplay.total}`
|
||||
? `${sizeDisplay.downloaded} downloaded of ${sizeDisplay.totalIsEstimate ? 'approximately ' : ''}${sizeDisplay.total} ${sizeDisplay.unit}`
|
||||
: sizeDisplay.fallback}
|
||||
>
|
||||
{hasDownloadedAmount ? (
|
||||
@@ -245,7 +245,7 @@ export const PropertiesModal = () => {
|
||||
<span className={downloadProgressColorClass(item.status)}>{sizeDisplay.downloaded}</span>
|
||||
<span className="text-text-muted"> / </span>
|
||||
<span className="text-text-secondary">
|
||||
{sizeDisplay.totalIsEstimate ? '~' : ''}{sizeDisplay.total}
|
||||
{sizeDisplay.totalIsEstimate ? '~' : ''}{sizeDisplay.total} {sizeDisplay.unit}
|
||||
</span>
|
||||
</>
|
||||
) : sizeDisplay.fallback}
|
||||
|
||||
@@ -14,10 +14,23 @@ describe('download progress size display', () => {
|
||||
totalIsEstimate: true,
|
||||
fallbackSize: 'Unknown'
|
||||
})).toEqual({
|
||||
downloaded: '1.20 GB',
|
||||
total: '2.40 GB',
|
||||
downloaded: '1.20',
|
||||
total: '2.40',
|
||||
unit: 'GB',
|
||||
totalIsEstimate: true,
|
||||
fallback: 'Unknown'
|
||||
});
|
||||
});
|
||||
|
||||
it('converts downloaded bytes into the total size unit', () => {
|
||||
expect(resolveDownloadSizeDisplay({
|
||||
downloadedBytes: 512 * 1024 ** 2,
|
||||
totalBytes: 2 * 1024 ** 3,
|
||||
fallbackSize: '2 GB'
|
||||
})).toMatchObject({
|
||||
downloaded: '0.50',
|
||||
total: '2.00',
|
||||
unit: 'GB'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
export interface DownloadSizeDisplay {
|
||||
downloaded: string | null;
|
||||
total: string | null;
|
||||
unit: string | null;
|
||||
totalIsEstimate: boolean;
|
||||
fallback: string;
|
||||
}
|
||||
|
||||
const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;
|
||||
|
||||
const isUsableByteCount = (value: number | null | undefined): value is number =>
|
||||
typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
||||
|
||||
export const formatDownloadBytes = (bytes: number): string => {
|
||||
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
||||
|
||||
const units = ['KB', 'MB', 'GB', 'TB'];
|
||||
const byteUnitIndex = (bytes: number): number => {
|
||||
let value = bytes;
|
||||
let unitIndex = -1;
|
||||
while (value >= 1024 && unitIndex < units.length - 1) {
|
||||
let unitIndex = 0;
|
||||
while (value >= 1024 && unitIndex < BYTE_UNITS.length - 1) {
|
||||
value /= 1024;
|
||||
unitIndex += 1;
|
||||
}
|
||||
return unitIndex;
|
||||
};
|
||||
|
||||
const formatDownloadBytesInUnit = (bytes: number, unitIndex: number): string => {
|
||||
const value = bytes / 1024 ** unitIndex;
|
||||
const precision = value >= 100 ? 0 : value >= 10 ? 1 : 2;
|
||||
return `${value.toFixed(precision)} ${units[unitIndex]}`;
|
||||
return value < 1024 && unitIndex === 0
|
||||
? `${Math.round(value)}`
|
||||
: value.toFixed(precision);
|
||||
};
|
||||
|
||||
export const formatDownloadBytes = (bytes: number): string => {
|
||||
const unitIndex = byteUnitIndex(bytes);
|
||||
return `${formatDownloadBytesInUnit(bytes, unitIndex)} ${BYTE_UNITS[unitIndex]}`;
|
||||
};
|
||||
|
||||
export const resolveDownloadSizeDisplay = ({
|
||||
@@ -34,8 +45,15 @@ export const resolveDownloadSizeDisplay = ({
|
||||
totalIsEstimate?: boolean;
|
||||
fallbackSize?: string | null;
|
||||
}): DownloadSizeDisplay => ({
|
||||
downloaded: isUsableByteCount(downloadedBytes) ? formatDownloadBytes(downloadedBytes) : null,
|
||||
total: isUsableByteCount(totalBytes) && totalBytes > 0 ? formatDownloadBytes(totalBytes) : null,
|
||||
downloaded: isUsableByteCount(downloadedBytes) && isUsableByteCount(totalBytes) && totalBytes > 0
|
||||
? formatDownloadBytesInUnit(downloadedBytes, byteUnitIndex(totalBytes))
|
||||
: null,
|
||||
total: isUsableByteCount(totalBytes) && totalBytes > 0
|
||||
? formatDownloadBytesInUnit(totalBytes, byteUnitIndex(totalBytes))
|
||||
: null,
|
||||
unit: isUsableByteCount(totalBytes) && totalBytes > 0
|
||||
? BYTE_UNITS[byteUnitIndex(totalBytes)]
|
||||
: null,
|
||||
totalIsEstimate: Boolean(totalIsEstimate && isUsableByteCount(totalBytes) && totalBytes > 0),
|
||||
fallback: fallbackSize && fallbackSize !== '-' ? fallbackSize : 'Unknown'
|
||||
});
|
||||
|
||||
@@ -80,6 +80,19 @@ describe('getKeychainStartupDecision', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps persistent access deferred without prompting when the version API is unavailable', () => {
|
||||
expect(getKeychainStartupDecision({
|
||||
portable: false,
|
||||
appVersion: '',
|
||||
approvedVersion: '1.0.5',
|
||||
accessGranted: true,
|
||||
promptDismissed: false
|
||||
})).toEqual({
|
||||
deferKeychainHydration: true,
|
||||
showKeychainPrompt: false
|
||||
});
|
||||
});
|
||||
|
||||
it('never gates portable pairing on credential-store access', () => {
|
||||
expect(getKeychainStartupDecision({
|
||||
portable: true,
|
||||
|
||||
@@ -25,9 +25,11 @@ export const getKeychainStartupDecision = ({
|
||||
};
|
||||
}
|
||||
|
||||
const versionChanged = Boolean(appVersion) && approvedVersion !== appVersion;
|
||||
const versionKnown = Boolean(appVersion.trim());
|
||||
const versionChanged = versionKnown && approvedVersion !== appVersion;
|
||||
const mustDeferAccess = !accessGranted || !versionKnown || versionChanged;
|
||||
return {
|
||||
deferKeychainHydration: !accessGranted || versionChanged,
|
||||
deferKeychainHydration: mustDeferAccess,
|
||||
showKeychainPrompt: versionChanged || (!accessGranted && !promptDismissed)
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user