Fix fresh-install pull blocked by saveSettings timestamp
After a browser restart with no extension data, enabling encryption calls saveSettings() which writes ss_lastModified = Date.now(). All pull paths then compare that fresh timestamp against the remote's older timestamp, see local >= remote, and skip the pull with "already on latest version" — even though local is completely empty. Fix: add _hasRealLocalData() which returns true only if local storage contains actual identity profiles or mappings with real values. The timestamp skip condition now requires BOTH a newer local timestamp AND real local data. An empty/fresh install always falls through to pull. Affects pullFromGist, pullFromUrl, pullFromSyncStorage, and importSyncCode. https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
This commit is contained in:
+23
-4
@@ -613,7 +613,7 @@ const SilentSendSync = {
|
||||
|
||||
if (!force) {
|
||||
const local = await this._getAllData();
|
||||
if (local.lastModified && local.lastModified >= data.lastModified) {
|
||||
if (local.lastModified && local.lastModified >= data.lastModified && this._hasRealLocalData(local)) {
|
||||
return {
|
||||
success: false,
|
||||
skipped: true,
|
||||
@@ -676,7 +676,7 @@ const SilentSendSync = {
|
||||
|
||||
// Check if there's new data before requiring auth
|
||||
const local = await this._getAllData();
|
||||
if (local.lastModified && local.lastModified >= syncMeta.lastModified) return null;
|
||||
if (local.lastModified && local.lastModified >= syncMeta.lastModified && this._hasRealLocalData(local)) return null;
|
||||
|
||||
// New data exists — reassemble
|
||||
const chunkKeys = Array.from({ length: syncMeta.chunks }, (_, i) => SYNC_KEY_PREFIX + i);
|
||||
@@ -819,7 +819,7 @@ const SilentSendSync = {
|
||||
if (remoteMod === 0) {
|
||||
return { success: false, reason: 'Gist contains no data (timestamp is 0). Push from the source browser first.' };
|
||||
}
|
||||
if (remoteMod <= (local.lastModified || 0)) {
|
||||
if (remoteMod <= (local.lastModified || 0) && this._hasRealLocalData(local)) {
|
||||
return { success: true, imported: false };
|
||||
}
|
||||
|
||||
@@ -882,7 +882,7 @@ const SilentSendSync = {
|
||||
|
||||
const local = await this._getAllData();
|
||||
const remoteMod = data._ssEncrypted ? data.lastModified : data.lastModified;
|
||||
if (remoteMod <= (local.lastModified || 0)) {
|
||||
if (remoteMod <= (local.lastModified || 0) && this._hasRealLocalData(local)) {
|
||||
return { success: true, imported: false };
|
||||
}
|
||||
|
||||
@@ -908,6 +908,25 @@ const SilentSendSync = {
|
||||
// Internal helpers
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns true if local storage contains real user data (identity or mappings).
|
||||
* Used to bypass the lastModified timestamp check on a fresh install where
|
||||
* saveSettings() has written a recent timestamp even though there is no PII
|
||||
* configured yet. Without this, a fresh browser after restart would always
|
||||
* skip pulls because its saveSettings timestamp > the remote's older timestamp.
|
||||
*/
|
||||
_hasRealLocalData(local) {
|
||||
if ((local.mappings || []).length > 0) return true;
|
||||
const profiles = local.identity?.profiles || [];
|
||||
return profiles.some(p =>
|
||||
(p.names || []).some(n => n.real?.trim()) ||
|
||||
(p.emails || []).some(e => e.real?.trim()) ||
|
||||
(p.usernames || []).some(u => u.real?.trim()) ||
|
||||
(p.phones || []).some(ph => ph.real?.trim()) ||
|
||||
p.catchAllEmail?.trim()
|
||||
);
|
||||
},
|
||||
|
||||
async _getAllData() {
|
||||
// Use dynamic import to avoid circular dependency
|
||||
const StorageModule = (await import('./storage.js')).default;
|
||||
|
||||
Reference in New Issue
Block a user