Clear out old migration logic and replace with thin shim for favorites; sort order is lost

This commit is contained in:
Jack Kingsman
2026-04-03 17:15:41 -07:00
parent 7ad1ee26a4
commit d5922a214b
15 changed files with 28 additions and 328 deletions
-9
View File
@@ -14,8 +14,6 @@ import type {
MaintenanceResult,
Message,
MessagesAroundResponse,
MigratePreferencesRequest,
MigratePreferencesResponse,
RawPacket,
RadioAdvertMode,
RadioConfig,
@@ -342,13 +340,6 @@ export const api = {
body: JSON.stringify({ type, id }),
}),
// Preferences migration (one-time, from localStorage to database)
migratePreferences: (request: MigratePreferencesRequest) =>
fetchJson<MigratePreferencesResponse>('/settings/migrate', {
method: 'POST',
body: JSON.stringify(request),
}),
// Fanout
getFanoutConfigs: () => fetchJson<FanoutConfig[]>('/fanout'),
createFanoutConfig: (config: {
+22 -55
View File
@@ -2,17 +2,8 @@ import { useState, useCallback, useEffect, useRef } from 'react';
import { api } from '../api';
import { takePrefetchOrFetch } from '../prefetch';
import { toast } from '../components/ui/sonner';
import {
initLastMessageTimes,
loadLocalStorageLastMessageTimes,
loadLocalStorageSortOrder,
clearLocalStorageConversationState,
} from '../utils/conversationState';
import {
isFavorite,
loadLocalStorageFavorites,
clearLocalStorageFavorites,
} from '../utils/favorites';
import { initLastMessageTimes } from '../utils/conversationState';
import { isFavorite } from '../utils/favorites';
import type { AppSettings, AppSettingsUpdate, Favorite } from '../types';
export function useAppSettings() {
@@ -153,59 +144,35 @@ export function useAppSettings() {
}
}, []);
// One-time migration of localStorage preferences to server
// Legacy favorites migration: if pre-server-side favorites exist in
// localStorage, toggle each one via the existing API and clear the key.
useEffect(() => {
if (!appSettings || hasMigratedRef.current) return;
if (appSettings.preferences_migrated) {
clearLocalStorageFavorites();
clearLocalStorageConversationState();
hasMigratedRef.current = true;
return;
}
const localFavorites = loadLocalStorageFavorites();
const localSortOrder = loadLocalStorageSortOrder();
const localLastMessageTimes = loadLocalStorageLastMessageTimes();
const hasLocalData =
localFavorites.length > 0 ||
localSortOrder !== 'recent' ||
Object.keys(localLastMessageTimes).length > 0;
if (!hasLocalData) {
hasMigratedRef.current = true;
return;
}
hasMigratedRef.current = true;
const migratePreferences = async () => {
const FAVORITES_KEY = 'remoteterm-favorites';
let localFavorites: Favorite[] = [];
try {
const stored = localStorage.getItem(FAVORITES_KEY);
if (stored) localFavorites = JSON.parse(stored);
} catch {
// corrupt or unavailable
}
if (localFavorites.length === 0) return;
const migrate = async () => {
try {
const result = await api.migratePreferences({
favorites: localFavorites,
sort_order: localSortOrder,
last_message_times: localLastMessageTimes,
});
if (result.migrated) {
toast.success('Preferences migrated', {
description: `Migrated ${localFavorites.length} favorites to server`,
});
for (const f of localFavorites) {
await api.toggleFavorite(f.type, f.id);
}
setAppSettings(result.settings);
initLastMessageTimes(result.settings.last_message_times ?? {});
clearLocalStorageFavorites();
clearLocalStorageConversationState();
localStorage.removeItem(FAVORITES_KEY);
await fetchAppSettings();
} catch (err) {
console.error('Failed to migrate preferences:', err);
console.error('Failed to migrate legacy favorites:', err);
}
};
migratePreferences();
}, [appSettings]);
migrate();
}, [appSettings, fetchAppSettings]);
return {
appSettings,
+1 -2
View File
@@ -24,7 +24,6 @@ const mocks = vi.hoisted(() => ({
requestTrace: vi.fn(),
updateRadioConfig: vi.fn(),
setPrivateKey: vi.fn(),
migratePreferences: vi.fn(),
},
toast: {
success: vi.fn(),
@@ -191,7 +190,7 @@ const baseSettings = {
favorites: [] as Array<{ type: 'channel' | 'contact'; id: string }>,
auto_decrypt_dm_on_advert: false,
last_message_times: {},
preferences_migrated: false,
advert_interval: 0,
last_advert_time: 0,
flood_scope: '',
+1 -2
View File
@@ -11,7 +11,6 @@ const mocks = vi.hoisted(() => ({
getUndecryptedPacketCount: vi.fn(),
getChannels: vi.fn(),
getContacts: vi.fn(),
migratePreferences: vi.fn(),
},
useConversationMessagesCalls: vi.fn(),
}));
@@ -219,7 +218,7 @@ describe('App search jump target handling', () => {
favorites: [],
auto_decrypt_dm_on_advert: false,
last_message_times: {},
preferences_migrated: true,
advert_interval: 0,
last_advert_time: 0,
});
+1 -2
View File
@@ -9,7 +9,6 @@ const mocks = vi.hoisted(() => ({
getUndecryptedPacketCount: vi.fn(),
getChannels: vi.fn(),
getContacts: vi.fn(),
migratePreferences: vi.fn(),
},
}));
@@ -170,7 +169,7 @@ describe('App startup hash resolution', () => {
favorites: [],
auto_decrypt_dm_on_advert: false,
last_message_times: {},
preferences_migrated: true,
advert_interval: 0,
last_advert_time: 0,
});
+1 -1
View File
@@ -62,7 +62,7 @@ const baseSettings: AppSettings = {
favorites: [],
auto_decrypt_dm_on_advert: false,
last_message_times: {},
preferences_migrated: false,
advert_interval: 0,
last_advert_time: 0,
flood_scope: '',
-12
View File
@@ -333,7 +333,6 @@ export interface AppSettings {
favorites: Favorite[];
auto_decrypt_dm_on_advert: boolean;
last_message_times: Record<string, number>;
preferences_migrated: boolean;
advert_interval: number;
last_advert_time: number;
flood_scope: string;
@@ -360,17 +359,6 @@ export interface TrackedTelemetryResponse {
names: Record<string, string>;
}
export interface MigratePreferencesRequest {
favorites: Favorite[];
sort_order: string;
last_message_times: Record<string, number>;
}
export interface MigratePreferencesResponse {
migrated: boolean;
settings: AppSettings;
}
/** Contact type constants */
export const CONTACT_TYPE_REPEATER = 2;
export const CONTACT_TYPE_ROOM = 3;
-37
View File
@@ -9,7 +9,6 @@
* across devices - see useUnreadCounts hook.
*/
const LAST_MESSAGE_KEY = 'remoteterm-lastMessageTime';
const SORT_ORDER_KEY = 'remoteterm-sortOrder';
const SIDEBAR_SECTION_SORT_ORDERS_KEY = 'remoteterm-sidebar-section-sort-orders';
@@ -72,30 +71,6 @@ export function getStateKey(type: 'channel' | 'contact', id: string): string {
return `${type}-${id}`;
}
/**
* Load last message times from localStorage (for migration only)
*/
export function loadLocalStorageLastMessageTimes(): ConversationTimes {
try {
const stored = localStorage.getItem(LAST_MESSAGE_KEY);
return stored ? JSON.parse(stored) : {};
} catch {
return {};
}
}
/**
* Load sort order from localStorage (for migration only)
*/
export function loadLocalStorageSortOrder(): SortOrder {
try {
const stored = localStorage.getItem(SORT_ORDER_KEY);
return stored === 'alpha' ? 'alpha' : 'recent';
} catch {
return 'recent';
}
}
/**
* Load the legacy single sidebar sort order from localStorage, if present.
*/
@@ -149,15 +124,3 @@ export function saveLocalStorageSidebarSectionSortOrders(orders: SidebarSectionS
// localStorage might be disabled
}
}
/**
* Clear conversation state from localStorage (after migration)
*/
export function clearLocalStorageConversationState(): void {
try {
localStorage.removeItem(LAST_MESSAGE_KEY);
localStorage.removeItem(SORT_ORDER_KEY);
} catch {
// localStorage might be disabled
}
}
+1 -28
View File
@@ -1,15 +1,11 @@
/**
* Favorites utilities.
*
* Favorites are now stored server-side in the database.
* This file provides helper functions for checking favorites
* and loading legacy localStorage data for migration.
* Favorites are stored server-side in the database.
*/
import type { Favorite } from '../types';
const FAVORITES_KEY = 'remoteterm-favorites';
/**
* Check if a conversation is favorited (from provided favorites array)
*/
@@ -20,26 +16,3 @@ export function isFavorite(
): boolean {
return favorites.some((f) => f.type === type && f.id === id);
}
/**
* Load favorites from localStorage (for migration only)
*/
export function loadLocalStorageFavorites(): Favorite[] {
try {
const stored = localStorage.getItem(FAVORITES_KEY);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
}
/**
* Clear favorites from localStorage (after migration)
*/
export function clearLocalStorageFavorites(): void {
try {
localStorage.removeItem(FAVORITES_KEY);
} catch {
// localStorage might be disabled
}
}