Use tanstackdb for space queries

This commit is contained in:
Hakan Shehu
2025-11-20 10:58:13 -08:00
parent 31b71f0547
commit 2252371cfc
6 changed files with 16 additions and 130 deletions

View File

@@ -37,7 +37,6 @@ import { RecordFieldValueCountQueryHandler } from './records/record-field-value-
import { RecordListQueryHandler } from './records/record-list';
import { RecordSearchQueryHandler } from './records/record-search';
import { ServerListQueryHandler } from './servers/server-list';
import { SpaceListQueryHandler } from './spaces/space-list';
import { UserListQueryHandler } from './users/user-list';
import { UserSearchQueryHandler } from './users/user-search';
import { UserStorageGetQueryHandler } from './users/user-storage-get';
@@ -84,7 +83,6 @@ export const buildQueryHandlerMap = (app: AppService): QueryHandlerMap => {
'user.storage.get': new UserStorageGetQueryHandler(app),
'local.file.get': new LocalFileGetQueryHandler(app),
'file.download.request.get': new FileDownloadRequestGetQueryHandler(app),
'space.list': new SpaceListQueryHandler(app),
'document.get': new DocumentGetQueryHandler(app),
'document.state.get': new DocumentStateGetQueryHandler(app),
'document.updates.list': new DocumentUpdatesListQueryHandler(app),

View File

@@ -1,98 +0,0 @@
import { SelectNode } from '@colanode/client/databases/workspace';
import { WorkspaceQueryHandlerBase } from '@colanode/client/handlers/queries/workspace-query-handler-base';
import { ChangeCheckResult, QueryHandler } from '@colanode/client/lib';
import { mapNode } from '@colanode/client/lib/mappers';
import { SpaceListQueryInput } from '@colanode/client/queries/spaces/space-list';
import { Event } from '@colanode/client/types/events';
import { LocalSpaceNode } from '@colanode/client/types/nodes';
export class SpaceListQueryHandler
extends WorkspaceQueryHandlerBase
implements QueryHandler<SpaceListQueryInput>
{
public async handleQuery(
input: SpaceListQueryInput
): Promise<LocalSpaceNode[]> {
const rows = await this.fetchChildren(input);
return rows.map(mapNode) as LocalSpaceNode[];
}
public async checkForChanges(
event: Event,
input: SpaceListQueryInput,
output: LocalSpaceNode[]
): Promise<ChangeCheckResult<SpaceListQueryInput>> {
if (
event.type === 'workspace.deleted' &&
event.workspace.userId === input.userId
) {
return {
hasChanges: true,
result: [],
};
}
if (
event.type === 'node.created' &&
event.workspace.userId === input.userId &&
event.node.type === 'space'
) {
const newChildren = [...output, event.node];
return {
hasChanges: true,
result: newChildren,
};
}
if (
event.type === 'node.updated' &&
event.workspace.userId === input.userId &&
event.node.type === 'space'
) {
const node = output.find((n) => n.id === event.node.id);
if (node) {
const newChildren = output.map((n) =>
n.id === event.node.id ? (event.node as LocalSpaceNode) : n
);
return {
hasChanges: true,
result: newChildren,
};
}
}
if (
event.type === 'node.deleted' &&
event.workspace.userId === input.userId &&
event.node.type === 'space'
) {
const node = output.find((n) => n.id === event.node.id);
if (node) {
const newChildren = output.filter((n) => n.id !== event.node.id);
return {
hasChanges: true,
result: newChildren,
};
}
}
return {
hasChanges: false,
};
}
private async fetchChildren(
input: SpaceListQueryInput
): Promise<SelectNode[]> {
const workspace = this.getWorkspace(input.userId);
const rows = await workspace.database
.selectFrom('nodes')
.selectAll()
.where('type', '=', 'space')
.execute();
return rows;
}
}

View File

@@ -25,7 +25,6 @@ export * from './nodes/node-reactions-aggregate';
export * from './nodes/node-tree-get';
export * from './records/record-list';
export * from './records/record-search';
export * from './spaces/space-list';
export * from './users/user-list';
export * from './users/user-search';
export * from './users/user-storage-get';

View File

@@ -1,17 +0,0 @@
import { LocalSpaceNode } from '@colanode/client/types/nodes';
export type SpaceListQueryInput = {
type: 'space.list';
page: number;
count: number;
userId: string;
};
declare module '@colanode/client/queries' {
interface QueryMap {
'space.list': {
input: SpaceListQueryInput;
output: LocalSpaceNode[];
};
}
}

View File

@@ -7,6 +7,7 @@ import {
LocalDatabaseViewNode,
LocalNode,
LocalRecordNode,
LocalSpaceNode,
Upload,
User,
} from '@colanode/client/types';
@@ -32,6 +33,7 @@ class WorkspaceCollections {
public readonly views: Collection<LocalDatabaseViewNode>;
public readonly records: Collection<LocalRecordNode>;
public readonly chats: Collection<LocalChatNode>;
public readonly spaces: Collection<LocalSpaceNode>;
constructor(userId: string) {
this.userId = userId;
@@ -45,17 +47,19 @@ class WorkspaceCollections {
.from({ nodes: this.nodes })
.where(({ nodes }) => eq(nodes.type, 'database'))
);
this.views = createLiveQueryCollection((q) =>
q
.from({ nodes: this.nodes })
.where(({ nodes }) => eq(nodes.type, 'database_view'))
);
this.chats = createLiveQueryCollection((q) =>
q.from({ nodes: this.nodes }).where(({ nodes }) => eq(nodes.type, 'chat'))
);
this.spaces = createLiveQueryCollection((q) =>
q
.from({ nodes: this.nodes })
.where(({ nodes }) => eq(nodes.type, 'space'))
);
this.records = createLiveQueryCollection((q) =>
q
.from({ nodes: this.nodes })

View File

@@ -1,23 +1,23 @@
import { useLiveQuery } from '@tanstack/react-db';
import { collections } from '@colanode/ui/collections';
import { SidebarHeader } from '@colanode/ui/components/layouts/sidebars/sidebar-header';
import { SpaceCreateButton } from '@colanode/ui/components/spaces/space-create-button';
import { SpaceSidebarItem } from '@colanode/ui/components/spaces/space-sidebar-item';
import { useWorkspace } from '@colanode/ui/contexts/workspace';
import { useLiveQuery } from '@colanode/ui/hooks/use-live-query';
export const SidebarSpaces = () => {
const workspace = useWorkspace();
const canCreateSpace =
workspace.role !== 'guest' && workspace.role !== 'none';
const spaceListQuery = useLiveQuery({
type: 'space.list',
userId: workspace.userId,
parentId: workspace.workspaceId,
page: 0,
count: 100,
});
const spaceListQuery = useLiveQuery((q) =>
q
.from({ spaces: collections.workspace(workspace.userId).spaces })
.orderBy(({ spaces }) => spaces.id, 'asc')
);
const spaces = spaceListQuery.data ?? [];
const spaces = spaceListQuery.data;
return (
<div className="flex flex-col group/sidebar h-full px-2">