mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Improve tanstack db collections
This commit is contained in:
@@ -26,8 +26,6 @@ export const AppProvider = ({ type }: AppProviderProps) => {
|
||||
database
|
||||
.preload()
|
||||
.then(() => {
|
||||
console.log('Colanode | Database', database);
|
||||
console.log('Colanode | Preloaded');
|
||||
setIsInitialized(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -105,7 +105,7 @@ export const LayoutDesktop = () => {
|
||||
routeTree,
|
||||
context: {},
|
||||
history: createMemoryHistory({
|
||||
initialEntries: [tab.location],
|
||||
initialEntries: [tab.location ?? '/'],
|
||||
}),
|
||||
defaultPreload: 'intent',
|
||||
scrollRestoration: true,
|
||||
|
||||
@@ -2,21 +2,19 @@ import { Plus } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useTabManager } from '@colanode/ui/contexts/tab-manager';
|
||||
import { useAccountMetadata } from '@colanode/ui/hooks/use-account-metadata';
|
||||
import { useAppMetadata } from '@colanode/ui/hooks/use-app-metadata';
|
||||
|
||||
export const TabAddButton = () => {
|
||||
const tabManager = useTabManager();
|
||||
const lastActiveAccount = useAppMetadata('account');
|
||||
const lastActiveWorkspace = useAccountMetadata('workspace');
|
||||
|
||||
const location = useMemo(() => {
|
||||
if (lastActiveAccount && lastActiveWorkspace) {
|
||||
return `/acc/${lastActiveAccount}/${lastActiveWorkspace}/home`;
|
||||
if (lastActiveAccount) {
|
||||
return `/acc/${lastActiveAccount}`;
|
||||
}
|
||||
|
||||
return '/';
|
||||
}, [lastActiveAccount, lastActiveWorkspace]);
|
||||
}, [lastActiveAccount]);
|
||||
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
||||
import { createCollection } from '@tanstack/react-db';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { buildQueryKey, AccountListQueryInput } from '@colanode/client/queries';
|
||||
import { Account } from '@colanode/client/types';
|
||||
|
||||
export const createAccountsCollection = (queryClient: QueryClient) => {
|
||||
const input: AccountListQueryInput = {
|
||||
type: 'account.list',
|
||||
};
|
||||
export const createAccountsCollection = () => {
|
||||
return createCollection<Account, string>({
|
||||
getKey(item) {
|
||||
return item.id;
|
||||
},
|
||||
sync: {
|
||||
async sync({ begin, write, commit, markReady }) {
|
||||
const accounts = await window.colanode.executeQuery({
|
||||
type: 'account.list',
|
||||
});
|
||||
|
||||
const key = buildQueryKey(input);
|
||||
begin();
|
||||
|
||||
return createCollection(
|
||||
queryCollectionOptions({
|
||||
id: key,
|
||||
queryKey: [key],
|
||||
queryClient,
|
||||
getKey: (item) => item.id,
|
||||
queryFn: async () => {
|
||||
return await window.colanode.executeQueryAndSubscribe(key, input);
|
||||
for (const account of accounts) {
|
||||
write({ type: 'insert', value: account });
|
||||
}
|
||||
|
||||
commit();
|
||||
markReady();
|
||||
|
||||
window.eventBus.subscribe((event) => {
|
||||
if (event.type === 'account.created') {
|
||||
begin();
|
||||
write({ type: 'insert', value: event.account });
|
||||
commit();
|
||||
} else if (event.type === 'account.updated') {
|
||||
begin();
|
||||
write({ type: 'update', value: event.account });
|
||||
commit();
|
||||
} else if (event.type === 'account.deleted') {
|
||||
begin();
|
||||
write({ type: 'delete', value: event.account });
|
||||
commit();
|
||||
}
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,70 +1,89 @@
|
||||
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
||||
import { createCollection } from '@tanstack/react-db';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import {
|
||||
AppMetadataListQueryInput,
|
||||
buildQueryKey,
|
||||
} from '@colanode/client/queries';
|
||||
import { AppMetadata } from '@colanode/client/types';
|
||||
|
||||
export const createAppMetadataCollection = (queryClient: QueryClient) => {
|
||||
const input: AppMetadataListQueryInput = {
|
||||
type: 'app.metadata.list',
|
||||
};
|
||||
const key = buildQueryKey(input);
|
||||
export const createAppMetadataCollection = () => {
|
||||
return createCollection<AppMetadata, string>({
|
||||
getKey(item) {
|
||||
return item.key;
|
||||
},
|
||||
sync: {
|
||||
async sync({ begin, write, commit, markReady, collection }) {
|
||||
const appMetadata = await window.colanode.executeQuery({
|
||||
type: 'app.metadata.list',
|
||||
});
|
||||
|
||||
return createCollection(
|
||||
queryCollectionOptions({
|
||||
id: key,
|
||||
queryKey: [key],
|
||||
queryClient,
|
||||
getKey: (item) => item.key,
|
||||
queryFn: async () => {
|
||||
return await window.colanode.executeQueryAndSubscribe(key, input);
|
||||
},
|
||||
onInsert: async ({ transaction }) => {
|
||||
const metadata = transaction.mutations[0].modified;
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'app.metadata.update',
|
||||
key: metadata.key,
|
||||
value: metadata.value,
|
||||
begin();
|
||||
|
||||
for (const item of appMetadata) {
|
||||
write({ type: 'insert', value: item });
|
||||
}
|
||||
|
||||
commit();
|
||||
markReady();
|
||||
|
||||
window.eventBus.subscribe((event) => {
|
||||
if (event.type === 'app.metadata.updated') {
|
||||
const existing = collection.get(event.metadata.key);
|
||||
if (existing) {
|
||||
begin();
|
||||
write({ type: 'update', value: event.metadata });
|
||||
commit();
|
||||
} else {
|
||||
begin();
|
||||
write({ type: 'insert', value: event.metadata });
|
||||
commit();
|
||||
}
|
||||
} else if (event.type === 'app.metadata.deleted') {
|
||||
begin();
|
||||
write({ type: 'delete', value: event.metadata });
|
||||
commit();
|
||||
}
|
||||
});
|
||||
},
|
||||
onUpdate: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original, changes } = mutation;
|
||||
if (!(`key` in original)) {
|
||||
throw new Error(`Original todo not found for update`);
|
||||
}
|
||||
},
|
||||
onInsert: async ({ transaction }) => {
|
||||
const metadata = transaction.mutations[0].modified;
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'app.metadata.update',
|
||||
key: metadata.key,
|
||||
value: metadata.value,
|
||||
});
|
||||
},
|
||||
onUpdate: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original, changes } = mutation;
|
||||
if (!(`key` in original)) {
|
||||
throw new Error(`Original todo not found for update`);
|
||||
}
|
||||
|
||||
if (!changes.value) {
|
||||
return;
|
||||
}
|
||||
if (!changes.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'app.metadata.update',
|
||||
key: original.key,
|
||||
value: changes.value,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
onDelete: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original } = mutation;
|
||||
if (!(`key` in original)) {
|
||||
throw new Error(`Original app metadata not found for delete`);
|
||||
}
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'app.metadata.update',
|
||||
key: original.key,
|
||||
value: changes.value,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
onDelete: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original } = mutation;
|
||||
if (!(`key` in original)) {
|
||||
throw new Error(`Original app metadata not found for delete`);
|
||||
}
|
||||
|
||||
await window.colanode.executeMutation({
|
||||
type: 'app.metadata.delete',
|
||||
key: original.key,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
await window.colanode.executeMutation({
|
||||
type: 'app.metadata.delete',
|
||||
key: original.key,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -85,10 +85,10 @@ class WorkspaceDatabase {
|
||||
}
|
||||
|
||||
class AppDatabase {
|
||||
public readonly servers = createServersCollection(queryClient);
|
||||
public readonly accounts = createAccountsCollection(queryClient);
|
||||
public readonly tabs = createTabsCollection(queryClient);
|
||||
public readonly metadata = createAppMetadataCollection(queryClient);
|
||||
public readonly servers = createServersCollection();
|
||||
public readonly accounts = createAccountsCollection();
|
||||
public readonly tabs = createTabsCollection();
|
||||
public readonly metadata = createAppMetadataCollection();
|
||||
|
||||
private readonly accountDatabases: Map<string, AccountDatabase> = new Map();
|
||||
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
||||
import { createCollection } from '@tanstack/react-db';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { buildQueryKey, ServerListQueryInput } from '@colanode/client/queries';
|
||||
import { Server } from '@colanode/client/types';
|
||||
|
||||
export const createServersCollection = (queryClient: QueryClient) => {
|
||||
const input: ServerListQueryInput = {
|
||||
type: 'server.list',
|
||||
};
|
||||
export const createServersCollection = () => {
|
||||
return createCollection<Server, string>({
|
||||
getKey(item) {
|
||||
return item.domain;
|
||||
},
|
||||
sync: {
|
||||
async sync({ begin, write, commit, markReady }) {
|
||||
const servers = await window.colanode.executeQuery({
|
||||
type: 'server.list',
|
||||
});
|
||||
|
||||
const key = buildQueryKey(input);
|
||||
begin();
|
||||
|
||||
return createCollection(
|
||||
queryCollectionOptions({
|
||||
id: 'servers',
|
||||
queryKey: [key],
|
||||
queryClient,
|
||||
getKey: (item) => item.domain,
|
||||
queryFn: async () => {
|
||||
console.log('Colanode | Executing query', key, input);
|
||||
return await window.colanode.executeQueryAndSubscribe(key, input);
|
||||
for (const server of servers) {
|
||||
write({ type: 'insert', value: server });
|
||||
}
|
||||
|
||||
commit();
|
||||
markReady();
|
||||
|
||||
window.eventBus.subscribe((event) => {
|
||||
if (event.type === 'server.created') {
|
||||
begin();
|
||||
write({ type: 'insert', value: event.server });
|
||||
commit();
|
||||
} else if (event.type === 'server.updated') {
|
||||
begin();
|
||||
write({ type: 'update', value: event.server });
|
||||
commit();
|
||||
} else if (event.type === 'server.deleted') {
|
||||
begin();
|
||||
write({ type: 'delete', value: event.server });
|
||||
commit();
|
||||
}
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,66 +1,84 @@
|
||||
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
||||
import { createCollection } from '@tanstack/react-db';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { buildQueryKey, TabsListQueryInput } from '@colanode/client/queries';
|
||||
import { Tab } from '@colanode/client/types';
|
||||
|
||||
export const createTabsCollection = (queryClient: QueryClient) => {
|
||||
const input: TabsListQueryInput = {
|
||||
type: 'tabs.list',
|
||||
};
|
||||
export const createTabsCollection = () => {
|
||||
return createCollection<Tab, string>({
|
||||
getKey(item) {
|
||||
return item.id;
|
||||
},
|
||||
sync: {
|
||||
async sync({ begin, write, commit, markReady }) {
|
||||
const tabs = await window.colanode.executeQuery({
|
||||
type: 'tabs.list',
|
||||
});
|
||||
|
||||
const key = buildQueryKey(input);
|
||||
begin();
|
||||
|
||||
return createCollection(
|
||||
queryCollectionOptions({
|
||||
id: key,
|
||||
queryKey: [key],
|
||||
queryClient,
|
||||
getKey: (item) => item.id,
|
||||
queryFn: async () => {
|
||||
return await window.colanode.executeQueryAndSubscribe(key, input);
|
||||
},
|
||||
onInsert: async ({ transaction }) => {
|
||||
const tab = transaction.mutations[0].modified;
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'tab.create',
|
||||
id: tab.id,
|
||||
location: tab.location,
|
||||
index: tab.index,
|
||||
for (const tab of tabs) {
|
||||
write({ type: 'insert', value: tab });
|
||||
}
|
||||
|
||||
commit();
|
||||
markReady();
|
||||
|
||||
window.eventBus.subscribe((event) => {
|
||||
if (event.type === 'tab.created') {
|
||||
begin();
|
||||
write({ type: 'insert', value: event.tab });
|
||||
commit();
|
||||
} else if (event.type === 'tab.updated') {
|
||||
begin();
|
||||
write({ type: 'update', value: event.tab });
|
||||
commit();
|
||||
} else if (event.type === 'tab.deleted') {
|
||||
begin();
|
||||
write({ type: 'delete', value: event.tab });
|
||||
commit();
|
||||
}
|
||||
});
|
||||
},
|
||||
onUpdate: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original, changes } = mutation;
|
||||
if (!(`id` in original)) {
|
||||
throw new Error(`Original todo not found for update`);
|
||||
}
|
||||
},
|
||||
onInsert: async ({ transaction }) => {
|
||||
const tab = transaction.mutations[0].modified;
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'tab.create',
|
||||
id: tab.id,
|
||||
location: tab.location,
|
||||
index: tab.index,
|
||||
});
|
||||
},
|
||||
onUpdate: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original, changes } = mutation;
|
||||
if (!(`id` in original)) {
|
||||
throw new Error(`Original todo not found for update`);
|
||||
}
|
||||
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'tab.update',
|
||||
id: original.id,
|
||||
location: changes.location,
|
||||
index: changes.index,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
onDelete: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original } = mutation;
|
||||
if (!(`id` in original)) {
|
||||
throw new Error(`Original todo not found for delete`);
|
||||
}
|
||||
return await window.colanode.executeMutation({
|
||||
type: 'tab.update',
|
||||
id: original.id,
|
||||
location: changes.location,
|
||||
index: changes.index,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
onDelete: async ({ transaction }) => {
|
||||
return await Promise.all(
|
||||
transaction.mutations.map(async (mutation) => {
|
||||
const { original } = mutation;
|
||||
if (!(`id` in original)) {
|
||||
throw new Error(`Original todo not found for delete`);
|
||||
}
|
||||
|
||||
await window.colanode.executeMutation({
|
||||
type: 'tab.delete',
|
||||
id: original.id,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
await window.colanode.executeMutation({
|
||||
type: 'tab.delete',
|
||||
id: original.id,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user