mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Refactor app metadata
This commit is contained in:
@@ -5,7 +5,6 @@ import { createDebugger } from '@colanode/core';
|
|||||||
import { app, BrowserWindow, ipcMain, protocol, shell } from 'electron';
|
import { app, BrowserWindow, ipcMain, protocol, shell } from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { WindowSize } from '@/shared/types/metadata';
|
|
||||||
import { mediator } from '@/main/mediator';
|
import { mediator } from '@/main/mediator';
|
||||||
import { getAppIconPath } from '@/main/lib/utils';
|
import { getAppIconPath } from '@/main/lib/utils';
|
||||||
import { CommandInput, CommandMap } from '@/shared/commands';
|
import { CommandInput, CommandMap } from '@/shared/commands';
|
||||||
@@ -44,7 +43,7 @@ const createWindow = async () => {
|
|||||||
await appService.migrate();
|
await appService.migrate();
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
let windowSize = await appService.metadata.get<WindowSize>('window_size');
|
let windowSize = (await appService.metadata.get('window_size'))?.value;
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: windowSize?.width ?? 1200,
|
width: windowSize?.width ?? 1200,
|
||||||
height: windowSize?.height ?? 800,
|
height: windowSize?.height ?? 800,
|
||||||
|
|||||||
@@ -38,20 +38,20 @@ interface DeletedTokenTable {
|
|||||||
created_at: ColumnType<string, string, string>;
|
created_at: ColumnType<string, string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MetadataTable {
|
interface AppMetadataTable {
|
||||||
key: ColumnType<string, string, never>;
|
key: ColumnType<string, string, never>;
|
||||||
value: ColumnType<string, string, string>;
|
value: ColumnType<string, string, string>;
|
||||||
created_at: ColumnType<string, string, never>;
|
created_at: ColumnType<string, string, never>;
|
||||||
updated_at: ColumnType<string | null, string | null, string | null>;
|
updated_at: ColumnType<string | null, string | null, string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SelectMetadata = Selectable<MetadataTable>;
|
export type SelectAppMetadata = Selectable<AppMetadataTable>;
|
||||||
export type CreateMetadata = Insertable<MetadataTable>;
|
export type CreateAppMetadata = Insertable<AppMetadataTable>;
|
||||||
export type UpdateMetadata = Updateable<MetadataTable>;
|
export type UpdateAppMetadata = Updateable<AppMetadataTable>;
|
||||||
|
|
||||||
export interface AppDatabaseSchema {
|
export interface AppDatabaseSchema {
|
||||||
servers: ServerTable;
|
servers: ServerTable;
|
||||||
accounts: AccountTable;
|
accounts: AccountTable;
|
||||||
deleted_tokens: DeletedTokenTable;
|
deleted_tokens: DeletedTokenTable;
|
||||||
metadata: MetadataTable;
|
metadata: AppMetadataTable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import {
|
|||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
import { encodeState } from '@colanode/crdt';
|
import { encodeState } from '@colanode/crdt';
|
||||||
|
|
||||||
import { SelectAccount, SelectServer } from '@/main/databases/app';
|
import {
|
||||||
|
SelectAccount,
|
||||||
|
SelectAppMetadata,
|
||||||
|
SelectServer,
|
||||||
|
} from '@/main/databases/app';
|
||||||
import { SelectEmoji } from '@/main/databases/emojis';
|
import { SelectEmoji } from '@/main/databases/emojis';
|
||||||
import { SelectIcon } from '@/main/databases/icons';
|
import { SelectIcon } from '@/main/databases/icons';
|
||||||
import { SelectWorkspace } from '@/main/databases/account';
|
import { SelectWorkspace } from '@/main/databases/account';
|
||||||
@@ -41,6 +45,7 @@ import {
|
|||||||
import { EntryInteraction } from '@/shared/types/entries';
|
import { EntryInteraction } from '@/shared/types/entries';
|
||||||
import { Emoji } from '@/shared/types/emojis';
|
import { Emoji } from '@/shared/types/emojis';
|
||||||
import { Icon } from '@/shared/types/icons';
|
import { Icon } from '@/shared/types/icons';
|
||||||
|
import { AppMetadata, AppMetadataKey } from '@/shared/types/apps';
|
||||||
|
|
||||||
export const mapUser = (row: SelectUser): User => {
|
export const mapUser = (row: SelectUser): User => {
|
||||||
return {
|
return {
|
||||||
@@ -291,6 +296,14 @@ export const mapIcon = (row: SelectIcon): Icon => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mapAppMetadata = (row: SelectAppMetadata): AppMetadata => {
|
||||||
|
return {
|
||||||
|
key: row.key as AppMetadataKey,
|
||||||
|
value: JSON.parse(row.value),
|
||||||
|
createdAt: row.created_at,
|
||||||
|
updatedAt: row.updated_at,
|
||||||
|
};
|
||||||
|
};
|
||||||
export const mapWorkspaceMetadata = (
|
export const mapWorkspaceMetadata = (
|
||||||
row: SelectWorkspaceMetadata
|
row: SelectWorkspaceMetadata
|
||||||
): WorkspaceMetadata => {
|
): WorkspaceMetadata => {
|
||||||
|
|||||||
66
apps/desktop/src/main/queries/apps/app-metadata-list.ts
Normal file
66
apps/desktop/src/main/queries/apps/app-metadata-list.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { ChangeCheckResult, QueryHandler } from '@/main/lib/types';
|
||||||
|
import { mapAppMetadata } from '@/main/lib/mappers';
|
||||||
|
import { AppMetadataListQueryInput } from '@/shared/queries/apps/app-metadata-list';
|
||||||
|
import { Event } from '@/shared/types/events';
|
||||||
|
import { AppMetadata } from '@/shared/types/apps';
|
||||||
|
import { SelectAppMetadata } from '@/main/databases/app/schema';
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
|
||||||
|
export class AppMetadataListQueryHandler
|
||||||
|
implements QueryHandler<AppMetadataListQueryInput>
|
||||||
|
{
|
||||||
|
public async handleQuery(
|
||||||
|
_: AppMetadataListQueryInput
|
||||||
|
): Promise<AppMetadata[]> {
|
||||||
|
const rows = await this.getAppMetadata();
|
||||||
|
if (!rows) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.map(mapAppMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkForChanges(
|
||||||
|
event: Event,
|
||||||
|
_: AppMetadataListQueryInput,
|
||||||
|
output: AppMetadata[]
|
||||||
|
): Promise<ChangeCheckResult<AppMetadataListQueryInput>> {
|
||||||
|
if (event.type === 'app_metadata_updated') {
|
||||||
|
const newOutput = output.map((metadata) => {
|
||||||
|
if (metadata.key === event.metadata.key) {
|
||||||
|
return event.metadata;
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasChanges: true,
|
||||||
|
result: newOutput,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'app_metadata_deleted') {
|
||||||
|
const newOutput = output.filter(
|
||||||
|
(metadata) => metadata.key !== event.metadata.key
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasChanges: true,
|
||||||
|
result: newOutput,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasChanges: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getAppMetadata(): Promise<SelectAppMetadata[] | undefined> {
|
||||||
|
const rows = await appService.database
|
||||||
|
.selectFrom('metadata')
|
||||||
|
.selectAll()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AppMetadataListQueryHandler } from '@/main/queries/apps/app-metadata-list';
|
||||||
import { AccountGetQueryHandler } from '@/main/queries/accounts/account-get';
|
import { AccountGetQueryHandler } from '@/main/queries/accounts/account-get';
|
||||||
import { AccountListQueryHandler } from '@/main/queries/accounts/accounts-list';
|
import { AccountListQueryHandler } from '@/main/queries/accounts/accounts-list';
|
||||||
import { EmojiGetQueryHandler } from '@/main/queries/emojis/emoji-get';
|
import { EmojiGetQueryHandler } from '@/main/queries/emojis/emoji-get';
|
||||||
@@ -41,6 +42,7 @@ type QueryHandlerMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const queryHandlerMap: QueryHandlerMap = {
|
export const queryHandlerMap: QueryHandlerMap = {
|
||||||
|
app_metadata_list: new AppMetadataListQueryHandler(),
|
||||||
account_list: new AccountListQueryHandler(),
|
account_list: new AccountListQueryHandler(),
|
||||||
message_list: new MessageListQueryHandler(),
|
message_list: new MessageListQueryHandler(),
|
||||||
message_reaction_list: new MessageReactionsListQueryHandler(),
|
message_reaction_list: new MessageReactionsListQueryHandler(),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { EventLoop } from '@/main/lib/event-loop';
|
|||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
import { NotificationService } from '@/main/services/notification-service';
|
import { NotificationService } from '@/main/services/notification-service';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
|
import { AppPlatform } from '@/shared/types/apps';
|
||||||
|
|
||||||
export class AppService {
|
export class AppService {
|
||||||
private readonly debug = createDebugger('desktop:service:app');
|
private readonly debug = createDebugger('desktop:service:app');
|
||||||
@@ -74,6 +75,9 @@ export class AppService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await migrator.migrateToLatest();
|
await migrator.migrateToLatest();
|
||||||
|
|
||||||
|
await this.metadata.set('version', this.version);
|
||||||
|
await this.metadata.set('platform', process.platform as AppPlatform);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAccount(id: string): AccountService | null {
|
public getAccount(id: string): AccountService | null {
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { createDebugger } from '@colanode/core';
|
import { createDebugger } from '@colanode/core';
|
||||||
|
|
||||||
import { AppService } from '@/main/services/app-service';
|
import { AppService } from '@/main/services/app-service';
|
||||||
|
import {
|
||||||
|
AppMetadata,
|
||||||
|
AppMetadataMap,
|
||||||
|
AppMetadataKey,
|
||||||
|
} from '@/shared/types/apps';
|
||||||
|
import { mapAppMetadata } from '@/main/lib/mappers';
|
||||||
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
|
|
||||||
export class MetadataService {
|
export class MetadataService {
|
||||||
private readonly debug = createDebugger('desktop:service:metadata');
|
private readonly debug = createDebugger('desktop:service:metadata');
|
||||||
@@ -10,7 +17,18 @@ export class MetadataService {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async get<T>(key: string): Promise<T | null> {
|
public async getAll(): Promise<AppMetadata[]> {
|
||||||
|
const metadata = await this.app.database
|
||||||
|
.selectFrom('metadata')
|
||||||
|
.selectAll()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return metadata.map(mapAppMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async get<K extends AppMetadataKey>(
|
||||||
|
key: K
|
||||||
|
): Promise<AppMetadataMap[K] | null> {
|
||||||
const metadata = await this.app.database
|
const metadata = await this.app.database
|
||||||
.selectFrom('metadata')
|
.selectFrom('metadata')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
@@ -21,14 +39,18 @@ export class MetadataService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.parse(metadata.value) as T;
|
return mapAppMetadata(metadata) as AppMetadataMap[K];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async set<T>(key: string, value: T) {
|
public async set<K extends AppMetadataKey>(
|
||||||
|
key: K,
|
||||||
|
value: AppMetadataMap[K]['value']
|
||||||
|
) {
|
||||||
this.debug(`Setting metadata key ${key} to value ${value}`);
|
this.debug(`Setting metadata key ${key} to value ${value}`);
|
||||||
|
|
||||||
await this.app.database
|
const createdMetadata = await this.app.database
|
||||||
.insertInto('metadata')
|
.insertInto('metadata')
|
||||||
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
key,
|
key,
|
||||||
value: JSON.stringify(value),
|
value: JSON.stringify(value),
|
||||||
@@ -40,15 +62,34 @@ export class MetadataService {
|
|||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.execute();
|
.executeTakeFirst();
|
||||||
|
|
||||||
|
if (!createdMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBus.publish({
|
||||||
|
type: 'app_metadata_updated',
|
||||||
|
metadata: mapAppMetadata(createdMetadata),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async delete(key: string) {
|
public async delete(key: string) {
|
||||||
this.debug(`Deleting metadata key ${key}`);
|
this.debug(`Deleting metadata key ${key}`);
|
||||||
|
|
||||||
await this.app.database
|
const deletedMetadata = await this.app.database
|
||||||
.deleteFrom('metadata')
|
.deleteFrom('metadata')
|
||||||
.where('key', '=', key)
|
.where('key', '=', key)
|
||||||
.execute();
|
.returningAll()
|
||||||
|
.executeTakeFirst();
|
||||||
|
|
||||||
|
if (!deletedMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBus.publish({
|
||||||
|
type: 'app_metadata_deleted',
|
||||||
|
metadata: mapAppMetadata(deletedMetadata),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Spinner } from '@/renderer/components/ui/spinner';
|
import { Spinner } from '@/renderer/components/ui/spinner';
|
||||||
|
|
||||||
export const RootLoader = () => {
|
export const AppLoader = () => {
|
||||||
return (
|
return (
|
||||||
<div className="min-w-screen flex h-full min-h-screen w-full items-center justify-center">
|
<div className="min-w-screen flex h-full min-h-screen w-full items-center justify-center">
|
||||||
<div className="flex flex-col items-center gap-8 text-center">
|
<div className="flex flex-col items-center gap-8 text-center">
|
||||||
@@ -1,11 +1,45 @@
|
|||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { AppContext } from './contexts/app';
|
||||||
|
|
||||||
|
import { DelayedComponent } from '@/renderer/components/ui/delayed-component';
|
||||||
|
import { AppLoader } from '@/renderer/app-loader';
|
||||||
|
import { useQuery } from '@/renderer/hooks/use-query';
|
||||||
import { RadarProvider } from '@/renderer/radar-provider';
|
import { RadarProvider } from '@/renderer/radar-provider';
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
|
const [initialized, setInitialized] = React.useState(false);
|
||||||
|
|
||||||
|
const { data, isPending } = useQuery({
|
||||||
|
type: 'app_metadata_list',
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
window.colanode.init().then(() => {
|
||||||
|
setInitialized(true);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!initialized || isPending) {
|
||||||
|
return (
|
||||||
|
<DelayedComponent>
|
||||||
|
<AppLoader />
|
||||||
|
</DelayedComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RadarProvider>
|
<AppContext.Provider
|
||||||
<Outlet />
|
value={{
|
||||||
</RadarProvider>
|
getMetadata: (key: string) => {
|
||||||
|
return data?.find((metadata) => metadata.key === key)?.value;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RadarProvider>
|
||||||
|
<Outlet />
|
||||||
|
</RadarProvider>
|
||||||
|
</AppContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { SidebarMenuIcon } from '@/renderer/components/layouts/sidebars/sidebar-
|
|||||||
import { SidebarMenuHeader } from '@/renderer/components/layouts/sidebars/sidebar-menu-header';
|
import { SidebarMenuHeader } from '@/renderer/components/layouts/sidebars/sidebar-menu-header';
|
||||||
import { SidebarMenuFooter } from '@/renderer/components/layouts/sidebars/sidebar-menu-footer';
|
import { SidebarMenuFooter } from '@/renderer/components/layouts/sidebars/sidebar-menu-footer';
|
||||||
import { SidebarMenuType } from '@/shared/types/workspaces';
|
import { SidebarMenuType } from '@/shared/types/workspaces';
|
||||||
|
import { useApp } from '@/renderer/contexts/app';
|
||||||
|
|
||||||
interface SidebarMenuProps {
|
interface SidebarMenuProps {
|
||||||
value: SidebarMenuType;
|
value: SidebarMenuType;
|
||||||
@@ -11,13 +12,22 @@ interface SidebarMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SidebarMenu = ({ value, onChange }: SidebarMenuProps) => {
|
export const SidebarMenu = ({ value, onChange }: SidebarMenuProps) => {
|
||||||
|
const app = useApp();
|
||||||
|
const platform = app.getMetadata('platform');
|
||||||
|
const windowSize = app.getMetadata('window_size');
|
||||||
|
const showMacOsPlaceholder = platform === 'darwin' && !windowSize?.fullscreen;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full w-[65px] min-w-[65px] items-center bg-slate-100">
|
<div className="flex flex-col h-full w-[65px] min-w-[65px] items-center bg-slate-100">
|
||||||
<div className="w-full h-8 flex gap-[8px] px-[6px] py-[7px]">
|
{showMacOsPlaceholder ? (
|
||||||
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
<div className="w-full h-8 flex gap-[8px] px-[6px] py-[7px]">
|
||||||
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
||||||
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
||||||
</div>
|
<div className="w-3 h-3 bg-gray-400 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-full h-4" />
|
||||||
|
)}
|
||||||
<SidebarMenuHeader />
|
<SidebarMenuHeader />
|
||||||
<div className="flex flex-col gap-1 mt-2 w-full p-2 items-center flex-grow">
|
<div className="flex flex-col gap-1 mt-2 w-full p-2 items-center flex-grow">
|
||||||
<SidebarMenuIcon
|
<SidebarMenuIcon
|
||||||
|
|||||||
13
apps/desktop/src/renderer/contexts/app.ts
Normal file
13
apps/desktop/src/renderer/contexts/app.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
import { AppMetadataKey, AppMetadataMap } from '@/shared/types/apps';
|
||||||
|
|
||||||
|
interface AppContext {
|
||||||
|
getMetadata: <K extends AppMetadataKey>(
|
||||||
|
key: K
|
||||||
|
) => AppMetadataMap[K]['value'] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppContext = createContext<AppContext>({} as AppContext);
|
||||||
|
|
||||||
|
export const useApp = () => useContext(AppContext);
|
||||||
@@ -11,14 +11,12 @@ import { App } from '@/renderer/app';
|
|||||||
import { Account } from '@/renderer/components/accounts/account';
|
import { Account } from '@/renderer/components/accounts/account';
|
||||||
import { AccountRedirect } from '@/renderer/components/accounts/account-redirect';
|
import { AccountRedirect } from '@/renderer/components/accounts/account-redirect';
|
||||||
import { Login } from '@/renderer/components/accounts/login';
|
import { Login } from '@/renderer/components/accounts/login';
|
||||||
import { DelayedComponent } from '@/renderer/components/ui/delayed-component';
|
|
||||||
import { Toaster } from '@/renderer/components/ui/toaster';
|
import { Toaster } from '@/renderer/components/ui/toaster';
|
||||||
import { TooltipProvider } from '@/renderer/components/ui/tooltip';
|
import { TooltipProvider } from '@/renderer/components/ui/tooltip';
|
||||||
import { Workspace } from '@/renderer/components/workspaces/workspace';
|
import { Workspace } from '@/renderer/components/workspaces/workspace';
|
||||||
import { WorkspaceCreate } from '@/renderer/components/workspaces/workspace-create';
|
import { WorkspaceCreate } from '@/renderer/components/workspaces/workspace-create';
|
||||||
import { WorkspaceRedirect } from '@/renderer/components/workspaces/workspace-redirect';
|
import { WorkspaceRedirect } from '@/renderer/components/workspaces/workspace-redirect';
|
||||||
import { useEventBus } from '@/renderer/hooks/use-event-bus';
|
import { useEventBus } from '@/renderer/hooks/use-event-bus';
|
||||||
import { RootLoader } from '@/renderer/root-loader';
|
|
||||||
import { Event } from '@/shared/types/events';
|
import { Event } from '@/shared/types/events';
|
||||||
|
|
||||||
const router = createHashRouter([
|
const router = createHashRouter([
|
||||||
@@ -56,7 +54,7 @@ const router = createHashRouter([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
export const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
networkMode: 'always',
|
networkMode: 'always',
|
||||||
@@ -69,7 +67,6 @@ const queryClient = new QueryClient({
|
|||||||
|
|
||||||
const Root = () => {
|
const Root = () => {
|
||||||
const eventBus = useEventBus();
|
const eventBus = useEventBus();
|
||||||
const [initialized, setInitialized] = React.useState(false);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const id = eventBus.subscribe((event: Event) => {
|
const id = eventBus.subscribe((event: Event) => {
|
||||||
@@ -96,23 +93,11 @@ const Root = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.colanode.init().then(() => {
|
|
||||||
setInitialized(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
eventBus.unsubscribe(id);
|
eventBus.unsubscribe(id);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
return (
|
|
||||||
<DelayedComponent>
|
|
||||||
<RootLoader />
|
|
||||||
</DelayedComponent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
|
|||||||
14
apps/desktop/src/shared/queries/apps/app-metadata-list.ts
Normal file
14
apps/desktop/src/shared/queries/apps/app-metadata-list.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { AppMetadata } from '@/shared/types/apps';
|
||||||
|
|
||||||
|
export type AppMetadataListQueryInput = {
|
||||||
|
type: 'app_metadata_list';
|
||||||
|
};
|
||||||
|
|
||||||
|
declare module '@/shared/queries' {
|
||||||
|
interface QueryMap {
|
||||||
|
app_metadata_list: {
|
||||||
|
input: AppMetadataListQueryInput;
|
||||||
|
output: AppMetadata[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
48
apps/desktop/src/shared/types/apps.ts
Normal file
48
apps/desktop/src/shared/types/apps.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
export type AppPlatform =
|
||||||
|
| 'aix'
|
||||||
|
| 'darwin'
|
||||||
|
| 'freebsd'
|
||||||
|
| 'linux'
|
||||||
|
| 'openbsd'
|
||||||
|
| 'sunos'
|
||||||
|
| 'win32';
|
||||||
|
|
||||||
|
export type WindowSize = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
fullscreen: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppPlatformMetadata = {
|
||||||
|
key: 'platform';
|
||||||
|
value: AppPlatform;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppVersionMetadata = {
|
||||||
|
key: 'version';
|
||||||
|
value: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppWindowSizeMetadata = {
|
||||||
|
key: 'window_size';
|
||||||
|
value: WindowSize;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppMetadata =
|
||||||
|
| AppPlatformMetadata
|
||||||
|
| AppVersionMetadata
|
||||||
|
| AppWindowSizeMetadata;
|
||||||
|
|
||||||
|
export type AppMetadataKey = AppMetadata['key'];
|
||||||
|
|
||||||
|
export type AppMetadataMap = {
|
||||||
|
platform: AppPlatformMetadata;
|
||||||
|
version: AppVersionMetadata;
|
||||||
|
window_size: AppWindowSizeMetadata;
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Entry, Message } from '@colanode/core';
|
import { Entry, Message } from '@colanode/core';
|
||||||
|
|
||||||
|
import { AppMetadata } from '@/shared/types/apps';
|
||||||
import { EntryInteraction } from '@/shared/types/entries';
|
import { EntryInteraction } from '@/shared/types/entries';
|
||||||
import {
|
import {
|
||||||
MessageInteraction,
|
MessageInteraction,
|
||||||
@@ -238,6 +239,16 @@ export type AccountConnectionMessageEvent = {
|
|||||||
message: Message;
|
message: Message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AppMetadataUpdatedEvent = {
|
||||||
|
type: 'app_metadata_updated';
|
||||||
|
metadata: AppMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppMetadataDeletedEvent = {
|
||||||
|
type: 'app_metadata_deleted';
|
||||||
|
metadata: AppMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
export type WorkspaceMetadataUpdatedEvent = {
|
export type WorkspaceMetadataUpdatedEvent = {
|
||||||
type: 'workspace_metadata_updated';
|
type: 'workspace_metadata_updated';
|
||||||
accountId: string;
|
accountId: string;
|
||||||
@@ -289,5 +300,7 @@ export type Event =
|
|||||||
| AccountConnectionOpenedEvent
|
| AccountConnectionOpenedEvent
|
||||||
| AccountConnectionClosedEvent
|
| AccountConnectionClosedEvent
|
||||||
| AccountConnectionMessageEvent
|
| AccountConnectionMessageEvent
|
||||||
|
| AppMetadataUpdatedEvent
|
||||||
|
| AppMetadataDeletedEvent
|
||||||
| WorkspaceMetadataUpdatedEvent
|
| WorkspaceMetadataUpdatedEvent
|
||||||
| WorkspaceMetadataDeletedEvent;
|
| WorkspaceMetadataDeletedEvent;
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export type WindowSize = {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
fullscreen: boolean;
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user