mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-08-01 22:43:17 +02:00
Move to server side preference and read indicator management
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* localStorage utilities for tracking conversation message times.
|
||||
* Conversation state utilities.
|
||||
*
|
||||
* Stores when each conversation last received a message, used for
|
||||
* sorting conversations by recency in the sidebar.
|
||||
* Last message times are tracked in-memory and persisted server-side.
|
||||
* This file provides helper functions for generating state keys
|
||||
* and managing conversation times.
|
||||
*
|
||||
* Read state (last_read_at) is tracked server-side for consistency
|
||||
* across devices - see useUnreadCounts hook.
|
||||
@@ -11,45 +12,42 @@
|
||||
import { getPubkeyPrefix } from './pubkey';
|
||||
|
||||
const LAST_MESSAGE_KEY = 'remoteterm-lastMessageTime';
|
||||
const SORT_ORDER_KEY = 'remoteterm-sortOrder';
|
||||
|
||||
export type ConversationTimes = Record<string, number>;
|
||||
export type SortOrder = 'recent' | 'alpha';
|
||||
|
||||
function loadTimes(key: string): ConversationTimes {
|
||||
try {
|
||||
const stored = localStorage.getItem(key);
|
||||
return stored ? JSON.parse(stored) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function saveTimes(key: string, times: ConversationTimes): void {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(times));
|
||||
} catch {
|
||||
// localStorage might be full or disabled
|
||||
}
|
||||
// In-memory cache of last message times (loaded from server on init)
|
||||
let lastMessageTimesCache: ConversationTimes = {};
|
||||
|
||||
/**
|
||||
* Initialize the last message times cache from server data
|
||||
*/
|
||||
export function initLastMessageTimes(times: ConversationTimes): void {
|
||||
lastMessageTimesCache = { ...times };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all last message times from the cache
|
||||
*/
|
||||
export function getLastMessageTimes(): ConversationTimes {
|
||||
return loadTimes(LAST_MESSAGE_KEY);
|
||||
return { ...lastMessageTimesCache };
|
||||
}
|
||||
|
||||
export function setLastMessageTime(stateKey: string, timestamp: number): ConversationTimes {
|
||||
const times = loadTimes(LAST_MESSAGE_KEY);
|
||||
// Only update if this is a newer message
|
||||
if (!times[stateKey] || timestamp > times[stateKey]) {
|
||||
times[stateKey] = timestamp;
|
||||
saveTimes(LAST_MESSAGE_KEY, times);
|
||||
}
|
||||
return times;
|
||||
/**
|
||||
* Update a single message time in the cache and return the updated cache.
|
||||
* Note: This does NOT persist to server - caller should sync if needed.
|
||||
*/
|
||||
export function setLastMessageTime(key: string, timestamp: number): ConversationTimes {
|
||||
lastMessageTimesCache[key] = timestamp;
|
||||
return { ...lastMessageTimesCache };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a state tracking key for message times.
|
||||
*
|
||||
* This is NOT the same as Message.conversation_key (the database field).
|
||||
* This creates prefixed keys for localStorage/state tracking:
|
||||
* This creates prefixed keys for state tracking:
|
||||
* - Channels: "channel-{channelKey}"
|
||||
* - Contacts: "contact-{12-char-pubkey-prefix}"
|
||||
*
|
||||
@@ -63,3 +61,39 @@ export function getStateKey(type: 'channel' | 'contact', id: string): string {
|
||||
// For contacts, use 12-char prefix for consistent matching
|
||||
return `contact-${getPubkeyPrefix(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';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,21 +1,39 @@
|
||||
/**
|
||||
* localStorage utilities for managing favorite conversations.
|
||||
* Favorites utilities.
|
||||
*
|
||||
* Favorites are stored client-side and displayed in a dedicated section
|
||||
* above channels in the sidebar, always sorted by most recent message.
|
||||
* Favorites are now stored server-side in the database.
|
||||
* This file provides helper functions for checking favorites
|
||||
* and loading legacy localStorage data for migration.
|
||||
*/
|
||||
|
||||
import type { Favorite } from '../types';
|
||||
import { pubkeysMatch } from './pubkey';
|
||||
|
||||
const FAVORITES_KEY = 'remoteterm-favorites';
|
||||
|
||||
export interface Favorite {
|
||||
type: 'channel' | 'contact';
|
||||
id: string; // channel key or contact public key
|
||||
/**
|
||||
* Check if a conversation is favorited (from provided favorites array)
|
||||
*
|
||||
* For contacts, uses prefix matching to handle full pubkeys vs 12-char prefixes.
|
||||
*/
|
||||
export function isFavorite(
|
||||
favorites: Favorite[],
|
||||
type: 'channel' | 'contact',
|
||||
id: string
|
||||
): boolean {
|
||||
return favorites.some((f) => {
|
||||
if (f.type !== type) return false;
|
||||
// For contacts, use prefix matching (handles full keys vs prefixes)
|
||||
if (type === 'contact') return pubkeysMatch(f.id, id);
|
||||
// For channels, exact match
|
||||
return f.id === id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load favorites from localStorage
|
||||
* Load favorites from localStorage (for migration only)
|
||||
*/
|
||||
export function loadFavorites(): Favorite[] {
|
||||
export function loadLocalStorageFavorites(): Favorite[] {
|
||||
try {
|
||||
const stored = localStorage.getItem(FAVORITES_KEY);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
@@ -25,58 +43,15 @@ export function loadFavorites(): Favorite[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save favorites to localStorage
|
||||
* Clear favorites from localStorage (after migration)
|
||||
*/
|
||||
function saveFavorites(favorites: Favorite[]): void {
|
||||
export function clearLocalStorageFavorites(): void {
|
||||
try {
|
||||
localStorage.setItem(FAVORITES_KEY, JSON.stringify(favorites));
|
||||
localStorage.removeItem(FAVORITES_KEY);
|
||||
} catch {
|
||||
// localStorage might be full or disabled
|
||||
// localStorage might be disabled
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a conversation to favorites
|
||||
*/
|
||||
export function addFavorite(type: 'channel' | 'contact', id: string): Favorite[] {
|
||||
const favorites = loadFavorites();
|
||||
// Check if already favorited
|
||||
if (favorites.some((f) => f.type === type && f.id === id)) {
|
||||
return favorites;
|
||||
}
|
||||
const updated = [...favorites, { type, id }];
|
||||
saveFavorites(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a conversation from favorites
|
||||
*/
|
||||
export function removeFavorite(type: 'channel' | 'contact', id: string): Favorite[] {
|
||||
const favorites = loadFavorites();
|
||||
const updated = favorites.filter((f) => !(f.type === type && f.id === id));
|
||||
saveFavorites(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a conversation is favorited
|
||||
*/
|
||||
export function isFavorite(
|
||||
favorites: Favorite[],
|
||||
type: 'channel' | 'contact',
|
||||
id: string
|
||||
): boolean {
|
||||
return favorites.some((f) => f.type === type && f.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle a conversation's favorite status
|
||||
*/
|
||||
export function toggleFavorite(type: 'channel' | 'contact', id: string): Favorite[] {
|
||||
const favorites = loadFavorites();
|
||||
if (favorites.some((f) => f.type === type && f.id === id)) {
|
||||
return removeFavorite(type, id);
|
||||
}
|
||||
return addFavorite(type, id);
|
||||
}
|
||||
// Re-export the Favorite type for convenience
|
||||
export type { Favorite };
|
||||
|
||||
Reference in New Issue
Block a user