Preserve sync state after re-pairing

Signed-off-by: Zoorpha <aaron@kubedo.com>
This commit is contained in:
Zoorpha 2026-06-23 01:33:44 +02:00
parent 49770857fa
commit 80b67a78e4
3 changed files with 34 additions and 3 deletions

View file

@ -7,7 +7,7 @@ import { RustShareVaultSyncSettingTab } from './ui/settings-tab';
import { StatusBar } from './ui/status-bar';
import { RustShareAPI } from './api';
import { SyncEngine } from './sync';
import { SyncState, createEmptySyncState, migrateSyncState, pruneTombstones } from './state';
import { SyncState, createEmptySyncState, migrateSyncState, pruneTombstones, retargetSyncStateDevice } from './state';
import { SyncQueue, SyncOperation } from './sync-queue';
import { syncLog } from './sync-log';
import { detectCloudSyncFolder, shouldIgnorePath, loadDesktopAuthToken, isValidUuid } from './utils';
@ -294,10 +294,13 @@ export default class RustShareVaultSyncPlugin extends Plugin {
}
// Initialize sync state
if (!this.syncState || this.syncState.vault_id !== vaultId || this.syncState.device_id !== deviceId) {
if (this.syncState?.vault_id === vaultId && this.syncState.device_id !== deviceId) {
this.syncState = retargetSyncStateDevice(this.syncState, deviceId, this.settings.deviceName);
} else if (!this.syncState || this.syncState.vault_id !== vaultId) {
this.syncState = createEmptySyncState(vaultId, deviceId, this.settings.deviceName);
}
await this.saveSettings();
this.statusBar.updateStatus('connected');
} catch (e) {
this.statusBar.updateStatus('error', `Connect failed: ${e}`);

View file

@ -34,6 +34,14 @@ export function createEmptySyncState(vaultId: string, deviceId: string, deviceNa
};
}
export function retargetSyncStateDevice(state: SyncState, deviceId: string, deviceName: string): SyncState {
return {
...state,
device_id: deviceId,
device_name: deviceName,
};
}
export function migrateSyncState(state: Record<string, unknown>): SyncState {
const version = typeof state.version === 'number' ? state.version : 0;
if (version === 1) {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { createEmptySyncState } from '../src/state';
import { createEmptySyncState, retargetSyncStateDevice } from '../src/state';
describe('createEmptySyncState', () => {
it('returns state with correct defaults and empty files/tombstones', () => {
@ -13,3 +13,23 @@ describe('createEmptySyncState', () => {
expect(state.tombstones).toEqual({});
});
});
describe('retargetSyncStateDevice', () => {
it('keeps vault file history when a re-paired device gets a new id', () => {
const state = createEmptySyncState('vault-123', 'old-device', 'Old Laptop');
state.last_server_rev = 7;
state.files['note.md'] = {
sha256: 'abc',
server_rev: 6,
last_synced_at: '2026-06-23T00:00:00.000Z',
};
const retargeted = retargetSyncStateDevice(state, 'new-device', 'New Laptop');
expect(retargeted.device_id).toBe('new-device');
expect(retargeted.device_name).toBe('New Laptop');
expect(retargeted.vault_id).toBe('vault-123');
expect(retargeted.last_server_rev).toBe(7);
expect(retargeted.files['note.md']).toEqual(state.files['note.md']);
});
});