diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 285eafcb..12bd2612 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -95,7 +95,7 @@ "lowlight": "^3.2.0", "lucide-react": "^0.473.0", "mime-types": "^2.1.35", - "re-resizable": "^6.10.1", + "re-resizable": "^6.10.3", "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dnd": "^16.0.1", diff --git a/apps/desktop/src/main/databases/workspace/migrations/00016-create-metadata-table.tsx b/apps/desktop/src/main/databases/workspace/migrations/00016-create-metadata-table.tsx new file mode 100644 index 00000000..e7424d2f --- /dev/null +++ b/apps/desktop/src/main/databases/workspace/migrations/00016-create-metadata-table.tsx @@ -0,0 +1,16 @@ +import { Migration } from 'kysely'; + +export const createMetadataTable: Migration = { + up: async (db) => { + await db.schema + .createTable('metadata') + .addColumn('key', 'text', (col) => col.notNull().primaryKey()) + .addColumn('value', 'text', (col) => col.notNull()) + .addColumn('created_at', 'text', (col) => col.notNull()) + .addColumn('updated_at', 'text') + .execute(); + }, + down: async (db) => { + await db.schema.dropTable('metadata').execute(); + }, +}; diff --git a/apps/desktop/src/main/databases/workspace/migrations/index.ts b/apps/desktop/src/main/databases/workspace/migrations/index.ts index 77f56ee9..87fb43e9 100644 --- a/apps/desktop/src/main/databases/workspace/migrations/index.ts +++ b/apps/desktop/src/main/databases/workspace/migrations/index.ts @@ -15,6 +15,7 @@ import { createMutationsTable } from './00012-create-mutations-table'; import { createEntryPathsTable } from './00013-create-entry-paths-table'; import { createTextsTable } from './00014-create-texts-table'; import { createCursorsTable } from './00015-create-cursors-table'; +import { createMetadataTable } from './00016-create-metadata-table'; export const workspaceDatabaseMigrations: Record = { '00001-create-users-table': createUsersTable, @@ -32,4 +33,5 @@ export const workspaceDatabaseMigrations: Record = { '00013-create-entry-paths-table': createEntryPathsTable, '00014-create-texts-table': createTextsTable, '00015-create-cursors-table': createCursorsTable, + '00016-create-metadata-table': createMetadataTable, }; diff --git a/apps/desktop/src/main/databases/workspace/schema.ts b/apps/desktop/src/main/databases/workspace/schema.ts index 632d5169..268f6c4a 100644 --- a/apps/desktop/src/main/databases/workspace/schema.ts +++ b/apps/desktop/src/main/databases/workspace/schema.ts @@ -231,6 +231,21 @@ interface CursorTable { updated_at: ColumnType; } +export type SelectCursor = Selectable; +export type CreateCursor = Insertable; +export type UpdateCursor = Updateable; + +interface MetadataTable { + key: ColumnType; + value: ColumnType; + created_at: ColumnType; + updated_at: ColumnType; +} + +export type SelectWorkspaceMetadata = Selectable; +export type CreateWorkspaceMetadata = Insertable; +export type UpdateWorkspaceMetadata = Updateable; + export interface WorkspaceDatabaseSchema { users: UserTable; entries: EntryTable; @@ -247,4 +262,5 @@ export interface WorkspaceDatabaseSchema { mutations: MutationTable; texts: TextTable; cursors: CursorTable; + metadata: MetadataTable; } diff --git a/apps/desktop/src/main/lib/mappers.ts b/apps/desktop/src/main/lib/mappers.ts index a182ab77..d8cb434b 100644 --- a/apps/desktop/src/main/lib/mappers.ts +++ b/apps/desktop/src/main/lib/mappers.ts @@ -22,12 +22,17 @@ import { SelectMessageInteraction, SelectFileInteraction, SelectEntryInteraction, + SelectWorkspaceMetadata, } from '@/main/databases/workspace'; import { Account } from '@/shared/types/accounts'; import { Server } from '@/shared/types/servers'; import { User } from '@/shared/types/users'; import { File, FileInteraction, FileState } from '@/shared/types/files'; -import { Workspace } from '@/shared/types/workspaces'; +import { + Workspace, + WorkspaceMetadata, + WorkspaceMetadataKey, +} from '@/shared/types/workspaces'; import { MessageInteraction, MessageNode, @@ -285,3 +290,14 @@ export const mapIcon = (row: SelectIcon): Icon => { tags: row.tags ? JSON.parse(row.tags) : [], }; }; + +export const mapWorkspaceMetadata = ( + row: SelectWorkspaceMetadata +): WorkspaceMetadata => { + return { + key: row.key as WorkspaceMetadataKey, + value: JSON.parse(row.value), + createdAt: row.created_at, + updatedAt: row.updated_at, + }; +}; diff --git a/apps/desktop/src/main/lib/utils.ts b/apps/desktop/src/main/lib/utils.ts index fea66fea..a39026d6 100644 --- a/apps/desktop/src/main/lib/utils.ts +++ b/apps/desktop/src/main/lib/utils.ts @@ -1,4 +1,4 @@ -import { extractFileType } from '@colanode/core'; +import { extractFileType, getIdType, IdType } from '@colanode/core'; import { DeleteResult, InsertResult, @@ -159,3 +159,76 @@ export const fetchUserStorageUsed = async ( return BigInt(storageUsedRow?.storage_used ?? 0); }; + +export const fetchEntryBreadcrumb = async ( + database: Kysely, + entryId: string +): Promise => { + const rows = await database + .selectFrom('entry_paths') + .select('ancestor_id') + .where('descendant_id', '=', entryId) + .orderBy('level', 'asc') + .execute(); + + return rows.map((row) => row.ancestor_id); +}; + +export const fetchMessageBreadcrumb = async ( + database: Kysely, + messageId: string +): Promise => { + const message = await database + .selectFrom('messages') + .select('parent_id') + .where('id', '=', messageId) + .executeTakeFirst(); + + if (!message) { + return []; + } + + const parentIdType = getIdType(message.parent_id); + if (parentIdType === IdType.Message) { + const messageBreadcrumb = await fetchMessageBreadcrumb( + database, + message.parent_id + ); + + return [...messageBreadcrumb, messageId]; + } + + const entryBreadcrumb = await fetchEntryBreadcrumb( + database, + message.parent_id + ); + return [...entryBreadcrumb, messageId]; +}; + +export const fetchFileBreadcrumb = async ( + database: Kysely, + fileId: string +): Promise => { + const file = await database + .selectFrom('files') + .select('parent_id') + .where('id', '=', fileId) + .executeTakeFirst(); + + if (!file) { + return []; + } + + const parentIdType = getIdType(file.parent_id); + if (parentIdType === IdType.Message) { + const messageBreadcrumb = await fetchMessageBreadcrumb( + database, + file.parent_id + ); + + return [...messageBreadcrumb, fileId]; + } + + const entryBreadcrumb = await fetchEntryBreadcrumb(database, file.parent_id); + return [...entryBreadcrumb, fileId]; +}; diff --git a/apps/desktop/src/main/mutations/index.ts b/apps/desktop/src/main/mutations/index.ts index 9013cd83..4ad2b1b7 100644 --- a/apps/desktop/src/main/mutations/index.ts +++ b/apps/desktop/src/main/mutations/index.ts @@ -59,6 +59,8 @@ import { WorkspaceCreateMutationHandler } from '@/main/mutations/workspaces/work import { WorkspaceUpdateMutationHandler } from '@/main/mutations/workspaces/workspace-update'; import { UserRoleUpdateMutationHandler } from '@/main/mutations/users/user-role-update'; import { UsersInviteMutationHandler } from '@/main/mutations/users/users-invite'; +import { WorkspaceMetadataSaveMutationHandler } from '@/main/mutations/workspaces/workspace-metadata-save'; +import { WorkspaceMetadataDeleteMutationHandler } from '@/main/mutations/workspaces/workspace-metadata-delete'; import { MutationHandler } from '@/main/lib/types'; import { MutationMap } from '@/shared/mutations'; @@ -128,4 +130,6 @@ export const mutationHandlerMap: MutationHandlerMap = { page_update: new PageUpdateMutationHandler(), folder_update: new FolderUpdateMutationHandler(), database_update: new DatabaseUpdateMutationHandler(), + workspace_metadata_save: new WorkspaceMetadataSaveMutationHandler(), + workspace_metadata_delete: new WorkspaceMetadataDeleteMutationHandler(), }; diff --git a/apps/desktop/src/main/mutations/workspaces/workspace-metadata-delete.ts b/apps/desktop/src/main/mutations/workspaces/workspace-metadata-delete.ts new file mode 100644 index 00000000..33671360 --- /dev/null +++ b/apps/desktop/src/main/mutations/workspaces/workspace-metadata-delete.ts @@ -0,0 +1,41 @@ +import { MutationHandler } from '@/main/lib/types'; +import { + WorkspaceMetadataDeleteMutationInput, + WorkspaceMetadataDeleteMutationOutput, +} from '@/shared/mutations/workspaces/workspace-metadata-delete'; +import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base'; +import { eventBus } from '@/shared/lib/event-bus'; +import { mapWorkspaceMetadata } from '@/main/lib/mappers'; + +export class WorkspaceMetadataDeleteMutationHandler + extends WorkspaceMutationHandlerBase + implements MutationHandler +{ + async handleMutation( + input: WorkspaceMetadataDeleteMutationInput + ): Promise { + const workspace = this.getWorkspace(input.accountId, input.workspaceId); + const deletedMetadata = await workspace.database + .deleteFrom('metadata') + .where('key', '=', input.key) + .returningAll() + .executeTakeFirst(); + + if (!deletedMetadata) { + return { + success: true, + }; + } + + eventBus.publish({ + type: 'workspace_metadata_deleted', + accountId: input.accountId, + workspaceId: input.workspaceId, + metadata: mapWorkspaceMetadata(deletedMetadata), + }); + + return { + success: true, + }; + } +} diff --git a/apps/desktop/src/main/mutations/workspaces/workspace-metadata-save.ts b/apps/desktop/src/main/mutations/workspaces/workspace-metadata-save.ts new file mode 100644 index 00000000..9bf13099 --- /dev/null +++ b/apps/desktop/src/main/mutations/workspaces/workspace-metadata-save.ts @@ -0,0 +1,51 @@ +import { MutationHandler } from '@/main/lib/types'; +import { + WorkspaceMetadataSaveMutationInput, + WorkspaceMetadataSaveMutationOutput, +} from '@/shared/mutations/workspaces/workspace-metadata-save'; +import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base'; +import { eventBus } from '@/shared/lib/event-bus'; +import { mapWorkspaceMetadata } from '@/main/lib/mappers'; + +export class WorkspaceMetadataSaveMutationHandler + extends WorkspaceMutationHandlerBase + implements MutationHandler +{ + async handleMutation( + input: WorkspaceMetadataSaveMutationInput + ): Promise { + const workspace = this.getWorkspace(input.accountId, input.workspaceId); + const createdMetadata = await workspace.database + .insertInto('metadata') + .returningAll() + .values({ + key: input.key, + value: input.value, + created_at: new Date().toISOString(), + }) + .onConflict((cb) => + cb.columns(['key']).doUpdateSet({ + value: input.value, + updated_at: new Date().toISOString(), + }) + ) + .executeTakeFirst(); + + if (!createdMetadata) { + return { + success: false, + }; + } + + eventBus.publish({ + type: 'workspace_metadata_updated', + accountId: input.accountId, + workspaceId: input.workspaceId, + metadata: mapWorkspaceMetadata(createdMetadata), + }); + + return { + success: true, + }; + } +} diff --git a/apps/desktop/src/main/queries/entries/entry-breadcrumb-get.ts b/apps/desktop/src/main/queries/entries/entry-breadcrumb-get.ts new file mode 100644 index 00000000..2ca8ec67 --- /dev/null +++ b/apps/desktop/src/main/queries/entries/entry-breadcrumb-get.ts @@ -0,0 +1,67 @@ +import { ChangeCheckResult, QueryHandler } from '@/main/lib/types'; +import { fetchEntryBreadcrumb } from '@/main/lib/utils'; +import { EntryBreadcrumbGetQueryInput } from '@/shared/queries/entries/entry-breadcrumb-get'; +import { Event } from '@/shared/types/events'; +import { WorkspaceQueryHandlerBase } from '@/main/queries/workspace-query-handler-base'; + +export class EntryBreadcrumbGetQueryHandler + extends WorkspaceQueryHandlerBase + implements QueryHandler +{ + public async handleQuery( + input: EntryBreadcrumbGetQueryInput + ): Promise { + const workspace = this.getWorkspace(input.accountId, input.workspaceId); + return fetchEntryBreadcrumb(workspace.database, input.entryId); + } + + public async checkForChanges( + event: Event, + input: EntryBreadcrumbGetQueryInput, + output: string[] + ): Promise> { + if ( + event.type === 'workspace_deleted' && + event.workspace.accountId === input.accountId && + event.workspace.id === input.workspaceId + ) { + return { + hasChanges: true, + result: [], + }; + } + + if ( + event.type === 'entry_created' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId && + event.entry.id === input.entryId + ) { + const newResult = await this.handleQuery(input); + return { + hasChanges: true, + result: newResult, + }; + } + + if ( + event.type === 'entry_deleted' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId + ) { + const entryId = output.find((id) => id === event.entry.id); + if (entryId) { + const newResult = await this.handleQuery(input); + return { + hasChanges: true, + result: newResult, + }; + } + } + + return { + hasChanges: false, + result: output, + }; + } +} diff --git a/apps/desktop/src/main/queries/files/file-breadcrumb-get.ts b/apps/desktop/src/main/queries/files/file-breadcrumb-get.ts new file mode 100644 index 00000000..7b68be35 --- /dev/null +++ b/apps/desktop/src/main/queries/files/file-breadcrumb-get.ts @@ -0,0 +1,67 @@ +import { ChangeCheckResult, QueryHandler } from '@/main/lib/types'; +import { FileBreadcrumbGetQueryInput } from '@/shared/queries/files/file-breadcrumb-get'; +import { Event } from '@/shared/types/events'; +import { WorkspaceQueryHandlerBase } from '@/main/queries/workspace-query-handler-base'; +import { fetchFileBreadcrumb } from '@/main/lib/utils'; + +export class FileBreadcrumbGetQueryHandler + extends WorkspaceQueryHandlerBase + implements QueryHandler +{ + public async handleQuery( + input: FileBreadcrumbGetQueryInput + ): Promise { + const workspace = this.getWorkspace(input.accountId, input.workspaceId); + return fetchFileBreadcrumb(workspace.database, input.fileId); + } + + public async checkForChanges( + event: Event, + input: FileBreadcrumbGetQueryInput, + output: string[] + ): Promise> { + if ( + event.type === 'workspace_deleted' && + event.workspace.accountId === input.accountId && + event.workspace.id === input.workspaceId + ) { + return { + hasChanges: true, + result: [], + }; + } + + if ( + event.type === 'file_created' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId && + event.file.id === input.fileId + ) { + const newOutput = await this.handleQuery(input); + return { + hasChanges: true, + result: newOutput, + }; + } + + if ( + event.type === 'file_deleted' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId && + event.file.id === input.fileId + ) { + const fileId = output.find((id) => id === event.file.id); + if (fileId) { + const newOutput = await this.handleQuery(input); + return { + hasChanges: true, + result: newOutput, + }; + } + } + + return { + hasChanges: false, + }; + } +} diff --git a/apps/desktop/src/main/queries/index.ts b/apps/desktop/src/main/queries/index.ts index 551f0698..16db2cac 100644 --- a/apps/desktop/src/main/queries/index.ts +++ b/apps/desktop/src/main/queries/index.ts @@ -8,6 +8,7 @@ import { EmojiGetBySkinIdQueryHandler } from '@/main/queries/emojis/emoji-get-by import { FileListQueryHandler } from '@/main/queries/files/file-list'; import { FileGetQueryHandler } from '@/main/queries/files/file-get'; import { FileMetadataGetQueryHandler } from '@/main/queries/files/file-metadata-get'; +import { FileBreadcrumbGetQueryHandler } from '@/main/queries/files/file-breadcrumb-get'; import { IconListQueryHandler } from '@/main/queries/icons/icon-list'; import { IconSearchQueryHandler } from '@/main/queries/icons/icon-search'; import { IconCategoryListQueryHandler } from '@/main/queries/icons/icon-category-list'; @@ -15,9 +16,11 @@ import { MessageGetQueryHandler } from '@/main/queries/messages/message-get'; import { MessageListQueryHandler } from '@/main/queries/messages/message-list'; import { MessageReactionsListQueryHandler } from '@/main/queries/messages/message-reaction-list'; import { MessageReactionsAggregateQueryHandler } from '@/main/queries/messages/message-reactions-aggregate'; +import { MessageBreadcrumbGetQueryHandler } from '@/main/queries/messages/message-breadcrumb-get'; import { EntryChildrenGetQueryHandler } from '@/main/queries/entries/entry-children-get'; import { EntryGetQueryHandler } from '@/main/queries/entries/entry-get'; import { EntryTreeGetQueryHandler } from '@/main/queries/entries/entry-tree-get'; +import { EntryBreadcrumbGetQueryHandler } from '@/main/queries/entries/entry-breadcrumb-get'; import { RadarDataGetQueryHandler } from '@/main/queries/interactions/radar-data-get'; import { RecordListQueryHandler } from '@/main/queries/records/record-list'; import { ServerListQueryHandler } from '@/main/queries/servers/server-list'; @@ -30,6 +33,7 @@ import { RecordSearchQueryHandler } from '@/main/queries/records/record-search'; import { UserGetQueryHandler } from '@/main/queries/users/user-get'; import { SpaceListQueryHandler } from '@/main/queries/spaces/space-list'; import { ChatListQueryHandler } from '@/main/queries/chats/chat-list'; +import { WorkspaceMetadataListQueryHandler } from '@/main/queries/workspaces/workspace-metadata-list'; import { QueryHandler } from '@/main/lib/types'; import { QueryMap } from '@/shared/queries'; @@ -43,7 +47,9 @@ export const queryHandlerMap: QueryHandlerMap = { message_reaction_list: new MessageReactionsListQueryHandler(), message_reactions_aggregate: new MessageReactionsAggregateQueryHandler(), message_get: new MessageGetQueryHandler(), + message_breadcrumb_get: new MessageBreadcrumbGetQueryHandler(), entry_get: new EntryGetQueryHandler(), + entry_breadcrumb_get: new EntryBreadcrumbGetQueryHandler(), record_list: new RecordListQueryHandler(), server_list: new ServerListQueryHandler(), user_search: new UserSearchQueryHandler(), @@ -62,6 +68,7 @@ export const queryHandlerMap: QueryHandlerMap = { entry_children_get: new EntryChildrenGetQueryHandler(), radar_data_get: new RadarDataGetQueryHandler(), file_metadata_get: new FileMetadataGetQueryHandler(), + file_breadcrumb_get: new FileBreadcrumbGetQueryHandler(), account_get: new AccountGetQueryHandler(), workspace_get: new WorkspaceGetQueryHandler(), database_list: new DatabaseListQueryHandler(), @@ -70,4 +77,5 @@ export const queryHandlerMap: QueryHandlerMap = { file_get: new FileGetQueryHandler(), chat_list: new ChatListQueryHandler(), space_list: new SpaceListQueryHandler(), + workspace_metadata_list: new WorkspaceMetadataListQueryHandler(), }; diff --git a/apps/desktop/src/main/queries/messages/message-breadcrumb-get.ts b/apps/desktop/src/main/queries/messages/message-breadcrumb-get.ts new file mode 100644 index 00000000..aa431917 --- /dev/null +++ b/apps/desktop/src/main/queries/messages/message-breadcrumb-get.ts @@ -0,0 +1,67 @@ +import { ChangeCheckResult, QueryHandler } from '@/main/lib/types'; +import { MessageBreadcrumbGetQueryInput } from '@/shared/queries/messages/message-breadcrumb-get'; +import { Event } from '@/shared/types/events'; +import { WorkspaceQueryHandlerBase } from '@/main/queries/workspace-query-handler-base'; +import { fetchMessageBreadcrumb } from '@/main/lib/utils'; + +export class MessageBreadcrumbGetQueryHandler + extends WorkspaceQueryHandlerBase + implements QueryHandler +{ + public async handleQuery( + input: MessageBreadcrumbGetQueryInput + ): Promise { + const workspace = this.getWorkspace(input.accountId, input.workspaceId); + return fetchMessageBreadcrumb(workspace.database, input.messageId); + } + + public async checkForChanges( + event: Event, + input: MessageBreadcrumbGetQueryInput, + output: string[] + ): Promise> { + if ( + event.type === 'workspace_deleted' && + event.workspace.accountId === input.accountId && + event.workspace.id === input.workspaceId + ) { + return { + hasChanges: true, + result: [], + }; + } + + if ( + event.type === 'message_created' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId && + event.message.id === input.messageId + ) { + const newOutput = await this.handleQuery(input); + return { + hasChanges: true, + result: newOutput, + }; + } + + if ( + event.type === 'message_deleted' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId && + event.message.id === input.messageId + ) { + const messageId = output.find((id) => id === event.message.id); + if (messageId) { + const newOutput = await this.handleQuery(input); + return { + hasChanges: true, + result: newOutput, + }; + } + } + + return { + hasChanges: false, + }; + } +} diff --git a/apps/desktop/src/main/queries/workspaces/workspace-metadata-list.ts b/apps/desktop/src/main/queries/workspaces/workspace-metadata-list.ts new file mode 100644 index 00000000..0528cee2 --- /dev/null +++ b/apps/desktop/src/main/queries/workspaces/workspace-metadata-list.ts @@ -0,0 +1,93 @@ +import { ChangeCheckResult, QueryHandler } from '@/main/lib/types'; +import { mapWorkspaceMetadata } from '@/main/lib/mappers'; +import { WorkspaceMetadataListQueryInput } from '@/shared/queries/workspaces/workspace-metadata-list'; +import { Event } from '@/shared/types/events'; +import { WorkspaceMetadata } from '@/shared/types/workspaces'; +import { WorkspaceQueryHandlerBase } from '@/main/queries/workspace-query-handler-base'; +import { SelectWorkspaceMetadata } from '@/main/databases/workspace/schema'; + +export class WorkspaceMetadataListQueryHandler + extends WorkspaceQueryHandlerBase + implements QueryHandler +{ + public async handleQuery( + input: WorkspaceMetadataListQueryInput + ): Promise { + const rows = await this.getWorkspaceMetadata( + input.accountId, + input.workspaceId + ); + if (!rows) { + return []; + } + + return rows.map(mapWorkspaceMetadata); + } + + public async checkForChanges( + event: Event, + input: WorkspaceMetadataListQueryInput, + output: WorkspaceMetadata[] + ): Promise> { + if ( + event.type === 'workspace_created' && + event.workspace.accountId === input.accountId && + event.workspace.id === input.workspaceId + ) { + return { + hasChanges: true, + result: [], + }; + } + + if ( + event.type === 'workspace_metadata_updated' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId + ) { + const newOutput = output.map((metadata) => { + if (metadata.key === event.metadata.key) { + return event.metadata; + } + return metadata; + }); + + return { + hasChanges: true, + result: newOutput, + }; + } + + if ( + event.type === 'workspace_metadata_deleted' && + event.accountId === input.accountId && + event.workspaceId === input.workspaceId + ) { + const newOutput = output.filter( + (metadata) => metadata.key !== event.metadata.key + ); + + return { + hasChanges: true, + result: newOutput, + }; + } + + return { + hasChanges: false, + }; + } + + private async getWorkspaceMetadata( + accountId: string, + workspaceId: string + ): Promise { + const workspace = this.getWorkspace(accountId, workspaceId); + const rows = await workspace.database + .selectFrom('metadata') + .selectAll() + .execute(); + + return rows; + } +} diff --git a/apps/desktop/src/renderer/components/channels/channel-body.tsx b/apps/desktop/src/renderer/components/channels/channel-body.tsx deleted file mode 100644 index dd1b85d6..00000000 --- a/apps/desktop/src/renderer/components/channels/channel-body.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { ChannelEntry, EntryRole } from '@colanode/core'; -import { useEffect } from 'react'; - -import { Conversation } from '@/renderer/components/messages/conversation'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useRadar } from '@/renderer/contexts/radar'; - -interface ChannelBodyProps { - channel: ChannelEntry; - role: EntryRole; -} - -export const ChannelBody = ({ channel, role }: ChannelBodyProps) => { - const workspace = useWorkspace(); - const radar = useRadar(); - - useEffect(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, channel.id); - - const interval = setInterval(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, channel.id); - }, 60000); - - return () => clearInterval(interval); - }, [channel.id, channel.type, channel.transactionId]); - - return ( - - ); -}; diff --git a/apps/desktop/src/renderer/components/channels/channel-breadcrumb-item.tsx b/apps/desktop/src/renderer/components/channels/channel-breadcrumb-item.tsx index ef4e589d..97887fec 100644 --- a/apps/desktop/src/renderer/components/channels/channel-breadcrumb-item.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-breadcrumb-item.tsx @@ -1,21 +1,35 @@ import { ChannelEntry } from '@colanode/core'; import { Avatar } from '@/renderer/components/avatars/avatar'; +import { useQuery } from '@/renderer/hooks/use-query'; +import { useWorkspace } from '@/renderer/contexts/workspace'; interface ChannelBreadcrumbItemProps { - channel: ChannelEntry; + id: string; } -export const ChannelBreadcrumbItem = ({ - channel, -}: ChannelBreadcrumbItemProps) => { +export const ChannelBreadcrumbItem = ({ id }: ChannelBreadcrumbItemProps) => { + const workspace = useWorkspace(); + const { data } = useQuery({ + type: 'entry_get', + entryId: id, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + if (!data) { + return null; + } + + const channel = data as ChannelEntry; + return (
{channel.attributes.name}
diff --git a/apps/desktop/src/renderer/components/channels/channel-container-tab.tsx b/apps/desktop/src/renderer/components/channels/channel-container-tab.tsx new file mode 100644 index 00000000..a092ebc3 --- /dev/null +++ b/apps/desktop/src/renderer/components/channels/channel-container-tab.tsx @@ -0,0 +1,39 @@ +import { ChannelEntry } from '@colanode/core'; + +import { Avatar } from '@/renderer/components/avatars/avatar'; +import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useQuery } from '@/renderer/hooks/use-query'; + +interface ChannelContainerTabProps { + channelId: string; +} + +export const ChannelContainerTab = ({ + channelId, +}: ChannelContainerTabProps) => { + const workspace = useWorkspace(); + + const { data: entry } = useQuery({ + type: 'entry_get', + entryId: channelId, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + const channel = entry as ChannelEntry; + if (!channel) { + return

Not found

; + } + + return ( +
+ + {channel.attributes.name} +
+ ); +}; diff --git a/apps/desktop/src/renderer/components/channels/channel-container.tsx b/apps/desktop/src/renderer/components/channels/channel-container.tsx index beea9b27..396f2f39 100644 --- a/apps/desktop/src/renderer/components/channels/channel-container.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-container.tsx @@ -1,57 +1,52 @@ -import { ChannelEntry, extractEntryRole } from '@colanode/core'; +import { ChannelEntry } from '@colanode/core'; -import { ChannelBody } from '@/renderer/components/channels/channel-body'; -import { ChannelHeader } from '@/renderer/components/channels/channel-header'; import { ChannelNotFound } from '@/renderer/components/channels/channel-not-found'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useQuery } from '@/renderer/hooks/use-query'; +import { + Container, + ContainerBody, + ContainerHeader, + ContainerSettings, +} from '@/renderer/components/ui/container'; +import { ContainerBreadcrumb } from '@/renderer/components/layouts/breadcrumbs/container-breadrumb'; +import { useEntryContainer } from '@/renderer/hooks/use-entry-container'; +import { ChannelSettings } from '@/renderer/components/channels/channel-settings'; +import { Conversation } from '@/renderer/components/messages/conversation'; +import { useEntryRadar } from '@/renderer/hooks/use-entry-radar'; interface ChannelContainerProps { channelId: string; } export const ChannelContainer = ({ channelId }: ChannelContainerProps) => { - const workspace = useWorkspace(); + const data = useEntryContainer(channelId); - const { data: entry, isPending: isPendingEntry } = useQuery({ - type: 'entry_get', - entryId: channelId, - accountId: workspace.accountId, - workspaceId: workspace.id, - }); + useEntryRadar(data.entry); - const channel = entry as ChannelEntry; - const channelExists = !!channel; - - const { data: root, isPending: isPendingRoot } = useQuery( - { - type: 'entry_get', - entryId: channel?.rootId ?? '', - accountId: workspace.accountId, - workspaceId: workspace.id, - }, - { - enabled: channelExists, - } - ); - - if (isPendingEntry || (isPendingRoot && channelExists)) { + if (data.isPending) { return null; } - if (!channel || !root) { + if (!data.entry) { return ; } - const role = extractEntryRole(root, workspace.userId); - if (!role) { - return ; - } + const { entry: channel, role } = data; return ( -
- - -
+ + + + + + + + + + + ); }; diff --git a/apps/desktop/src/renderer/components/channels/channel-create-dialog.tsx b/apps/desktop/src/renderer/components/channels/channel-create-dialog.tsx index 6285390d..2e147576 100644 --- a/apps/desktop/src/renderer/components/channels/channel-create-dialog.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-create-dialog.tsx @@ -11,6 +11,7 @@ import { import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { toast } from '@/renderer/hooks/use-toast'; +import { useLayout } from '@/renderer/contexts/layout'; interface ChannelCreateDialogProps { spaceId: string; @@ -24,6 +25,7 @@ export const ChannelCreateDialog = ({ onOpenChange, }: ChannelCreateDialogProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const { mutate, isPending } = useMutation(); return ( @@ -61,7 +63,7 @@ export const ChannelCreateDialog = ({ }, onSuccess(output) { onOpenChange(false); - workspace.openInMain(output.id); + layout.openLeft(output.id); }, onError(error) { toast({ diff --git a/apps/desktop/src/renderer/components/channels/channel-delete-dialog.tsx b/apps/desktop/src/renderer/components/channels/channel-delete-dialog.tsx index 7f61ea5e..3c156c6b 100644 --- a/apps/desktop/src/renderer/components/channels/channel-delete-dialog.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-delete-dialog.tsx @@ -8,6 +8,7 @@ import { AlertDialogTitle, } from '@/renderer/components/ui/alert-dialog'; import { Button } from '@/renderer/components/ui/button'; +import { useLayout } from '@/renderer/contexts/layout'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { toast } from '@/renderer/hooks/use-toast'; @@ -24,6 +25,7 @@ export const ChannelDeleteDialog = ({ onOpenChange, }: ChannelDeleteDialogProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const { mutate, isPending } = useMutation(); return ( @@ -53,7 +55,7 @@ export const ChannelDeleteDialog = ({ }, onSuccess() { onOpenChange(false); - workspace.closeEntry(channelId); + layout.close(channelId); }, onError(error) { toast({ diff --git a/apps/desktop/src/renderer/components/channels/channel-header.tsx b/apps/desktop/src/renderer/components/channels/channel-header.tsx deleted file mode 100644 index c44f7b57..00000000 --- a/apps/desktop/src/renderer/components/channels/channel-header.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { ChannelEntry, EntryRole } from '@colanode/core'; - -import { ChannelSettings } from '@/renderer/components/channels/channel-settings'; -import { EntryBreadcrumb } from '@/renderer/components/layouts/entry-breadcrumb'; -import { EntryFullscreenButton } from '@/renderer/components/layouts/entry-fullscreen-button'; -import { Header } from '@/renderer/components/ui/header'; -import { useContainer } from '@/renderer/contexts/container'; - -interface ChannelHeaderProps { - channel: ChannelEntry; - role: EntryRole; -} - -export const ChannelHeader = ({ channel, role }: ChannelHeaderProps) => { - const container = useContainer(); - - return ( -
-
-
- {container.mode === 'main' && } - {container.mode === 'modal' && ( - - )} -
-
- -
-
-
- ); -}; diff --git a/apps/desktop/src/renderer/components/channels/channel-settings.tsx b/apps/desktop/src/renderer/components/channels/channel-settings.tsx index 7f502df4..c9fea397 100644 --- a/apps/desktop/src/renderer/components/channels/channel-settings.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-settings.tsx @@ -30,7 +30,7 @@ export const ChannelSettings = ({ channel, role }: ChannelSettingsProps) => { - + {channel.attributes.name} diff --git a/apps/desktop/src/renderer/components/channels/channel-sidebar-item.tsx b/apps/desktop/src/renderer/components/channels/channel-sidebar-item.tsx index 2a1c05a8..d04c0f6f 100644 --- a/apps/desktop/src/renderer/components/channels/channel-sidebar-item.tsx +++ b/apps/desktop/src/renderer/components/channels/channel-sidebar-item.tsx @@ -5,6 +5,7 @@ import { Avatar } from '@/renderer/components/avatars/avatar'; import { ReadStateIndicator } from '@/renderer/components/layouts/read-state-indicator'; import { useRadar } from '@/renderer/contexts/radar'; import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; import { cn } from '@/shared/lib/utils'; interface ChannelSidebarItemProps { @@ -13,9 +14,10 @@ interface ChannelSidebarItemProps { export const ChannelSidebarItem = ({ channel }: ChannelSidebarItemProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const radar = useRadar(); - const isActive = workspace.isEntryActive(channel.id); + const isActive = layout.activeTab === channel.id; const channelState = radar.getChannelState( workspace.accountId, workspace.id, diff --git a/apps/desktop/src/renderer/components/chats/chat-body.tsx b/apps/desktop/src/renderer/components/chats/chat-body.tsx deleted file mode 100644 index cd6f8c6f..00000000 --- a/apps/desktop/src/renderer/components/chats/chat-body.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ChatEntry, EntryRole } from '@colanode/core'; -import { useEffect } from 'react'; - -import { Conversation } from '@/renderer/components/messages/conversation'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useRadar } from '@/renderer/contexts/radar'; - -interface ChatBodyProps { - chat: ChatEntry; - role: EntryRole; -} - -export const ChatBody = ({ chat, role }: ChatBodyProps) => { - const workspace = useWorkspace(); - const radar = useRadar(); - - useEffect(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, chat.id); - - const interval = setInterval(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, chat.id); - }, 60000); - - return () => clearInterval(interval); - }, [chat.id, chat.type, chat.transactionId]); - - return ( - - ); -}; diff --git a/apps/desktop/src/renderer/components/chats/chat-breadcrumb-item.tsx b/apps/desktop/src/renderer/components/chats/chat-breadcrumb-item.tsx index e2506088..672d1e01 100644 --- a/apps/desktop/src/renderer/components/chats/chat-breadcrumb-item.tsx +++ b/apps/desktop/src/renderer/components/chats/chat-breadcrumb-item.tsx @@ -1,35 +1,48 @@ -import { ChatEntry } from '@colanode/core'; - import { Avatar } from '@/renderer/components/avatars/avatar'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useQuery } from '@/renderer/hooks/use-query'; interface ChatBreadcrumbItemProps { - chat: ChatEntry; + id: string; } -export const ChatBreadcrumbItem = ({ chat }: ChatBreadcrumbItemProps) => { +export const ChatBreadcrumbItem = ({ id }: ChatBreadcrumbItemProps) => { const workspace = useWorkspace(); - const userId = - Object.keys(chat.attributes.collaborators).find( - (id) => id !== workspace.userId - ) ?? ''; - const { data, isPending } = useQuery({ + const { data: chat } = useQuery({ + type: 'entry_get', + entryId: id, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + const userId = + chat && chat.type === 'chat' + ? (Object.keys(chat.attributes.collaborators).find( + (id) => id !== workspace.userId + ) ?? '') + : ''; + + const { data: user } = useQuery({ type: 'user_get', accountId: workspace.accountId, workspaceId: workspace.id, userId, }); - if (isPending || !data) { + if (!chat || !user) { return null; } return (
- - {data.name} + + {user.name}
); }; diff --git a/apps/desktop/src/renderer/components/chats/chat-container-tab.tsx b/apps/desktop/src/renderer/components/chats/chat-container-tab.tsx new file mode 100644 index 00000000..71027b91 --- /dev/null +++ b/apps/desktop/src/renderer/components/chats/chat-container-tab.tsx @@ -0,0 +1,43 @@ +import { Avatar } from '@/renderer/components/avatars/avatar'; +import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useQuery } from '@/renderer/hooks/use-query'; + +interface ChatContainerTabProps { + chatId: string; +} + +export const ChatContainerTab = ({ chatId }: ChatContainerTabProps) => { + const workspace = useWorkspace(); + + const { data: chat } = useQuery({ + type: 'entry_get', + entryId: chatId, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + const userId = + chat && chat.type === 'chat' + ? (Object.keys(chat.attributes.collaborators).find( + (id) => id !== workspace.userId + ) ?? '') + : ''; + + const { data: user } = useQuery({ + type: 'user_get', + accountId: workspace.accountId, + workspaceId: workspace.id, + userId, + }); + + if (!chat || !user) { + return

Not found

; + } + + return ( +
+ + {user.name} +
+ ); +}; diff --git a/apps/desktop/src/renderer/components/chats/chat-container.tsx b/apps/desktop/src/renderer/components/chats/chat-container.tsx index 84276dd0..a3cb5043 100644 --- a/apps/desktop/src/renderer/components/chats/chat-container.tsx +++ b/apps/desktop/src/renderer/components/chats/chat-container.tsx @@ -1,43 +1,56 @@ -import { extractEntryRole } from '@colanode/core'; +import { ChatEntry } from '@colanode/core'; -import { ChatBody } from '@/renderer/components/chats/chat-body'; -import { ChatHeader } from '@/renderer/components/chats/chat-header'; +import { + Container, + ContainerBody, + ContainerHeader, + ContainerSettings, +} from '@/renderer/components/ui/container'; +import { ContainerBreadcrumb } from '@/renderer/components/layouts/breadcrumbs/container-breadrumb'; import { ChatNotFound } from '@/renderer/components/chats/chat-not-found'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useQuery } from '@/renderer/hooks/use-query'; +import { EntryCollaboratorsPopover } from '@/renderer/components/collaborators/entry-collaborators-popover'; +import { Conversation } from '@/renderer/components/messages/conversation'; +import { useEntryRadar } from '@/renderer/hooks/use-entry-radar'; +import { useEntryContainer } from '@/renderer/hooks/use-entry-container'; interface ChatContainerProps { chatId: string; } export const ChatContainer = ({ chatId }: ChatContainerProps) => { - const workspace = useWorkspace(); + const data = useEntryContainer(chatId); - const { data, isPending } = useQuery({ - type: 'entry_get', - entryId: chatId, - accountId: workspace.accountId, - workspaceId: workspace.id, - }); + useEntryRadar(data.entry); - if (isPending) { + if (data.isPending) { return null; } - const node = data; - if (!node || node.type !== 'chat') { + if (!data.entry) { return ; } - const role = extractEntryRole(node, workspace.userId); - if (!role) { - return ; - } + const { entry, role } = data; return ( -
- - -
+ + + + + + + + + + + ); }; diff --git a/apps/desktop/src/renderer/components/chats/chat-create-popover.tsx b/apps/desktop/src/renderer/components/chats/chat-create-popover.tsx index 92f472b8..ef04a3d0 100644 --- a/apps/desktop/src/renderer/components/chats/chat-create-popover.tsx +++ b/apps/desktop/src/renderer/components/chats/chat-create-popover.tsx @@ -10,17 +10,19 @@ import { UserSearch } from '@/renderer/components/users/user-search'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { toast } from '@/renderer/hooks/use-toast'; +import { useLayout } from '@/renderer/contexts/layout'; export const ChatCreatePopover = () => { const workspace = useWorkspace(); const { mutate, isPending } = useMutation(); + const layout = useLayout(); const [open, setOpen] = React.useState(false); return ( - + { userId: user.id, }, onSuccess(output) { - workspace.openInMain(output.id); + layout.openLeft(output.id); setOpen(false); }, onError(error) { diff --git a/apps/desktop/src/renderer/components/chats/chat-header.tsx b/apps/desktop/src/renderer/components/chats/chat-header.tsx deleted file mode 100644 index b3bd90a1..00000000 --- a/apps/desktop/src/renderer/components/chats/chat-header.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ChatEntry, EntryRole } from '@colanode/core'; - -import { EntryCollaboratorsPopover } from '@/renderer/components/collaborators/entry-collaborators-popover'; -import { EntryBreadcrumb } from '@/renderer/components/layouts/entry-breadcrumb'; -import { EntryFullscreenButton } from '@/renderer/components/layouts/entry-fullscreen-button'; -import { Header } from '@/renderer/components/ui/header'; -import { useContainer } from '@/renderer/contexts/container'; - -interface ChatHeaderProps { - chat: ChatEntry; - role: EntryRole; -} - -export const ChatHeader = ({ chat, role }: ChatHeaderProps) => { - const container = useContainer(); - - return ( -
-
-
- - {container.mode === 'modal' && ( - - )} -
-
- -
-
-
- ); -}; diff --git a/apps/desktop/src/renderer/components/chats/chat-sidebar-item.tsx b/apps/desktop/src/renderer/components/chats/chat-sidebar-item.tsx index 8b1d96f8..80c5c0e2 100644 --- a/apps/desktop/src/renderer/components/chats/chat-sidebar-item.tsx +++ b/apps/desktop/src/renderer/components/chats/chat-sidebar-item.tsx @@ -6,6 +6,7 @@ import { ReadStateIndicator } from '@/renderer/components/layouts/read-state-ind import { useRadar } from '@/renderer/contexts/radar'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useQuery } from '@/renderer/hooks/use-query'; +import { useLayout } from '@/renderer/contexts/layout'; import { cn } from '@/shared/lib/utils'; interface ChatSidebarItemProps { @@ -14,6 +15,7 @@ interface ChatSidebarItemProps { export const ChatSidebarItem = ({ chat }: ChatSidebarItemProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const radar = useRadar(); const userId = @@ -37,7 +39,7 @@ export const ChatSidebarItem = ({ chat }: ChatSidebarItemProps) => { workspace.id, chat.id ); - const isActive = workspace.isEntryActive(chat.id); + const isActive = layout.activeTab === chat.id; const unreadCount = nodeReadState.unseenMessagesCount + nodeReadState.mentionsCount; diff --git a/apps/desktop/src/renderer/components/databases/boards/board-view-record-card.tsx b/apps/desktop/src/renderer/components/databases/boards/board-view-record-card.tsx index 6eb9cbb5..30991e6a 100644 --- a/apps/desktop/src/renderer/components/databases/boards/board-view-record-card.tsx +++ b/apps/desktop/src/renderer/components/databases/boards/board-view-record-card.tsx @@ -5,7 +5,7 @@ import { useDrag } from 'react-dnd'; import { RecordFieldValue } from '@/renderer/components/records/record-field-value'; import { useRecord } from '@/renderer/contexts/record'; import { useView } from '@/renderer/contexts/view'; -import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; interface DragResult { option: SelectOptionAttributes; @@ -13,7 +13,7 @@ interface DragResult { } export const BoardViewRecordCard = () => { - const workspace = useWorkspace(); + const layout = useLayout(); const view = useView(); const record = useRecord(); @@ -52,7 +52,9 @@ export const BoardViewRecordCard = () => { role="presentation" key={record.id} className="animate-fade-in flex cursor-pointer flex-col gap-1 rounded-md border p-2 text-left hover:bg-gray-50" - onClick={() => workspace.openInModal(record.id)} + onClick={() => { + layout.previewLeft(record.id, true); + }} >

{hasName ? name : 'Unnamed'} diff --git a/apps/desktop/src/renderer/components/databases/boards/board-view.tsx b/apps/desktop/src/renderer/components/databases/boards/board-view.tsx index efb390b5..f453dcee 100644 --- a/apps/desktop/src/renderer/components/databases/boards/board-view.tsx +++ b/apps/desktop/src/renderer/components/databases/boards/board-view.tsx @@ -24,7 +24,7 @@ export const BoardView = () => { const selectOptions = Object.values(groupByField.options ?? {}); return ( -

+
diff --git a/apps/desktop/src/renderer/components/databases/calendars/calendar-view-record-card.tsx b/apps/desktop/src/renderer/components/databases/calendars/calendar-view-record-card.tsx index e5409700..7bb50f07 100644 --- a/apps/desktop/src/renderer/components/databases/calendars/calendar-view-record-card.tsx +++ b/apps/desktop/src/renderer/components/databases/calendars/calendar-view-record-card.tsx @@ -1,10 +1,10 @@ import { RecordFieldValue } from '@/renderer/components/records/record-field-value'; import { useRecord } from '@/renderer/contexts/record'; import { useView } from '@/renderer/contexts/view'; -import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; export const CalendarViewRecordCard = () => { - const workspace = useWorkspace(); + const layout = useLayout(); const view = useView(); const record = useRecord(); @@ -16,7 +16,9 @@ export const CalendarViewRecordCard = () => { role="presentation" key={record.id} className="animate-fade-in flex justify-start items-start cursor-pointer flex-col gap-1 rounded-md border p-2 hover:bg-gray-50" - onClick={() => workspace.openInModal(record.id)} + onClick={() => { + layout.previewLeft(record.id, true); + }} >

{name ?? 'Unnamed'} diff --git a/apps/desktop/src/renderer/components/databases/calendars/calendar-view.tsx b/apps/desktop/src/renderer/components/databases/calendars/calendar-view.tsx index 38d1392e..56bd4305 100644 --- a/apps/desktop/src/renderer/components/databases/calendars/calendar-view.tsx +++ b/apps/desktop/src/renderer/components/databases/calendars/calendar-view.tsx @@ -23,7 +23,7 @@ export const CalendarView = () => { return ( -

+
diff --git a/apps/desktop/src/renderer/components/databases/database-body.tsx b/apps/desktop/src/renderer/components/databases/database-body.tsx deleted file mode 100644 index dc42abdf..00000000 --- a/apps/desktop/src/renderer/components/databases/database-body.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { DatabaseEntry, EntryRole } from '@colanode/core'; -import { useEffect } from 'react'; - -import { Database } from '@/renderer/components/databases/database'; -import { DatabaseViews } from '@/renderer/components/databases/database-views'; -import { useRadar } from '@/renderer/contexts/radar'; -import { useWorkspace } from '@/renderer/contexts/workspace'; - -interface DatabaseBodyProps { - database: DatabaseEntry; - role: EntryRole; -} - -export const DatabaseBody = ({ database, role }: DatabaseBodyProps) => { - const workspace = useWorkspace(); - const radar = useRadar(); - - useEffect(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, database.id); - - const interval = setInterval(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, database.id); - }, 60000); - - return () => clearInterval(interval); - }, [database.id, database.type, database.transactionId]); - - return ( - - - - ); -}; diff --git a/apps/desktop/src/renderer/components/databases/database-breadcrumb-item.tsx b/apps/desktop/src/renderer/components/databases/database-breadcrumb-item.tsx index ed63295f..687ab9cc 100644 --- a/apps/desktop/src/renderer/components/databases/database-breadcrumb-item.tsx +++ b/apps/desktop/src/renderer/components/databases/database-breadcrumb-item.tsx @@ -1,21 +1,32 @@ import { DatabaseEntry } from '@colanode/core'; import { Avatar } from '@/renderer/components/avatars/avatar'; +import { useQuery } from '@/renderer/hooks/use-query'; +import { useWorkspace } from '@/renderer/contexts/workspace'; interface DatabaseBreadcrumbItemProps { - database: DatabaseEntry; + id: string; } -export const DatabaseBreadcrumbItem = ({ - database, -}: DatabaseBreadcrumbItemProps) => { +export const DatabaseBreadcrumbItem = ({ id }: DatabaseBreadcrumbItemProps) => { + const workspace = useWorkspace(); + const { data } = useQuery({ + type: 'entry_get', + entryId: id, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + if (!data) { + return null; + } + const database = data as DatabaseEntry; return (
{database.attributes.name}
diff --git a/apps/desktop/src/renderer/components/databases/database-container-tab.tsx b/apps/desktop/src/renderer/components/databases/database-container-tab.tsx new file mode 100644 index 00000000..6ffce43f --- /dev/null +++ b/apps/desktop/src/renderer/components/databases/database-container-tab.tsx @@ -0,0 +1,39 @@ +import { DatabaseEntry } from '@colanode/core'; + +import { Avatar } from '@/renderer/components/avatars/avatar'; +import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useQuery } from '@/renderer/hooks/use-query'; + +interface DatabaseContainerTabProps { + databaseId: string; +} + +export const DatabaseContainerTab = ({ + databaseId, +}: DatabaseContainerTabProps) => { + const workspace = useWorkspace(); + + const { data: entry } = useQuery({ + type: 'entry_get', + entryId: databaseId, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + const database = entry as DatabaseEntry; + if (!database) { + return

Not found

; + } + + return ( +
+ + {database.attributes.name} +
+ ); +}; diff --git a/apps/desktop/src/renderer/components/databases/database-container.tsx b/apps/desktop/src/renderer/components/databases/database-container.tsx index ee73064f..17ec94f6 100644 --- a/apps/desktop/src/renderer/components/databases/database-container.tsx +++ b/apps/desktop/src/renderer/components/databases/database-container.tsx @@ -1,57 +1,51 @@ -import { DatabaseEntry, extractEntryRole } from '@colanode/core'; +import { DatabaseEntry } from '@colanode/core'; -import { DatabaseBody } from '@/renderer/components/databases/database-body'; -import { DatabaseHeader } from '@/renderer/components/databases/database-header'; import { DatabaseNotFound } from '@/renderer/components/databases/database-not-found'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useQuery } from '@/renderer/hooks/use-query'; +import { + Container, + ContainerBody, + ContainerHeader, + ContainerSettings, +} from '@/renderer/components/ui/container'; +import { ContainerBreadcrumb } from '@/renderer/components/layouts/breadcrumbs/container-breadrumb'; +import { useEntryContainer } from '@/renderer/hooks/use-entry-container'; +import { useEntryRadar } from '@/renderer/hooks/use-entry-radar'; +import { DatabaseSettings } from '@/renderer/components/databases/database-settings'; +import { Database } from '@/renderer/components/databases/database'; +import { DatabaseViews } from '@/renderer/components/databases/database-views'; interface DatabaseContainerProps { databaseId: string; } export const DatabaseContainer = ({ databaseId }: DatabaseContainerProps) => { - const workspace = useWorkspace(); + const data = useEntryContainer(databaseId); - const { data: entry, isPending: isPendingEntry } = useQuery({ - type: 'entry_get', - entryId: databaseId, - accountId: workspace.accountId, - workspaceId: workspace.id, - }); + useEntryRadar(data.entry); - const database = entry as DatabaseEntry; - const databaseExists = !!database; - - const { data: root, isPending: isPendingRoot } = useQuery( - { - type: 'entry_get', - entryId: database?.rootId ?? '', - accountId: workspace.accountId, - workspaceId: workspace.id, - }, - { - enabled: databaseExists, - } - ); - - if (isPendingEntry || (isPendingRoot && databaseExists)) { + if (data.isPending) { return null; } - if (!database || !root) { + if (!data.entry) { return ; } - const role = extractEntryRole(root, workspace.userId); - if (!role) { - return ; - } + const { entry: database, role } = data; return ( -
- - -
+ + + + + + + + + + + + + ); }; diff --git a/apps/desktop/src/renderer/components/databases/database-create-dialog.tsx b/apps/desktop/src/renderer/components/databases/database-create-dialog.tsx index d85c88db..58eb42cc 100644 --- a/apps/desktop/src/renderer/components/databases/database-create-dialog.tsx +++ b/apps/desktop/src/renderer/components/databases/database-create-dialog.tsx @@ -11,6 +11,7 @@ import { import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { toast } from '@/renderer/hooks/use-toast'; +import { useLayout } from '@/renderer/contexts/layout'; interface DatabaseCreateDialogProps { spaceId: string; @@ -24,6 +25,7 @@ export const DatabaseCreateDialog = ({ onOpenChange, }: DatabaseCreateDialogProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const { mutate, isPending } = useMutation(); return ( @@ -61,7 +63,7 @@ export const DatabaseCreateDialog = ({ }, onSuccess(output) { onOpenChange(false); - workspace.openInMain(output.id); + layout.openLeft(output.id); }, onError(error) { toast({ diff --git a/apps/desktop/src/renderer/components/databases/database-delete-dialog.tsx b/apps/desktop/src/renderer/components/databases/database-delete-dialog.tsx index a4312a74..36eba5a3 100644 --- a/apps/desktop/src/renderer/components/databases/database-delete-dialog.tsx +++ b/apps/desktop/src/renderer/components/databases/database-delete-dialog.tsx @@ -9,6 +9,7 @@ import { } from '@/renderer/components/ui/alert-dialog'; import { Button } from '@/renderer/components/ui/button'; import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { toast } from '@/renderer/hooks/use-toast'; @@ -24,6 +25,7 @@ export const DatabaseDeleteDialog = ({ onOpenChange, }: DatabaseDeleteDialogProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const { mutate, isPending } = useMutation(); return ( @@ -53,7 +55,7 @@ export const DatabaseDeleteDialog = ({ }, onSuccess() { onOpenChange(false); - workspace.closeEntry(entryId); + layout.close(entryId); }, onError(error) { toast({ diff --git a/apps/desktop/src/renderer/components/databases/database-header.tsx b/apps/desktop/src/renderer/components/databases/database-header.tsx deleted file mode 100644 index c1362cce..00000000 --- a/apps/desktop/src/renderer/components/databases/database-header.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { DatabaseEntry, EntryRole } from '@colanode/core'; - -import { DatabaseSettings } from '@/renderer/components/databases/database-settings'; -import { EntryBreadcrumb } from '@/renderer/components/layouts/entry-breadcrumb'; -import { EntryFullscreenButton } from '@/renderer/components/layouts/entry-fullscreen-button'; -import { Header } from '@/renderer/components/ui/header'; -import { useContainer } from '@/renderer/contexts/container'; - -interface DatabaseHeaderProps { - database: DatabaseEntry; - role: EntryRole; -} - -export const DatabaseHeader = ({ database, role }: DatabaseHeaderProps) => { - const container = useContainer(); - - return ( -
-
-
- {container.mode === 'main' && } - {container.mode === 'modal' && ( - - )} -
-
- -
-
-
- ); -}; diff --git a/apps/desktop/src/renderer/components/databases/database-settings.tsx b/apps/desktop/src/renderer/components/databases/database-settings.tsx index 1b30a743..ebdcf053 100644 --- a/apps/desktop/src/renderer/components/databases/database-settings.tsx +++ b/apps/desktop/src/renderer/components/databases/database-settings.tsx @@ -30,7 +30,7 @@ export const DatabaseSettings = ({ database, role }: DatabaseSettingsProps) => { - + {database.attributes.name} diff --git a/apps/desktop/src/renderer/components/databases/database-sidiebar-item.tsx b/apps/desktop/src/renderer/components/databases/database-sidiebar-item.tsx index 9db16c5f..9fae5373 100644 --- a/apps/desktop/src/renderer/components/databases/database-sidiebar-item.tsx +++ b/apps/desktop/src/renderer/components/databases/database-sidiebar-item.tsx @@ -1,7 +1,7 @@ import { DatabaseEntry } from '@colanode/core'; import { Avatar } from '@/renderer/components/avatars/avatar'; -import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; import { cn } from '@/shared/lib/utils'; interface DatabaseSidebarItemProps { @@ -9,8 +9,8 @@ interface DatabaseSidebarItemProps { } export const DatabaseSidebarItem = ({ database }: DatabaseSidebarItemProps) => { - const workspace = useWorkspace(); - const isActive = workspace.isEntryActive(database.id); + const layout = useLayout(); + const isActive = layout.activeTab === database.id; const isUnread = false; const mentionsCount = 0; diff --git a/apps/desktop/src/renderer/components/databases/database-views.tsx b/apps/desktop/src/renderer/components/databases/database-views.tsx index 3ea1ab4a..3e2bb25b 100644 --- a/apps/desktop/src/renderer/components/databases/database-views.tsx +++ b/apps/desktop/src/renderer/components/databases/database-views.tsx @@ -25,7 +25,7 @@ export const DatabaseViews = () => { >
- + {activeView && } diff --git a/apps/desktop/src/renderer/components/databases/tables/table-view-name-cell.tsx b/apps/desktop/src/renderer/components/databases/tables/table-view-name-cell.tsx index ec7d4553..dff9214c 100644 --- a/apps/desktop/src/renderer/components/databases/tables/table-view-name-cell.tsx +++ b/apps/desktop/src/renderer/components/databases/tables/table-view-name-cell.tsx @@ -6,6 +6,7 @@ import React, { Fragment } from 'react'; import { Spinner } from '@/renderer/components/ui/spinner'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; +import { useLayout } from '@/renderer/contexts/layout'; import { toast } from '@/renderer/hooks/use-toast'; interface NameEditorProps { @@ -58,6 +59,7 @@ interface TableViewNameCellProps { export const TableViewNameCell = ({ record }: TableViewNameCellProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const [isEditing, setIsEditing] = React.useState(false); const { mutate, isPending } = useMutation(); @@ -111,7 +113,9 @@ export const TableViewNameCell = ({ record }: TableViewNameCellProps) => { diff --git a/apps/desktop/src/renderer/components/databases/tables/table-view.tsx b/apps/desktop/src/renderer/components/databases/tables/table-view.tsx index 330eb9b9..a88c7ae2 100644 --- a/apps/desktop/src/renderer/components/databases/tables/table-view.tsx +++ b/apps/desktop/src/renderer/components/databases/tables/table-view.tsx @@ -12,7 +12,7 @@ import { ViewTabs } from '@/renderer/components/databases/view-tabs'; export const TableView = () => { return ( -
+
diff --git a/apps/desktop/src/renderer/components/databases/view.tsx b/apps/desktop/src/renderer/components/databases/view.tsx index 16e936b8..e9be47e9 100644 --- a/apps/desktop/src/renderer/components/databases/view.tsx +++ b/apps/desktop/src/renderer/components/databases/view.tsx @@ -16,6 +16,7 @@ import { useDatabase } from '@/renderer/contexts/database'; import { ViewContext } from '@/renderer/contexts/view'; import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; +import { useLayout } from '@/renderer/contexts/layout'; import { generateFieldValuesFromFilters, generateViewFieldIndex, @@ -34,6 +35,7 @@ interface ViewProps { export const View = ({ view }: ViewProps) => { const workspace = useWorkspace(); const database = useDatabase(); + const layout = useLayout(); const { mutate } = useMutation(); const fields: ViewField[] = React.useMemo(() => { @@ -537,7 +539,7 @@ export const View = ({ view }: ViewProps) => { fields, }, onSuccess: (output) => { - workspace.openInModal(output.id); + layout.previewLeft(output.id, true); }, onError(error) { toast({ diff --git a/apps/desktop/src/renderer/components/files/file-block.tsx b/apps/desktop/src/renderer/components/files/file-block.tsx index a73f1853..4d65b9dc 100644 --- a/apps/desktop/src/renderer/components/files/file-block.tsx +++ b/apps/desktop/src/renderer/components/files/file-block.tsx @@ -1,5 +1,6 @@ import { FilePreview } from '@/renderer/components/files/file-preview'; import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; import { useQuery } from '@/renderer/hooks/use-query'; interface FileBlockProps { @@ -8,6 +9,7 @@ interface FileBlockProps { export const FileBlock = ({ id }: FileBlockProps) => { const workspace = useWorkspace(); + const layout = useLayout(); const { data } = useQuery({ type: 'file_get', @@ -24,7 +26,7 @@ export const FileBlock = ({ id }: FileBlockProps) => {
{ - workspace.openInModal(id); + layout.previewLeft(id, true); }} > diff --git a/apps/desktop/src/renderer/components/files/file-body.tsx b/apps/desktop/src/renderer/components/files/file-body.tsx index fbb27322..57dd82c8 100644 --- a/apps/desktop/src/renderer/components/files/file-body.tsx +++ b/apps/desktop/src/renderer/components/files/file-body.tsx @@ -1,11 +1,9 @@ import { SquareArrowOutUpRight } from 'lucide-react'; -// import { useEffect } from 'react'; import { FilePreview } from '@/renderer/components/files/file-preview'; import { FileSidebar } from '@/renderer/components/files/file-sidebar'; import { Button } from '@/renderer/components/ui/button'; import { useWorkspace } from '@/renderer/contexts/workspace'; -// import { useRadar } from '@/renderer/contexts/radar'; import { FileWithState } from '@/shared/types/files'; interface FileBodyProps { @@ -14,27 +12,6 @@ interface FileBodyProps { export const FileBody = ({ file }: FileBodyProps) => { const workspace = useWorkspace(); - // const radar = useRadar(); - - // useEffect(() => { - // radar.markAsOpened( - // workspace.userId, - // file.id, - // 'file', - // file.transactionId - // ); - - // const interval = setInterval(() => { - // radar.markAsOpened( - // workspace.userId, - // file.id, - // file.type, - // file.transactionId - // ); - // }, 60000); - - // return () => clearInterval(interval); - // }, [file.id, file.type, file.transactionId]); return (
diff --git a/apps/desktop/src/renderer/components/files/file-breadcrumb-item.tsx b/apps/desktop/src/renderer/components/files/file-breadcrumb-item.tsx new file mode 100644 index 00000000..0225c90c --- /dev/null +++ b/apps/desktop/src/renderer/components/files/file-breadcrumb-item.tsx @@ -0,0 +1,32 @@ +import { FileThumbnail } from '@/renderer/components/files/file-thumbnail'; +import { useQuery } from '@/renderer/hooks/use-query'; +import { useWorkspace } from '@/renderer/contexts/workspace'; + +interface FileBreadcrumbItemProps { + id: string; +} + +export const FileBreadcrumbItem = ({ id }: FileBreadcrumbItemProps) => { + const workspace = useWorkspace(); + + const { data: file } = useQuery({ + type: 'file_get', + id, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + if (!file) { + return null; + } + + return ( +
+ + {file.name} +
+ ); +}; diff --git a/apps/desktop/src/renderer/components/files/file-container-tab.tsx b/apps/desktop/src/renderer/components/files/file-container-tab.tsx new file mode 100644 index 00000000..d676ec87 --- /dev/null +++ b/apps/desktop/src/renderer/components/files/file-container-tab.tsx @@ -0,0 +1,32 @@ +import { FileThumbnail } from '@/renderer/components/files/file-thumbnail'; +import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useQuery } from '@/renderer/hooks/use-query'; + +interface FileContainerTabProps { + fileId: string; +} + +export const FileContainerTab = ({ fileId }: FileContainerTabProps) => { + const workspace = useWorkspace(); + + const { data: file } = useQuery({ + type: 'file_get', + id: fileId, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + if (!file) { + return

Not found

; + } + + return ( +
+ + {file.name} +
+ ); +}; diff --git a/apps/desktop/src/renderer/components/files/file-container.tsx b/apps/desktop/src/renderer/components/files/file-container.tsx index aeb9fd62..633da567 100644 --- a/apps/desktop/src/renderer/components/files/file-container.tsx +++ b/apps/desktop/src/renderer/components/files/file-container.tsx @@ -1,72 +1,41 @@ -import { extractEntryRole } from '@colanode/core'; - import { FileBody } from '@/renderer/components/files/file-body'; -import { FileHeader } from '@/renderer/components/files/file-header'; +import { + Container, + ContainerBody, + ContainerHeader, + ContainerSettings, +} from '@/renderer/components/ui/container'; +import { ContainerBreadcrumb } from '@/renderer/components/layouts/breadcrumbs/container-breadrumb'; import { FileNotFound } from '@/renderer/components/files/file-not-found'; -import { useWorkspace } from '@/renderer/contexts/workspace'; -import { useQuery } from '@/renderer/hooks/use-query'; +import { useFileContainer } from '@/renderer/hooks/use-file-container'; +import { FileSettings } from '@/renderer/components/files/file-settings'; interface FileContainerProps { fileId: string; } export const FileContainer = ({ fileId }: FileContainerProps) => { - const workspace = useWorkspace(); + const data = useFileContainer(fileId); - const { data: file, isPending: isPendingFile } = useQuery({ - type: 'file_get', - id: fileId, - accountId: workspace.accountId, - workspaceId: workspace.id, - }); - - const fileExists = !!file; - - const { data: entry, isPending: isPendingEntry } = useQuery( - { - type: 'entry_get', - entryId: file?.entryId ?? '', - accountId: workspace.accountId, - workspaceId: workspace.id, - }, - { - enabled: fileExists, - } - ); - - const { data: root, isPending: isPendingRoot } = useQuery( - { - type: 'entry_get', - entryId: file?.rootId ?? '', - accountId: workspace.accountId, - workspaceId: workspace.id, - }, - { - enabled: fileExists, - } - ); - - if ( - isPendingFile || - (isPendingEntry && fileExists) || - (isPendingRoot && fileExists) - ) { + if (data.isPending) { return null; } - if (!file || !entry || !root) { - return ; - } - - const role = extractEntryRole(root, workspace.userId); - if (!role) { + if (!data.file) { return ; } return ( -
- - -
+ + + + + + + + + + + ); }; diff --git a/apps/desktop/src/renderer/components/files/file-context-menu.tsx b/apps/desktop/src/renderer/components/files/file-context-menu.tsx index f1943f20..af9be467 100644 --- a/apps/desktop/src/renderer/components/files/file-context-menu.tsx +++ b/apps/desktop/src/renderer/components/files/file-context-menu.tsx @@ -9,7 +9,7 @@ import { ContextMenuShortcut, ContextMenuTrigger, } from '@/renderer/components/ui/context-menu'; -import { useWorkspace } from '@/renderer/contexts/workspace'; +import { useLayout } from '@/renderer/contexts/layout'; interface FileContextMenuProps { id: string; @@ -17,7 +17,7 @@ interface FileContextMenuProps { } export const FileContextMenu = ({ id, children }: FileContextMenuProps) => { - const workspace = useWorkspace(); + const layout = useLayout(); const [openDelete, setOpenDelete] = React.useState(false); return ( @@ -27,7 +27,7 @@ export const FileContextMenu = ({ id, children }: FileContextMenuProps) => { { - workspace.openInModal(id); + layout.previewLeft(id, true); }} className="pl-2" > diff --git a/apps/desktop/src/renderer/components/files/file-header.tsx b/apps/desktop/src/renderer/components/files/file-header.tsx deleted file mode 100644 index d54af8f8..00000000 --- a/apps/desktop/src/renderer/components/files/file-header.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Entry, EntryRole } from '@colanode/core'; - -import { FileSettings } from '@/renderer/components/files/file-settings'; -import { EntryBreadcrumb } from '@/renderer/components/layouts/entry-breadcrumb'; -import { EntryFullscreenButton } from '@/renderer/components/layouts/entry-fullscreen-button'; -import { Header } from '@/renderer/components/ui/header'; -import { useContainer } from '@/renderer/contexts/container'; -import { FileWithState } from '@/shared/types/files'; - -interface FileHeaderProps { - file: FileWithState; - entry: Entry; - role: EntryRole; -} - -export const FileHeader = ({ file, entry, role }: FileHeaderProps) => { - const container = useContainer(); - - return ( -
-
-
- {container.mode === 'main' && } - {container.mode === 'modal' && ( - - )} -
-
- -
-
-
- ); -}; diff --git a/apps/desktop/src/renderer/components/files/file-settings.tsx b/apps/desktop/src/renderer/components/files/file-settings.tsx index 3f2ce08b..e7e57fa4 100644 --- a/apps/desktop/src/renderer/components/files/file-settings.tsx +++ b/apps/desktop/src/renderer/components/files/file-settings.tsx @@ -1,6 +1,6 @@ import { Copy, Settings, Trash2 } from 'lucide-react'; import React from 'react'; -import { Entry, EntryRole, hasEntryRole } from '@colanode/core'; +import { EntryRole, hasEntryRole } from '@colanode/core'; import { FileDeleteDialog } from '@/renderer/components/files/file-delete-dialog'; import { @@ -15,14 +15,13 @@ import { useWorkspace } from '@/renderer/contexts/workspace'; interface FileSettingsProps { file: FileWithState; role: EntryRole; - entry: Entry; } -export const FileSettings = ({ file, role, entry }: FileSettingsProps) => { +export const FileSettings = ({ file, role }: FileSettingsProps) => { const workspace = useWorkspace(); const [showDeleteModal, setShowDeleteModal] = React.useState(false); const canDelete = - file.parentId === entry.id && + file.parentId === file.entryId && (file.createdBy === workspace.userId || hasEntryRole(role, 'editor')); return ( diff --git a/apps/desktop/src/renderer/components/folders/folder-body.tsx b/apps/desktop/src/renderer/components/folders/folder-body.tsx index 8557b79c..98826624 100644 --- a/apps/desktop/src/renderer/components/folders/folder-body.tsx +++ b/apps/desktop/src/renderer/components/folders/folder-body.tsx @@ -7,7 +7,7 @@ import { List, Upload, } from 'lucide-react'; -import React, { useEffect } from 'react'; +import React from 'react'; import { FolderFiles } from '@/renderer/components/folders/folder-files'; import { Button } from '@/renderer/components/ui/button'; @@ -25,16 +25,15 @@ import { useWorkspace } from '@/renderer/contexts/workspace'; import { useMutation } from '@/renderer/hooks/use-mutation'; import { FolderLayoutType } from '@/shared/types/folders'; import { toast } from '@/renderer/hooks/use-toast'; -import { useRadar } from '@/renderer/contexts/radar'; -export type FolderLayout = { +export type FolderLayoutOption = { value: FolderLayoutType; name: string; description: string; icon: React.FC>; }; -export const folderLayouts: FolderLayout[] = [ +export const folderLayouts: FolderLayoutOption[] = [ { name: 'Grid', value: 'grid', @@ -62,7 +61,6 @@ interface FolderBodyProps { export const FolderBody = ({ folder }: FolderBodyProps) => { const workspace = useWorkspace(); - const radar = useRadar(); const { mutate } = useMutation(); const [layout, setLayout] = React.useState('grid'); @@ -120,16 +118,6 @@ export const FolderBody = ({ folder }: FolderBodyProps) => { isDialogOpenedRef.current = false; }; - useEffect(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, folder.id); - - const interval = setInterval(() => { - radar.markEntryAsOpened(workspace.accountId, workspace.id, folder.id); - }, 60000); - - return () => clearInterval(interval); - }, [folder.id, folder.type, folder.transactionId]); - return ( { files.forEach((file) => console.log(file)); }} > -
+