Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 36 additions & 29 deletions packages/store/src/collection/collection-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,14 @@ export const prepareCollectionSlice =
}))
},

onEntryCreated: () => {},
onEntryCreated: () => {
set((state) => ({
collectionEntries: computeCollectionEntries(
state.currentCollectionPath,
get().entries,
),
}))
},
Comment thread
hhhjin marked this conversation as resolved.

onEntriesDeleted: ({ paths }) => {
set((state) => {
Expand Down Expand Up @@ -142,21 +149,21 @@ export const prepareCollectionSlice =
},

onEntryRenamed: ({ oldPath, newPath, isDirectory }) => {
if (!isDirectory) {
return
}

set((state) => {
const nextCurrentCollectionPath = replacePathPrefixIfDescendant(
state.currentCollectionPath,
oldPath,
newPath,
)
const nextLastCollectionPath = replacePathPrefixIfDescendant(
state.lastCollectionPath,
oldPath,
newPath,
)
const nextCurrentCollectionPath = isDirectory
? replacePathPrefixIfDescendant(
state.currentCollectionPath,
oldPath,
newPath,
)
: state.currentCollectionPath
const nextLastCollectionPath = isDirectory
? replacePathPrefixIfDescendant(
state.lastCollectionPath,
oldPath,
newPath,
)
: state.lastCollectionPath

return {
currentCollectionPath: nextCurrentCollectionPath,
Expand All @@ -170,21 +177,21 @@ export const prepareCollectionSlice =
},

onEntryMoved: ({ sourcePath, newPath, isDirectory }) => {
if (!isDirectory) {
return
}

set((state) => {
const nextCurrentCollectionPath = replacePathPrefixIfDescendant(
state.currentCollectionPath,
sourcePath,
newPath,
)
const nextLastCollectionPath = replacePathPrefixIfDescendant(
state.lastCollectionPath,
sourcePath,
newPath,
)
const nextCurrentCollectionPath = isDirectory
? replacePathPrefixIfDescendant(
state.currentCollectionPath,
sourcePath,
newPath,
)
: state.currentCollectionPath
const nextLastCollectionPath = isDirectory
? replacePathPrefixIfDescendant(
state.lastCollectionPath,
sourcePath,
newPath,
)
: state.lastCollectionPath

return {
currentCollectionPath: nextCurrentCollectionPath,
Expand Down
2 changes: 2 additions & 0 deletions packages/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
type IndexingSliceDependencies,
prepareIndexingSlice,
} from "./indexing/indexing-slice"
import { registerCollectionIntegration } from "./integrations/register-collection-integration"
import { registerGitSyncWorkspaceIntegration } from "./integrations/register-git-sync-workspace-integration"
import { registerIndexingIntegration } from "./integrations/register-indexing-integration"
import { createStoreEventHub } from "./integrations/store-events"
Expand Down Expand Up @@ -91,6 +92,7 @@ export const createMditStore = (
...createUISlice(...args),
}))

registerCollectionIntegration(store, events)
registerIndexingIntegration(store, events)
registerGitSyncWorkspaceIntegration(store, events)

Expand Down
197 changes: 197 additions & 0 deletions packages/store/src/integrations/integrations.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,207 @@
import { describe, expect, it, vi } from "vitest"
import { createStore } from "zustand/vanilla"
import type { MditStore } from ".."
import { prepareCollectionSlice } from "../collection/collection-slice"
import { registerCollectionIntegration } from "./register-collection-integration"
import { registerGitSyncWorkspaceIntegration } from "./register-git-sync-workspace-integration"
import { registerIndexingIntegration } from "./register-indexing-integration"
import { createStoreEventHub } from "./store-events"

type EntryLike = {
path: string
name: string
isDirectory: boolean
children?: EntryLike[]
}

const makeFile = (path: string, name: string): EntryLike => ({
path,
name,
isDirectory: false,
})

const makeDir = (
path: string,
name: string,
children: EntryLike[] = [],
): EntryLike => ({
path,
name,
isDirectory: true,
children,
})

const createCollectionIntegrationStore = () => {
const createSlice = prepareCollectionSlice()

return createStore<any>()((set, get, api) => ({
workspacePath: "/ws",
entries: [],
...createSlice(set, get, api),
}))
}

describe("store integrations", () => {
it("refreshes collection entries when workspace entries are replaced", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/folder",
entries: [
makeDir("/ws/folder", "folder", [
makeFile("/ws/folder/note.md", "note.md"),
]),
],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entries-replaced",
workspacePath: "/ws",
})

expect(store.getState().collectionEntries).toEqual([
makeFile("/ws/folder/note.md", "note.md"),
])
})

it("refreshes collection entries when workspace creates an entry", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/folder",
entries: [
makeDir("/ws/folder", "folder", [
makeFile("/ws/folder/new.md", "new.md"),
]),
],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entry-created",
workspacePath: "/ws",
parentPath: "/ws/folder",
entry: makeFile("/ws/folder/new.md", "new.md"),
})

expect(store.getState().collectionEntries).toEqual([
makeFile("/ws/folder/new.md", "new.md"),
])
})

it("clears collection paths when deleted entries remove the active collection", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/folder/child",
lastCollectionPath: "/ws/folder/other",
entries: [makeDir("/ws/another", "another")],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entries-deleted",
workspacePath: "/ws",
paths: ["/ws/folder"],
})

expect(store.getState().currentCollectionPath).toBeNull()
expect(store.getState().lastCollectionPath).toBeNull()
expect(store.getState().collectionEntries).toEqual([])
})

