The Status Message field required typing every status from scratch. Add a one-click preset row: - Built-in "Quick statuses" spanning gaming, social, life and work (Gaming, In a party, Ranked grind, AFK, Watching, In a meeting, Working remotely, Lunch, On vacation, Out sick...), each carrying a suggested auto-clear so a click sets the message and the timer at once. - Custom presets: save the current status as a reusable preset, stored in io.lotus.status_presets account data (synced across devices via the shared account-data list store), de-duped by normalized label, capped at 20, deletable inline. The existing save path is factored into a shared applyStatus() used by the Save button and by preset apply, so server writes, the status localStorage keys, and the auto-clear expiry bookkeeping stay identical. Ordering/de-dupe logic is pure in utils/statusPresets.ts (upsertPreset, normalizeLabel) with unit tests; no change to the presence wire format, expiry monitor, or presence-mode selector. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
import { useCallback } from 'react';
|
|
import { useMatrixClient } from './useMatrixClient';
|
|
import { createAccountDataListStore } from './createAccountDataListStore';
|
|
import { StatusPreset, upsertPreset } from '../utils/statusPresets';
|
|
|
|
const STATUS_PRESETS_KEY = 'io.lotus.status_presets';
|
|
const MAX_PRESETS = 20;
|
|
|
|
type StatusPresetsContent = {
|
|
presets: StatusPreset[];
|
|
};
|
|
|
|
// Shared, concurrency-safe store. See createAccountDataListStore for why the
|
|
// snapshot + write queue must be module-scoped (writes are serialized to avoid
|
|
// lost updates, since setAccountData replaces the whole content with no merge).
|
|
const statusPresetsStore = createAccountDataListStore<StatusPreset[], StatusPresetsContent>({
|
|
eventType: STATUS_PRESETS_KEY,
|
|
read: (content) => content?.presets ?? [],
|
|
write: (presets) => ({ presets }),
|
|
});
|
|
|
|
export function useStatusPresets(): {
|
|
presets: StatusPreset[];
|
|
addPreset: (preset: StatusPreset) => Promise<void>;
|
|
removePreset: (id: string) => Promise<void>;
|
|
} {
|
|
const mx = useMatrixClient();
|
|
const presets = statusPresetsStore.useValue(mx);
|
|
|
|
const addPreset = useCallback(
|
|
(preset: StatusPreset) =>
|
|
statusPresetsStore.enqueueWrite(mx, (current) => upsertPreset(current, preset, MAX_PRESETS)),
|
|
[mx],
|
|
);
|
|
|
|
const removePreset = useCallback(
|
|
(id: string) =>
|
|
statusPresetsStore.enqueueWrite(mx, (current) => current.filter((p) => p.id !== id)),
|
|
[mx],
|
|
);
|
|
|
|
return { presets, addPreset, removePreset };
|
|
}
|