feat(dashboard): close legacy parity gaps
This commit is contained in:
1 parent
92287c0a9a
commit
4b3f00729f
21 files changed
+397
-28
No files matched your search
@@ -5,7 +5,7 @@ import { MediaCard } from './media-card.js';
|
||||
import { useMediaQuery, useMediaSyncStatusQuery, useTriggerMediaSyncMutation } from './queries.js';
|
||||
import './media.css';
|
||||
|
||||
const initialFilters: MediaFilters = {
|
||||
export const initialMediaFilters: MediaFilters = {
|
||||
conversation: 'all',
|
||||
sender: 'all',
|
||||
type: 'all',
|
||||
@@ -20,11 +20,17 @@ function formatLastSync(timestamp: number): string {
|
||||
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(timestamp);
|
||||
}
|
||||
|
||||
export function MediaPage() {
|
||||
export interface MediaPageProps {
|
||||
filters?: MediaFilters;
|
||||
onFiltersChange?(filters: MediaFilters): void;
|
||||
}
|
||||
|
||||
export function MediaPage({ filters: controlledFilters, onFiltersChange }: MediaPageProps) {
|
||||
const mediaQuery = useMediaQuery();
|
||||
const statusQuery = useMediaSyncStatusQuery();
|
||||
const syncMutation = useTriggerMediaSyncMutation();
|
||||
const [filters, setFilters] = useState<MediaFilters>(initialFilters);
|
||||
const [localFilters, setLocalFilters] = useState<MediaFilters>(initialMediaFilters);
|
||||
const filters = controlledFilters ?? localFilters;
|
||||
const [revealedIds, setRevealedIds] = useState<Set<string>>(() => new Set());
|
||||
const [isRevealAll, setIsRevealAll] = useState(false);
|
||||
const [isFullSyncDialogOpen, setIsFullSyncDialogOpen] = useState(false);
|
||||
@@ -42,7 +48,9 @@ export function MediaPage() {
|
||||
const isSyncBusy = statusQuery.data?.isSyncing === true || syncMutation.isPending;
|
||||
|
||||
function updateFilter<Key extends keyof MediaFilters>(key: Key, value: MediaFilters[Key]) {
|
||||
setFilters((current) => ({ ...current, [key]: value }));
|
||||
const next = { ...filters, [key]: value };
|
||||
if (controlledFilters === undefined) setLocalFilters(next);
|
||||
onFiltersChange?.(next);
|
||||
}
|
||||
|
||||
function toggleItem(itemId: string) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import type { MediaFilters } from './filters.js';
|
||||
import { initialMediaFilters, MediaPage } from './media-page.js';
|
||||
|
||||
function parseType(value: string | null): MediaFilters['type'] {
|
||||
return value === 'image' || value === 'gif' ? value : 'all';
|
||||
}
|
||||
|
||||
function parseDateRange(value: string | null): MediaFilters['dateRange'] {
|
||||
return value === '24h' || value === '7d' || value === '30d' ? value : 'all';
|
||||
}
|
||||
|
||||
export function MediaRoute() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const filters: MediaFilters = {
|
||||
conversation: searchParams.get('conversation') ?? initialMediaFilters.conversation,
|
||||
sender: searchParams.get('sender') ?? initialMediaFilters.sender,
|
||||
type: parseType(searchParams.get('type')),
|
||||
dateRange: parseDateRange(searchParams.get('range')),
|
||||
};
|
||||
|
||||
function updateFilters(next: MediaFilters) {
|
||||
const params = new URLSearchParams();
|
||||
if (next.conversation !== 'all') params.set('conversation', next.conversation);
|
||||
if (next.sender !== 'all') params.set('sender', next.sender);
|
||||
if (next.type !== 'all') params.set('type', next.type);
|
||||
if (next.dateRange !== 'all') params.set('range', next.dateRange);
|
||||
setSearchParams(params, { replace: true });
|
||||
}
|
||||
|
||||
return <MediaPage filters={filters} onFiltersChange={updateFilters} />;
|
||||
}
|
||||
Reference in new issue
Block a user