it("rewrites collection paths when directories are renamed", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/old/child",
lastCollectionPath: "/ws/old/other",
entries: [makeDir("/ws/new", "new")],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entry-renamed",
workspacePath: "/ws",
oldPath: "/ws/old",
newPath: "/ws/new",
isDirectory: true,
newName: "new",
})

expect(store.getState().currentCollectionPath).toBe("/ws/new/child")
expect(store.getState().lastCollectionPath).toBe("/ws/new/other")
})

it("refreshes collection entries when files are renamed", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/folder",
entries: [
makeDir("/ws/folder", "folder", [
makeFile("/ws/folder/new.md", "new.md"),
]),
],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entry-renamed",
workspacePath: "/ws",
oldPath: "/ws/folder/old.md",
newPath: "/ws/folder/new.md",
isDirectory: false,
newName: "new.md",
})

expect(store.getState().collectionEntries).toEqual([
makeFile("/ws/folder/new.md", "new.md"),
])
})

it("rewrites collection paths when directories are moved", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
currentCollectionPath: "/ws/src/child",
lastCollectionPath: "/ws/src/other",
entries: [makeDir("/ws/dest", "dest")],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entry-moved",
workspacePath: "/ws",
sourcePath: "/ws/src",
destinationDirPath: "/ws",
newPath: "/ws/dest",
isDirectory: true,
})

expect(store.getState().currentCollectionPath).toBe("/ws/dest/child")
expect(store.getState().lastCollectionPath).toBe("/ws/dest/other")
})

it("ignores collection events for a different workspace", async () => {
const events = createStoreEventHub()
const store = createCollectionIntegrationStore()
store.setState({
workspacePath: "/other",
currentCollectionPath: "/other/folder",
entries: [makeDir("/other/folder", "folder")],
})

registerCollectionIntegration(store as unknown as MditStore, events)
await events.emit({
type: "workspace/entries-replaced",
workspacePath: "/ws",
})

expect(store.getState().collectionEntries).toEqual([])
})

it("resets indexing state when workspace resets", async () => {
const events = createStoreEventHub()
const state = {
Expand Down
70 changes: 70 additions & 0 deletions packages/store/src/integrations/register-collection-integration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { MditStore } from ".."
import type { StoreEventHub } from "./store-events"

export const registerCollectionIntegration = (
store: MditStore,
events: StoreEventHub,
) =>
events.subscribe((event) => {
const state = store.getState()

switch (event.type) {
case "workspace/entries-replaced": {
if (state.workspacePath !== event.workspacePath) {
return
}

state.refreshCollectionEntries()
return
}
case "workspace/entry-created": {
if (state.workspacePath !== event.workspacePath) {
return
}

state.onEntryCreated({
parentPath: event.parentPath,
entry: event.entry,
expandParent: event.expandParent,
expandNewDirectory: event.expandNewDirectory,
})
return
}
case "workspace/entries-deleted": {
if (state.workspacePath !== event.workspacePath) {
return
}

state.onEntriesDeleted({ paths: event.paths })
return
}
case "workspace/entry-renamed": {
if (state.workspacePath !== event.workspacePath) {
return
}

state.onEntryRenamed({
oldPath: event.oldPath,
newPath: event.newPath,
isDirectory: event.isDirectory,
newName: event.newName,
})
return
}
case "workspace/entry-moved": {
if (state.workspacePath !== event.workspacePath) {
return
}

state.onEntryMoved({
sourcePath: event.sourcePath,
destinationDirPath: event.destinationDirPath,
newPath: event.newPath,
isDirectory: event.isDirectory,
})
return
}
default:
return
}
})
Loading
Loading