mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Use tanstackdb for database and view queries
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
import { SelectNode } from '@colanode/client/databases/workspace';
|
||||
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 { DatabaseListQueryInput } from '@colanode/client/queries/databases/database-list';
|
||||
import { Event } from '@colanode/client/types/events';
|
||||
import { LocalDatabaseNode } from '@colanode/client/types/nodes';
|
||||
import { compareString } from '@colanode/core';
|
||||
|
||||
export class DatabaseListQueryHandler
|
||||
extends WorkspaceQueryHandlerBase
|
||||
implements QueryHandler<DatabaseListQueryInput>
|
||||
{
|
||||
public async handleQuery(
|
||||
input: DatabaseListQueryInput
|
||||
): Promise<LocalDatabaseNode[]> {
|
||||
const databases = await this.fetchDatabases(input);
|
||||
const databaseNodes = databases.map(mapNode) as LocalDatabaseNode[];
|
||||
return databaseNodes.sort((a, b) => compareString(a.id, b.id));
|
||||
}
|
||||
|
||||
public async checkForChanges(
|
||||
event: Event,
|
||||
input: DatabaseListQueryInput,
|
||||
output: LocalDatabaseNode[]
|
||||
): Promise<ChangeCheckResult<DatabaseListQueryInput>> {
|
||||
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 === 'database'
|
||||
) {
|
||||
const newResult = await this.handleQuery(input);
|
||||
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newResult,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
event.type === 'node.updated' &&
|
||||
event.workspace.userId === input.userId &&
|
||||
event.node.type === 'database'
|
||||
) {
|
||||
const database = output.find((database) => database.id === event.node.id);
|
||||
if (database) {
|
||||
const newResult = output.map((node) => {
|
||||
if (node.id === event.node.id) {
|
||||
return event.node as LocalDatabaseNode;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newResult,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
event.type === 'node.deleted' &&
|
||||
event.workspace.userId === input.userId &&
|
||||
event.node.type === 'database'
|
||||
) {
|
||||
const database = output.find((node) => node.id === event.node.id);
|
||||
|
||||
if (database) {
|
||||
const newOutput = await this.handleQuery(input);
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newOutput,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasChanges: false,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchDatabases(
|
||||
input: DatabaseListQueryInput
|
||||
): Promise<SelectNode[]> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const databases = await workspace.database
|
||||
.selectFrom('nodes')
|
||||
.where('type', '=', 'database')
|
||||
.selectAll()
|
||||
.execute();
|
||||
|
||||
return databases;
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import { SelectNode } from '@colanode/client/databases/workspace';
|
||||
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 { DatabaseViewListQueryInput } from '@colanode/client/queries/databases/database-view-list';
|
||||
import { Event } from '@colanode/client/types/events';
|
||||
import { LocalDatabaseViewNode } from '@colanode/client/types/nodes';
|
||||
import { compareString } from '@colanode/core';
|
||||
|
||||
export class DatabaseViewListQueryHandler
|
||||
extends WorkspaceQueryHandlerBase
|
||||
implements QueryHandler<DatabaseViewListQueryInput>
|
||||
{
|
||||
public async handleQuery(
|
||||
input: DatabaseViewListQueryInput
|
||||
): Promise<LocalDatabaseViewNode[]> {
|
||||
const databaseViews = await this.fetchDatabaseViews(input);
|
||||
const databaseViewNodes = databaseViews.map(
|
||||
mapNode
|
||||
) as LocalDatabaseViewNode[];
|
||||
return databaseViewNodes.sort((a, b) => compareString(a.id, b.id));
|
||||
}
|
||||
|
||||
public async checkForChanges(
|
||||
event: Event,
|
||||
input: DatabaseViewListQueryInput,
|
||||
output: LocalDatabaseViewNode[]
|
||||
): Promise<ChangeCheckResult<DatabaseViewListQueryInput>> {
|
||||
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 === 'database_view' &&
|
||||
event.node.parentId === input.databaseId
|
||||
) {
|
||||
const newResult = await this.handleQuery(input);
|
||||
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newResult,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
event.type === 'node.updated' &&
|
||||
event.workspace.userId === input.userId &&
|
||||
event.node.type === 'database_view' &&
|
||||
event.node.parentId === input.databaseId
|
||||
) {
|
||||
const databaseView = output.find((dv) => dv.id === event.node.id);
|
||||
|
||||
if (databaseView) {
|
||||
const newResult = output.map((node) => {
|
||||
if (node.id === event.node.id) {
|
||||
return event.node as LocalDatabaseViewNode;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newResult,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
event.type === 'node.deleted' &&
|
||||
event.workspace.userId === input.userId &&
|
||||
event.node.type === 'database_view' &&
|
||||
event.node.parentId === input.databaseId
|
||||
) {
|
||||
const databaseView = output.find((node) => node.id === event.node.id);
|
||||
|
||||
if (databaseView) {
|
||||
const newOutput = await this.handleQuery(input);
|
||||
return {
|
||||
hasChanges: true,
|
||||
result: newOutput,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasChanges: false,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchDatabaseViews(
|
||||
input: DatabaseViewListQueryInput
|
||||
): Promise<SelectNode[]> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const databaseViews = await workspace.database
|
||||
.selectFrom('nodes')
|
||||
.where('parent_id', '=', input.databaseId)
|
||||
.where('type', '=', 'database_view')
|
||||
.selectAll()
|
||||
.execute();
|
||||
|
||||
return databaseViews;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import { AccountListQueryHandler } from './accounts/accounts-list';
|
||||
import { MetadataListQueryHandler } from './apps/metadata-list';
|
||||
import { TabsListQueryHandler } from './apps/tabs-list';
|
||||
import { AvatarGetQueryHandler } from './avatars/avatar-get';
|
||||
import { DatabaseListQueryHandler } from './databases/database-list';
|
||||
import { DatabaseViewListQueryHandler } from './databases/database-view-list';
|
||||
import { DocumentGetQueryHandler } from './documents/document-get';
|
||||
import { DocumentStateGetQueryHandler } from './documents/document-state-get';
|
||||
import { DocumentUpdatesListQueryHandler } from './documents/document-update-list';
|
||||
@@ -82,8 +80,6 @@ export const buildQueryHandlerMap = (app: AppService): QueryHandlerMap => {
|
||||
'icon.category.list': new IconCategoryListQueryHandler(app),
|
||||
'node.children.get': new NodeChildrenGetQueryHandler(app),
|
||||
'radar.data.get': new RadarDataGetQueryHandler(app),
|
||||
'database.list': new DatabaseListQueryHandler(app),
|
||||
'database.view.list': new DatabaseViewListQueryHandler(app),
|
||||
'record.search': new RecordSearchQueryHandler(app),
|
||||
'user.storage.get': new UserStorageGetQueryHandler(app),
|
||||
'local.file.get': new LocalFileGetQueryHandler(app),
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { LocalDatabaseNode } from '@colanode/client/types/nodes';
|
||||
|
||||
export type DatabaseListQueryInput = {
|
||||
type: 'database.list';
|
||||
userId: string;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/queries' {
|
||||
interface QueryMap {
|
||||
'database.list': {
|
||||
input: DatabaseListQueryInput;
|
||||
output: LocalDatabaseNode[];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { LocalDatabaseViewNode } from '@colanode/client/types/nodes';
|
||||
|
||||
export type DatabaseViewListQueryInput = {
|
||||
type: 'database.view.list';
|
||||
userId: string;
|
||||
databaseId: string;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/queries' {
|
||||
interface QueryMap {
|
||||
'database.view.list': {
|
||||
input: DatabaseViewListQueryInput;
|
||||
output: LocalDatabaseViewNode[];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@ import { sha256 } from 'js-sha256';
|
||||
|
||||
export * from './accounts/account-list';
|
||||
export * from './apps/metadata-list';
|
||||
export * from './databases/database-list';
|
||||
export * from './databases/database-view-list';
|
||||
export * from './documents/document-get';
|
||||
export * from './documents/document-state-get';
|
||||
export * from './documents/document-updates-list';
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Collection, createLiveQueryCollection, eq } from '@tanstack/react-db';
|
||||
import {
|
||||
Download,
|
||||
LocalChatNode,
|
||||
LocalDatabaseNode,
|
||||
LocalDatabaseViewNode,
|
||||
LocalNode,
|
||||
LocalRecordNode,
|
||||
Upload,
|
||||
@@ -26,6 +28,8 @@ class WorkspaceCollections {
|
||||
public readonly downloads: Collection<Download, string>;
|
||||
public readonly uploads: Collection<Upload, string>;
|
||||
public readonly nodes: Collection<LocalNode, string>;
|
||||
public readonly databases: Collection<LocalDatabaseNode>;
|
||||
public readonly views: Collection<LocalDatabaseViewNode>;
|
||||
public readonly records: Collection<LocalRecordNode>;
|
||||
public readonly chats: Collection<LocalChatNode>;
|
||||
|
||||
@@ -36,6 +40,18 @@ class WorkspaceCollections {
|
||||
this.uploads = createUploadsCollection(userId);
|
||||
this.nodes = createNodesCollection(userId);
|
||||
|
||||
this.databases = createLiveQueryCollection((q) =>
|
||||
q
|
||||
.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'))
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useLiveQuery } from '@tanstack/react-db';
|
||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { Button } from '@colanode/ui/components/ui/button';
|
||||
import {
|
||||
@@ -17,7 +19,6 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@colanode/ui/components/ui/popover';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useLiveQuery } from '@colanode/ui/hooks/use-live-query';
|
||||
import { cn } from '@colanode/ui/lib/utils';
|
||||
|
||||
interface DatabaseSelectProps {
|
||||
@@ -29,12 +30,13 @@ export const DatabaseSelect = ({ id, onChange }: DatabaseSelectProps) => {
|
||||
const workspace = useWorkspace();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const databaseListQuery = useLiveQuery({
|
||||
type: 'database.list',
|
||||
userId: workspace.userId,
|
||||
});
|
||||
const databaseListQuery = useLiveQuery((q) =>
|
||||
q
|
||||
.from({ databases: collections.workspace(workspace.userId).databases })
|
||||
.orderBy(({ databases }) => databases.id, 'asc')
|
||||
);
|
||||
|
||||
const databases = databaseListQuery.data ?? [];
|
||||
const databases = databaseListQuery.data;
|
||||
const selectedDatabase = id
|
||||
? databases.find((database) => database.id === id)
|
||||
: undefined;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { eq, useLiveQuery } from '@tanstack/react-db';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { View } from '@colanode/ui/components/databases/view';
|
||||
import { useDatabase } from '@colanode/ui/contexts/database';
|
||||
import { DatabaseViewsContext } from '@colanode/ui/contexts/database-views';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useLiveQuery } from '@colanode/ui/hooks/use-live-query';
|
||||
|
||||
interface DatabaseViewsProps {
|
||||
inline?: boolean;
|
||||
@@ -15,13 +16,14 @@ export const DatabaseViews = ({ inline = false }: DatabaseViewsProps) => {
|
||||
const database = useDatabase();
|
||||
const [activeViewId, setActiveViewId] = useState<string | null>(null);
|
||||
|
||||
const databaseViewListQuery = useLiveQuery({
|
||||
type: 'database.view.list',
|
||||
userId: workspace.userId,
|
||||
databaseId: database.id,
|
||||
});
|
||||
const databaseViewListQuery = useLiveQuery((q) =>
|
||||
q
|
||||
.from({ views: collections.workspace(workspace.userId).views })
|
||||
.where(({ views }) => eq(views.attributes.parentId, database.id))
|
||||
.orderBy(({ views }) => views.attributes.index, 'asc')
|
||||
);
|
||||
|
||||
const views = databaseViewListQuery.data ?? [];
|
||||
const views = databaseViewListQuery.data;
|
||||
const activeView = views.find((view) => view.id === activeViewId);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user