diff --git a/apps/nextjs-app/src/features/app/blocks/view/gallery/components/Card.tsx b/apps/nextjs-app/src/features/app/blocks/view/gallery/components/Card.tsx index 6d7d4b733f..df27bc1bab 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/gallery/components/Card.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/gallery/components/Card.tsx @@ -19,6 +19,7 @@ import { useTranslation } from 'next-i18next'; import { Fragment, useMemo } from 'react'; import { tableConfig } from '@/features/i18n/table.config'; import { useContextMenu } from '../../hooks/useContextMenu'; +import { useExpandRecord } from '../../hooks/useExpandRecord'; import { useGallery } from '../hooks'; import { CARD_COVER_HEIGHT, CARD_STYLE } from '../utils'; import { CardCarousel } from './CardCarousel'; @@ -33,16 +34,10 @@ export const Card = (props: IKanbanCardProps) => { const viewId = useViewId(); const getFieldStatic = useFieldStaticGetter(); const { t } = useTranslation(tableConfig.i18nNamespaces); - const { - coverField, - primaryField, - displayFields, - permission, - isCoverFit, - isFieldNameHidden, - setExpandRecordId, - } = useGallery(); + const { coverField, primaryField, displayFields, permission, isCoverFit, isFieldNameHidden } = + useGallery(); const { copyRecordUrl, viewRecordHistory, addRecordComment } = useContextMenu(); + const expandRecord = useExpandRecord(); const { cardCreatable, cardDeletable, cardEditable, cardCommentCreatable } = permission; const coverFieldId = coverField?.id; @@ -59,8 +54,8 @@ export const Card = (props: IKanbanCardProps) => { ); }, [card, primaryField, t]); - const onExpand = () => { - setExpandRecordId(card.id); + const onExpand = async () => { + await expandRecord(card.id); }; const onDelete = () => { @@ -87,7 +82,7 @@ export const Card = (props: IKanbanCardProps) => { const record = res.data.records[0]; if (record != null) { - setExpandRecordId(record.id); + await expandRecord(record.id); } }; @@ -96,12 +91,10 @@ export const Card = (props: IKanbanCardProps) => { }; const onViewRecordHistory = async () => { - setExpandRecordId(card.id); await viewRecordHistory(card.id); }; const onAddRecordComment = async () => { - setExpandRecordId(card.id); await addRecordComment(card.id); }; diff --git a/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryContext.ts b/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryContext.ts index 630cfe7be1..6523ed70a4 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryContext.ts +++ b/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryContext.ts @@ -1,6 +1,5 @@ import type { IGetRecordsRo } from '@teable/openapi'; import type { AttachmentField, IFieldInstance } from '@teable/sdk/model'; -import type { Dispatch, SetStateAction } from 'react'; import { createContext } from 'react'; import type { IGalleryPermission } from '../type'; @@ -12,7 +11,6 @@ export interface IGalleryContext { permission: IGalleryPermission; primaryField: IFieldInstance; displayFields: IFieldInstance[]; - setExpandRecordId: Dispatch>; } export const GalleryContext = createContext(null!); diff --git a/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryProvider.tsx b/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryProvider.tsx index 9d8bca5c33..9aa3647607 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryProvider.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/gallery/context/GalleryProvider.tsx @@ -105,7 +105,6 @@ export const GalleryProvider = ({ children }: { children: ReactNode }) => { coverField, primaryField, displayFields, - setExpandRecordId, }; }, [ recordQuery, @@ -115,7 +114,6 @@ export const GalleryProvider = ({ children }: { children: ReactNode }) => { coverField, primaryField, displayFields, - setExpandRecordId, ]); const onClose = () => { diff --git a/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.spec.ts b/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.spec.ts new file mode 100644 index 0000000000..ca0acd65cb --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.spec.ts @@ -0,0 +1,31 @@ +import { renderHook } from '@testing-library/react'; +import { useExpandRecord } from './useExpandRecord'; + +const push = vi.fn(); + +vi.mock('next/router', () => ({ + useRouter: () => ({ + pathname: '/base/[baseId]/[tableId]/[viewId]', + query: { baseId: 'bse1', tableId: 'tbl1', viewId: 'viw1' }, + push, + }), +})); + +describe('useExpandRecord', () => { + beforeEach(() => push.mockReset()); + + it('puts the record id in the url so the expanded record stays linkable', async () => { + const { result } = renderHook(() => useExpandRecord()); + + await result.current('rec1'); + + expect(push).toHaveBeenCalledWith( + { + pathname: '/base/[baseId]/[tableId]/[viewId]', + query: { baseId: 'bse1', tableId: 'tbl1', viewId: 'viw1', recordId: 'rec1' }, + }, + undefined, + { shallow: true } + ); + }); +}); diff --git a/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.ts b/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.ts new file mode 100644 index 0000000000..769f600e05 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/view/hooks/useExpandRecord.ts @@ -0,0 +1,24 @@ +import { useRouter } from 'next/router'; +import { useCallback } from 'react'; + +// expanding a record must go through the url, otherwise the expanded record is +// not linkable (copy record url would return the bare view url) +export const useExpandRecord = () => { + const router = useRouter(); + + return useCallback( + async (recordId: string) => { + await router.push( + { + pathname: router.pathname, + query: { ...router.query, recordId }, + }, + undefined, + { + shallow: true, + } + ); + }, + [router] + ); +}; diff --git a/apps/nextjs-app/src/features/app/blocks/view/kanban/components/KanbanCard.tsx b/apps/nextjs-app/src/features/app/blocks/view/kanban/components/KanbanCard.tsx index 5849df6c95..edd70c9105 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/kanban/components/KanbanCard.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/kanban/components/KanbanCard.tsx @@ -21,6 +21,7 @@ import { useMemo } from 'react'; import { tableConfig } from '@/features/i18n/table.config'; import { CardCarousel } from '../../gallery/components'; import { useContextMenu } from '../../hooks/useContextMenu'; +import { useExpandRecord } from '../../hooks/useExpandRecord'; import type { IKanbanContext } from '../context'; import { useKanban } from '../hooks'; import type { IStackData } from '../type'; @@ -47,9 +48,9 @@ export const KanbanCard = (props: IKanbanCardProps) => { coverField, isCoverFit, isFieldNameHidden, - setExpandRecordId, } = useKanban() as Required; const { copyRecordUrl, viewRecordHistory, addRecordComment } = useContextMenu(); + const expandRecord = useExpandRecord(); const { cardCreatable, cardDeletable, cardEditable, cardCommentCreatable } = permission; const { id: fieldId } = stackField; @@ -67,8 +68,8 @@ export const KanbanCard = (props: IKanbanCardProps) => { ); }, [card, primaryField, t]); - const onExpand = () => { - setExpandRecordId(card.id); + const onExpand = async () => { + await expandRecord(card.id); }; const onDelete = () => { @@ -100,7 +101,7 @@ export const KanbanCard = (props: IKanbanCardProps) => { const record = res.data.records[0]; if (record != null) { - setExpandRecordId(record.id); + await expandRecord(record.id); } }; @@ -109,12 +110,10 @@ export const KanbanCard = (props: IKanbanCardProps) => { }; const onViewRecordHistory = async () => { - setExpandRecordId(card.id); await viewRecordHistory(card.id); }; const onAddRecordComment = async () => { - setExpandRecordId(card.id); await addRecordComment(card.id); }; diff --git a/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanContext.ts b/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanContext.ts index 6e2d062610..a4b4f17a73 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanContext.ts +++ b/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanContext.ts @@ -1,6 +1,5 @@ import type { IGetRecordsRo } from '@teable/openapi'; import type { AttachmentField, IFieldInstance } from '@teable/sdk/model'; -import type { Dispatch, SetStateAction } from 'react'; import { createContext } from 'react'; import type { IKanbanPermission, IStackData } from '../type'; @@ -15,7 +14,6 @@ export interface IKanbanContext { permission: IKanbanPermission; primaryField: IFieldInstance; displayFields: IFieldInstance[]; - setExpandRecordId: Dispatch>; } export const KanbanContext = createContext(null!); diff --git a/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanProvider.tsx b/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanProvider.tsx index 3072b7d4c4..8ccfbfb2f5 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanProvider.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/kanban/context/KanbanProvider.tsx @@ -270,7 +270,6 @@ export const KanbanProvider = ({ children }: { children: ReactNode }) => { primaryField, displayFields, stackCollection, - setExpandRecordId, }; }, [ recordQuery, @@ -283,7 +282,6 @@ export const KanbanProvider = ({ children }: { children: ReactNode }) => { primaryField, displayFields, stackCollection, - setExpandRecordId, ]); const onClose = () => {