feat(status): built-in and custom status presets
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>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user