Use tanstackdb for file nodes

This commit is contained in:
Hakan Shehu
2025-10-23 17:50:56 +02:00
parent 74209c3b09
commit 2e71ee1f46
5 changed files with 11 additions and 144 deletions

View File

@@ -1,105 +0,0 @@
import { WorkspaceQueryHandlerBase } from '@colanode/client/handlers/queries/workspace-query-handler-base';
import { mapNode } from '@colanode/client/lib/mappers';
import { ChangeCheckResult, QueryHandler } from '@colanode/client/lib/types';
import { FileListQueryInput } from '@colanode/client/queries/files/file-list';
import { Event } from '@colanode/client/types/events';
import { LocalFileNode } from '@colanode/client/types/nodes';
export class FileListQueryHandler
extends WorkspaceQueryHandlerBase
implements QueryHandler<FileListQueryInput>
{
public async handleQuery(
input: FileListQueryInput
): Promise<LocalFileNode[]> {
return await this.fetchFiles(input);
}
public async checkForChanges(
event: Event,
input: FileListQueryInput,
output: LocalFileNode[]
): Promise<ChangeCheckResult<FileListQueryInput>> {
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.parentId === input.parentId
) {
const output = await this.handleQuery(input);
return {
hasChanges: true,
result: output,
};
}
if (
event.type === 'node.updated' &&
event.workspace.userId === input.userId &&
event.node.parentId === input.parentId
) {
const file = output.find((file) => file.id === event.node.id);
if (file) {
const newResult = output.map((file) => {
if (file.id === event.node.id && event.node.type === 'file') {
return event.node;
}
return file;
});
return {
hasChanges: true,
result: newResult,
};
}
}
if (
event.type === 'node.deleted' &&
event.workspace.userId === input.userId &&
event.node.parentId === input.parentId
) {
const file = output.find((file) => file.id === event.node.id);
if (file) {
const output = await this.handleQuery(input);
return {
hasChanges: true,
result: output,
};
}
}
return {
hasChanges: false,
};
}
private async fetchFiles(
input: FileListQueryInput
): Promise<LocalFileNode[]> {
const workspace = this.getWorkspace(input.userId);
const offset = (input.page - 1) * input.count;
const files = await workspace.database
.selectFrom('nodes')
.selectAll()
.where('type', '=', 'file')
.where('parent_id', '=', input.parentId)
.orderBy('id', 'asc')
.limit(input.count)
.offset(offset)
.execute();
return files.map(mapNode) as LocalFileNode[];
}
}

View File

@@ -17,7 +17,6 @@ import { EmojiSearchQueryHandler } from './emojis/emoji-search';
import { EmojiSvgGetQueryHandler } from './emojis/emoji-svg-get';
import { DownloadListQueryHandler } from './files/download-list';
import { FileDownloadRequestGetQueryHandler } from './files/file-download-request-get';
import { FileListQueryHandler } from './files/file-list';
import { LocalFileGetQueryHandler } from './files/local-file-get';
import { TempFileListQueryHandler } from './files/temp-file-list';
import { UploadListQueryHandler } from './files/upload-list';
@@ -63,7 +62,6 @@ export const buildQueryHandlerMap = (app: AppService): QueryHandlerMap => {
'user.search': new UserSearchQueryHandler(app),
'workspace.list': new WorkspaceListQueryHandler(app),
'user.list': new UserListQueryHandler(app),
'file.list': new FileListQueryHandler(app),
'emoji.list': new EmojiListQueryHandler(app),
'emoji.get': new EmojiGetQueryHandler(app),
'emoji.get.by.skin.id': new EmojiGetBySkinIdQueryHandler(app),

View File

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

View File

@@ -10,7 +10,6 @@ export * from './emojis/emoji-get-by-skin-id';
export * from './emojis/emoji-get';
export * from './emojis/emoji-list';
export * from './emojis/emoji-search';
export * from './files/file-list';
export * from './files/local-file-get';
export * from './files/file-download-request-get';
export * from './icons/icon-category-list';

View File

@@ -1,17 +1,14 @@
import { eq, useLiveQuery } from '@tanstack/react-db';
import { useNavigate } from '@tanstack/react-router';
import { useState } from 'react';
import { match } from 'ts-pattern';
import { FileListQueryInput } from '@colanode/client/queries';
import { FolderLayoutType } from '@colanode/client/types';
import { FolderLayoutType, LocalFileNode } from '@colanode/client/types';
import { GalleryLayout } from '@colanode/ui/components/folders/galleries/gallery-layout';
import { GridLayout } from '@colanode/ui/components/folders/grids/grid-layout';
import { ListLayout } from '@colanode/ui/components/folders/lists/list-layout';
import { FolderContext } from '@colanode/ui/contexts/folder';
import { useWorkspace } from '@colanode/ui/contexts/workspace';
import { useLiveQueries } from '@colanode/ui/hooks/use-live-queries';
const FILES_PER_PAGE = 100;
import { database } from '@colanode/ui/data';
interface FolderFilesProps {
id: string;
@@ -27,19 +24,15 @@ export const FolderFiles = ({
const workspace = useWorkspace();
const navigate = useNavigate({ from: '/workspace/$userId' });
const [lastPage] = useState<number>(1);
const inputs: FileListQueryInput[] = Array.from({
length: lastPage,
}).map((_, i) => ({
type: 'file.list',
userId: workspace.userId,
parentId: id,
count: FILES_PER_PAGE,
page: i + 1,
}));
const viewListQuery = useLiveQuery((q) =>
q
.from({ nodes: database.workspace(workspace.userId).nodes })
.where(({ nodes }) => eq(nodes.type, 'file'))
.where(({ nodes }) => eq(nodes.parentId, id))
.orderBy(({ nodes }) => nodes.id, 'asc')
);
const result = useLiveQueries(inputs);
const files = result.flatMap((data) => data.data ?? []);
const files = viewListQuery.data.map((node) => node as LocalFileNode) ?? [];
return (
<FolderContext.Provider