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) {
|
if (!force) {
|
||||||
const local = await this._getAllData();
|
const local = await this._getAllData();
|
||||||
if (local.lastModified && local.lastModified >= data.lastModified) {
|
if (local.lastModified && local.lastModified >= data.lastModified && this._hasRealLocalData(local)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
skipped: true,
|
skipped: true,
|
||||||
@@ -676,7 +676,7 @@ const SilentSendSync = {
|
|||||||
|
|
||||||
// Check if there's new data before requiring auth
|
// Check if there's new data before requiring auth
|
||||||
const local = await this._getAllData();
|
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
|
// New data exists — reassemble
|
||||||
const chunkKeys = Array.from({ length: syncMeta.chunks }, (_, i) => SYNC_KEY_PREFIX + i);
|
const chunkKeys = Array.from({ length: syncMeta.chunks }, (_, i) => SYNC_KEY_PREFIX + i);
|
||||||
@@ -819,7 +819,7 @@ const SilentSendSync = {
|
|||||||
if (remoteMod === 0) {
|
if (remoteMod === 0) {
|
||||||
return { success: false, reason: 'Gist contains no data (timestamp is 0). Push from the source browser first.' };
|
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 };
|
return { success: true, imported: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,7 +882,7 @@ const SilentSendSync = {
|
|||||||
|
|
||||||
const local = await this._getAllData();
|
const local = await this._getAllData();
|
||||||
const remoteMod = data._ssEncrypted ? data.lastModified : data.lastModified;
|
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 };
|
return { success: true, imported: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,6 +908,25 @@ const SilentSendSync = {
|
|||||||
// Internal helpers
|
// 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() {
|
async _getAllData() {
|
||||||
// Use dynamic import to avoid circular dependency
|
// Use dynamic import to avoid circular dependency
|
||||||
const StorageModule = (await import('./storage.js')).default;
|
const StorageModule = (await import('./storage.js')).default;
|
||||||
|
|||||||
Reference in New Issue
Block a user