refactor: remove Trash system — delete is now permanent with confirm modal
Remove all vestiges of the abandoned Trash system: trashed/trashedAt fields from types, frontmatter parsing, sidebar filtering, editor banners, inspector components, mock data, and all related tests. Delete is already permanent via useDeleteActions with a confirmation dialog. Notes with trashed:true in existing vault frontmatter are now treated as normal notes (the flag is ignored by the parser). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,8 +12,6 @@ const makeEntry = (overrides: Partial<VaultEntry> = {}): VaultEntry => ({
|
||||
relatedTo: [],
|
||||
status: null,
|
||||
archived: false,
|
||||
trashed: false,
|
||||
trashedAt: null,
|
||||
modifiedAt: 1700000000,
|
||||
createdAt: 1700000000,
|
||||
fileSize: 100,
|
||||
|
||||
@@ -7,7 +7,6 @@ function makeEntry(overrides: Partial<VaultEntry> = {}): VaultEntry {
|
||||
path: '/vault/note/test.md', filename: 'test.md', title: 'Test',
|
||||
isA: 'Note', aliases: [], belongsTo: [], relatedTo: [],
|
||||
status: null, organized: false, archived: false,
|
||||
trashed: false, trashedAt: null,
|
||||
modifiedAt: null, createdAt: null, fileSize: 0,
|
||||
snippet: '', wordCount: 0, relationships: {},
|
||||
icon: null, color: null, order: null, template: null, sort: null, outgoingLinks: [],
|
||||
@@ -577,11 +576,6 @@ describe('isInboxEntry', () => {
|
||||
expect(isInboxEntry(note)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for a trashed note', () => {
|
||||
const note = makeEntry({ trashed: true })
|
||||
expect(isInboxEntry(note)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for an archived note', () => {
|
||||
const note = makeEntry({ archived: true })
|
||||
expect(isInboxEntry(note)).toBe(false)
|
||||
@@ -686,7 +680,7 @@ describe('filterEntries — folder selection', () => {
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
it('excludes archived and trashed entries by default', () => {
|
||||
it('excludes archived entries by default', () => {
|
||||
const withArchived = [
|
||||
...entries,
|
||||
makeEntry({ path: '/vault/projects/laputa/archived.md', title: 'Archived', archived: true }),
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { VaultEntry, SidebarSelection, InboxPeriod, ViewFile } from '../typ
|
||||
import { evaluateView } from './viewFilters'
|
||||
import { wikilinkTarget, resolveEntry } from './wikilink'
|
||||
|
||||
export type NoteListFilter = 'open' | 'archived' | 'trashed'
|
||||
export type NoteListFilter = 'open' | 'archived'
|
||||
|
||||
export interface RelationshipGroup {
|
||||
label: string
|
||||
@@ -306,12 +306,11 @@ export function buildRelationshipGroups(
|
||||
return b.groups
|
||||
}
|
||||
|
||||
const isActive = (e: VaultEntry) => !e.archived && !e.trashed
|
||||
const isActive = (e: VaultEntry) => !e.archived
|
||||
const isMarkdown = (e: VaultEntry) => e.fileKind === 'markdown' || !e.fileKind
|
||||
|
||||
function applySubFilter(entries: VaultEntry[], subFilter: NoteListFilter): VaultEntry[] {
|
||||
if (subFilter === 'archived') return entries.filter((e) => e.archived && !e.trashed)
|
||||
if (subFilter === 'trashed') return entries.filter((e) => e.trashed)
|
||||
if (subFilter === 'archived') return entries.filter((e) => e.archived)
|
||||
return entries.filter(isActive)
|
||||
}
|
||||
|
||||
@@ -344,9 +343,8 @@ function filterByKind(entries: VaultEntry[], selection: SidebarSelection, subFil
|
||||
|
||||
function filterByFilterType(entries: VaultEntry[], filter: string): VaultEntry[] {
|
||||
if (filter === 'all') return entries.filter(isActive)
|
||||
if (filter === 'archived') return entries.filter((e) => e.archived && !e.trashed)
|
||||
if (filter === 'trash') return entries.filter((e) => e.trashed)
|
||||
if (filter === 'favorites') return entries.filter((e) => e.favorite && !e.archived && !e.trashed)
|
||||
if (filter === 'archived') return entries.filter((e) => e.archived)
|
||||
if (filter === 'favorites') return entries.filter((e) => e.favorite && !e.archived)
|
||||
if (filter === 'pulse') return []
|
||||
return []
|
||||
}
|
||||
@@ -357,34 +355,32 @@ export function filterEntries(entries: VaultEntry[], selection: SidebarSelection
|
||||
|
||||
/** Count notes per sub-filter for a given type. */
|
||||
export function countByFilter(entries: VaultEntry[], type: string): Record<NoteListFilter, number> {
|
||||
let open = 0, archived = 0, trashed = 0
|
||||
let open = 0, archived = 0
|
||||
for (const e of entries) {
|
||||
if (!isMarkdown(e) || e.isA !== type) continue
|
||||
if (e.trashed) trashed++
|
||||
else if (e.archived) archived++
|
||||
if (e.archived) archived++
|
||||
else open++
|
||||
}
|
||||
return { open, archived, trashed }
|
||||
return { open, archived }
|
||||
}
|
||||
|
||||
/** Count notes per sub-filter across all entries (no type filter). */
|
||||
export function countAllByFilter(entries: VaultEntry[]): Record<NoteListFilter, number> {
|
||||
let open = 0, archived = 0, trashed = 0
|
||||
let open = 0, archived = 0
|
||||
for (const e of entries) {
|
||||
if (!isMarkdown(e)) continue
|
||||
if (e.trashed) trashed++
|
||||
else if (e.archived) archived++
|
||||
if (e.archived) archived++
|
||||
else open++
|
||||
}
|
||||
return { open, archived, trashed }
|
||||
return { open, archived }
|
||||
}
|
||||
|
||||
// --- Inbox ---
|
||||
|
||||
/** Check if entry belongs in the Inbox (markdown only, not organized, not trashed/archived, not a Type). */
|
||||
/** Check if entry belongs in the Inbox (markdown only, not organized, not archived, not a Type). */
|
||||
export function isInboxEntry(entry: VaultEntry): boolean {
|
||||
if (!isMarkdown(entry)) return false
|
||||
if (entry.trashed || entry.archived) return false
|
||||
if (entry.archived) return false
|
||||
if (entry.isA === 'Type') return false
|
||||
return !entry.organized
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ const BUILT_IN_SECTION_GROUPS: SectionGroup[] = [
|
||||
const BUILT_IN_TYPE_MAP = new Map(BUILT_IN_SECTION_GROUPS.map((sg) => [sg.type, sg]))
|
||||
|
||||
const isMarkdown = (e: VaultEntry) => e.fileKind === 'markdown' || !e.fileKind
|
||||
const isActive = (e: VaultEntry) => !e.trashed && !e.archived
|
||||
const isActive = (e: VaultEntry) => !e.archived
|
||||
|
||||
/** Collect unique isA values from active (non-trashed, non-archived) markdown entries. Untyped entries count as 'Note'. */
|
||||
/** Collect unique isA values from active (non-archived) markdown entries. Untyped entries count as 'Note'. */
|
||||
export function collectActiveTypes(entries: VaultEntry[]): Set<string> {
|
||||
const types = new Set<string>()
|
||||
for (const e of entries) {
|
||||
|
||||
@@ -11,7 +11,7 @@ function makeEntry(overrides: Partial<VaultEntry> = {}): VaultEntry {
|
||||
return {
|
||||
path: '/test.md', filename: 'test.md', title: 'Test', isA: null,
|
||||
aliases: [], belongsTo: [], relatedTo: [], status: null,
|
||||
archived: false, trashed: false, trashedAt: null, modifiedAt: null, createdAt: null,
|
||||
archived: false, modifiedAt: null, createdAt: null,
|
||||
fileSize: 0, snippet: '', wordCount: 0, relationships: {}, icon: null, color: null,
|
||||
order: null, template: null, sort: null, outgoingLinks: [],
|
||||
...overrides,
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('getTypeLightColor', () => {
|
||||
|
||||
const baseEntry: VaultEntry = {
|
||||
path: '', filename: '', title: '', isA: null, aliases: [], belongsTo: [], relatedTo: [],
|
||||
status: null, archived: false, trashed: false, trashedAt: null,
|
||||
status: null, archived: false,
|
||||
modifiedAt: null, createdAt: null, fileSize: 0, snippet: '', relationships: {},
|
||||
wordCount: 0,
|
||||
icon: null, color: null, order: null, sidebarLabel: null, template: null, sort: null,
|
||||
|
||||
@@ -8,7 +8,7 @@ function makeEntry(overrides: Partial<VaultEntry>): VaultEntry {
|
||||
return {
|
||||
path: '/vault/test.md', filename: 'test.md', title: 'Test', isA: null,
|
||||
aliases: [], belongsTo: [], relatedTo: [], status: null,
|
||||
archived: false, trashed: false, trashedAt: null,
|
||||
archived: false,
|
||||
modifiedAt: NOW, createdAt: NOW, fileSize: 100, snippet: '',
|
||||
wordCount: 0, relationships: {}, icon: null, color: null,
|
||||
order: null, sidebarLabel: null, template: null, sort: null, view: null,
|
||||
@@ -95,7 +95,7 @@ describe('evaluateView', () => {
|
||||
expect(result.map((e) => e.title)).toEqual(['Has'])
|
||||
})
|
||||
|
||||
it('excludes archived and trashed entries', () => {
|
||||
it('excludes archived entries', () => {
|
||||
const view: ViewDefinition = {
|
||||
name: 'All', icon: null, color: null, sort: null,
|
||||
filters: { all: [{ field: 'type', op: 'equals', value: 'Note' }] },
|
||||
@@ -103,7 +103,6 @@ describe('evaluateView', () => {
|
||||
const entries = [
|
||||
makeEntry({ isA: 'Note', title: 'Active' }),
|
||||
makeEntry({ isA: 'Note', title: 'Archived', archived: true }),
|
||||
makeEntry({ isA: 'Note', title: 'Trashed', trashed: true }),
|
||||
]
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['Active'])
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { VaultEntry, ViewDefinition, FilterGroup, FilterNode, FilterConditi
|
||||
|
||||
/** Evaluate a view's filters against a list of entries, returning only matches. */
|
||||
export function evaluateView(definition: ViewDefinition, entries: VaultEntry[]): VaultEntry[] {
|
||||
return entries.filter((e) => !e.trashed && !e.archived && evaluateGroup(definition.filters, e))
|
||||
return entries.filter((e) => !e.archived && evaluateGroup(definition.filters, e))
|
||||
}
|
||||
|
||||
function evaluateGroup(group: FilterGroup, entry: VaultEntry): boolean {
|
||||
@@ -27,7 +27,6 @@ function resolveField(entry: VaultEntry, field: string): { scalar?: string | num
|
||||
if (lower === 'title') return { scalar: entry.title }
|
||||
if (lower === 'filename') return { scalar: entry.filename }
|
||||
if (lower === 'archived') return { scalar: entry.archived }
|
||||
if (lower === 'trashed') return { scalar: entry.trashed }
|
||||
if (lower === 'favorite') return { scalar: entry.favorite }
|
||||
if (lower === 'body') return { scalar: entry.snippet }
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { wikilinkTarget, wikilinkDisplay, resolveEntry, relativePathStem } from
|
||||
function makeEntry(overrides: Partial<VaultEntry>): VaultEntry {
|
||||
return {
|
||||
path: '/vault/note.md', filename: 'note.md', title: 'Note', isA: null,
|
||||
aliases: [], belongsTo: [], relatedTo: [], status: null, archived: false, trashed: false, trashedAt: null,
|
||||
aliases: [], belongsTo: [], relatedTo: [], status: null, archived: false,
|
||||
modifiedAt: null, createdAt: null, fileSize: 100, snippet: '', wordCount: 0,
|
||||
relationships: {}, icon: null, color: null, order: null, template: null,
|
||||
sort: null, outgoingLinks: [], sidebarLabel: null, view: null, visible: null,
|
||||
|
||||
@@ -13,8 +13,6 @@ function makeEntry(overrides: Partial<VaultEntry>): VaultEntry {
|
||||
relatedTo: [],
|
||||
status: null,
|
||||
archived: false,
|
||||
trashed: false,
|
||||
trashedAt: null,
|
||||
modifiedAt: null,
|
||||
createdAt: null,
|
||||
fileSize: 100,
|
||||
|
||||
Reference in New Issue
Block a user