|
1 | 1 | import { useMemo, useState } from 'react' |
2 | 2 | import type { AssetMeta } from '@shared/ipc' |
3 | | -import { useStore } from '../store' |
| 3 | +import { useStore, type AssetSortColumn, type AssetSortOrder } from '../store' |
4 | 4 | import { assetTabPath } from '../lib/asset-tabs' |
5 | 5 | import { confirmMoveToTrash } from '../lib/confirm-trash' |
6 | 6 | import { promptApp } from '../lib/prompt-requests' |
@@ -37,12 +37,28 @@ const ASSET_ROW_GRID = |
37 | 37 | 'grid grid-cols-[minmax(0,1fr)_6rem_4rem_5rem_5rem_1.75rem] items-center gap-4' |
38 | 38 |
|
39 | 39 | // Which column the Assets list is sorted by. (#460) |
40 | | -export type AssetSortKey = 'name' | 'used' | 'type' | 'size' | 'modified' |
| 40 | +export type AssetSortKey = AssetSortColumn |
41 | 41 | export interface AssetSort { |
42 | 42 | key: AssetSortKey |
43 | 43 | dir: 'asc' | 'desc' |
44 | 44 | } |
45 | 45 |
|
| 46 | +/** Split the stored `<column>-<dir>` preference into the shape `sortAssets` |
| 47 | + * takes. The value is validated in the store, so a bad split can't reach |
| 48 | + * here; fall back to the default anyway rather than sorting by `undefined`. (#473) */ |
| 49 | +export function parseAssetSortOrder(order: AssetSortOrder): AssetSort { |
| 50 | + const at = order.lastIndexOf('-') |
| 51 | + const key = order.slice(0, at) as AssetSortKey |
| 52 | + const dir = order.slice(at + 1) as AssetSort['dir'] |
| 53 | + if (!key || (dir !== 'asc' && dir !== 'desc')) return { key: 'name', dir: 'asc' } |
| 54 | + return { key, dir } |
| 55 | +} |
| 56 | + |
| 57 | +/** Inverse of `parseAssetSortOrder`, for writing the preference back. (#473) */ |
| 58 | +export function assetSortOrderOf(sort: AssetSort): AssetSortOrder { |
| 59 | + return `${sort.key}-${sort.dir}` as AssetSortOrder |
| 60 | +} |
| 61 | + |
46 | 62 | /** Display label for a note path in the "used by" menu — its filename. */ |
47 | 63 | function noteLabel(notePath: string): string { |
48 | 64 | return notePath.split('/').pop()?.replace(/\.md$/i, '') ?? notePath |
@@ -103,10 +119,11 @@ export function AssetsView(): JSX.Element { |
103 | 119 | const [filter, setFilter] = useState('') |
104 | 120 | const [menu, setMenu] = useState<{ x: number; y: number; asset: AssetMeta } | null>(null) |
105 | 121 | const [usageMenu, setUsageMenu] = useState<{ x: number; y: number; notes: string[] } | null>(null) |
106 | | - const [sort, setSort] = useState<{ key: AssetSortKey; dir: 'asc' | 'desc' }>({ |
107 | | - key: 'name', |
108 | | - dir: 'asc' |
109 | | - }) |
| 122 | + // Sort lives in the store (and config.toml), not local state, so leaving the |
| 123 | + // view and coming back keeps the column you picked. (#473) |
| 124 | + const assetSortOrder = useStore((s) => s.assetSortOrder) |
| 125 | + const setAssetSortOrder = useStore((s) => s.setAssetSortOrder) |
| 126 | + const sort = useMemo(() => parseAssetSortOrder(assetSortOrder), [assetSortOrder]) |
110 | 127 |
|
111 | 128 | // assetPath → note paths that embed it (resolved via relative-path + the |
112 | 129 | // unique-basename fallback, matching how embeds render). (#185) |
@@ -138,11 +155,11 @@ export function AssetsView(): JSX.Element { |
138 | 155 | // Click a header: sort by it, or flip direction if it's already active. Text |
139 | 156 | // columns start ascending; count/size/date start descending (biggest first). |
140 | 157 | const toggleSort = (key: AssetSortKey): void => { |
141 | | - setSort((prev) => |
142 | | - prev.key === key |
143 | | - ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' } |
| 158 | + const next: AssetSort = |
| 159 | + sort.key === key |
| 160 | + ? { key, dir: sort.dir === 'asc' ? 'desc' : 'asc' } |
144 | 161 | : { key, dir: key === 'name' || key === 'type' ? 'asc' : 'desc' } |
145 | | - ) |
| 162 | + setAssetSortOrder(assetSortOrderOf(next)) |
146 | 163 | } |
147 | 164 |
|
148 | 165 | const copyEmbed = (asset: AssetMeta): void => { |
|
0 commit comments