mirror of
https://github.com/suitenumerique/docs.git
synced 2026-08-17 21:25:43 +02:00
👔(frontend) integrate dedicated content endpoint
To improve the performance of loading document content, we have implemented a dedicated endpoint for fetching document content. This allows us to load the document metadata and content separately. We updated the different components to utilize this new endpoint, ensuring that the document content is fetched and updated correctly.
This commit is contained in:
@@ -250,22 +250,16 @@ export const waitForResponseCreateDoc = (page: Page) => {
|
||||
};
|
||||
|
||||
export const mockedDocument = async (page: Page, data: object) => {
|
||||
await page.route(/\**\/documents\/\**/, async (route) => {
|
||||
// document/[ID]/ or document/[ID]/tree/ routes
|
||||
await page.route(/.*\/documents\/[^/]+\/(?:$|tree\/.*)/, async (route) => {
|
||||
const request = route.request();
|
||||
if (
|
||||
request.method().includes('GET') &&
|
||||
!request.url().includes('page=') &&
|
||||
!request.url().includes('versions') &&
|
||||
!request.url().includes('accesses') &&
|
||||
!request.url().includes('invitations')
|
||||
) {
|
||||
if (request.method().includes('GET') && !request.url().includes('page=')) {
|
||||
const { abilities, ...doc } = data as unknown as {
|
||||
abilities?: Record<string, unknown>;
|
||||
};
|
||||
await route.fulfill({
|
||||
json: {
|
||||
id: 'mocked-document-id',
|
||||
content: '',
|
||||
title: 'Mocked document',
|
||||
path: '000000',
|
||||
abilities: {
|
||||
@@ -299,6 +293,17 @@ export const mockedDocument = async (page: Page, data: object) => {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(/.*\/documents\/[^/]+\/content\/$/, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('GET')) {
|
||||
await route.fulfill({
|
||||
body: '',
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const mockedListDocs = async (page: Page, data: object[] = []) => {
|
||||
|
||||
@@ -27,25 +27,16 @@ export const overrideDocContent = async ({
|
||||
browserName: BrowserName;
|
||||
}) => {
|
||||
// Override content prop with assets/base-content-test-pdf.txt
|
||||
await page.route(/\**\/documents\/\**/, async (route) => {
|
||||
await page.route(/.*\/documents\/[^/]+\/content\/$/, async (route) => {
|
||||
const request = route.request();
|
||||
if (
|
||||
request.method().includes('GET') &&
|
||||
!request.url().includes('page=') &&
|
||||
!request.url().includes('versions') &&
|
||||
!request.url().includes('accesses') &&
|
||||
!request.url().includes('invitations')
|
||||
) {
|
||||
if (request.method() === 'GET') {
|
||||
const response = await route.fetch();
|
||||
const json = await response.json();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
json.content = fs.readFileSync(
|
||||
path.join(__dirname, 'assets/base-content-test-pdf.txt'),
|
||||
'utf-8',
|
||||
);
|
||||
void route.fulfill({
|
||||
response,
|
||||
body: JSON.stringify(json),
|
||||
body: fs.readFileSync(
|
||||
path.join(__dirname, 'assets/base-content-test-pdf.txt'),
|
||||
'utf-8',
|
||||
),
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Doc,
|
||||
LinkReach,
|
||||
getDocLinkReach,
|
||||
useCollaboration,
|
||||
useIsCollaborativeEditable,
|
||||
useProviderStore,
|
||||
} from '@/docs/doc-management';
|
||||
@@ -79,6 +80,7 @@ interface DocEditorProps {
|
||||
}
|
||||
|
||||
export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
useCollaboration(doc.id);
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { provider, isReady } = useProviderStore();
|
||||
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
|
||||
|
||||
+19
-15
@@ -67,13 +67,15 @@ describe('useSaveDoc', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const docId = 'test-doc-id';
|
||||
|
||||
fetchMock.patch('http://test.jest/api/v1.0/documents/test-doc-id/', {
|
||||
body: JSON.stringify({
|
||||
id: 'test-doc-id',
|
||||
content: 'test-content',
|
||||
title: 'test-title',
|
||||
}),
|
||||
});
|
||||
fetchMock.patch(
|
||||
'http://test.jest/api/v1.0/documents/test-doc-id/content/',
|
||||
{
|
||||
body: JSON.stringify({
|
||||
id: 'test-doc-id',
|
||||
content: 'test-content',
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
renderHook(() => useSaveDoc(docId, yDoc, true), {
|
||||
wrapper: AppWrapper,
|
||||
@@ -94,7 +96,7 @@ describe('useSaveDoc', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchMock.lastCall()?.[0]).toBe(
|
||||
'http://test.jest/api/v1.0/documents/test-doc-id/',
|
||||
'http://test.jest/api/v1.0/documents/test-doc-id/content/',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -104,13 +106,15 @@ describe('useSaveDoc', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const docId = 'test-doc-id';
|
||||
|
||||
fetchMock.patch('http://test.jest/api/v1.0/documents/test-doc-id/', {
|
||||
body: JSON.stringify({
|
||||
id: 'test-doc-id',
|
||||
content: 'test-content',
|
||||
title: 'test-title',
|
||||
}),
|
||||
});
|
||||
fetchMock.patch(
|
||||
'http://test.jest/api/v1.0/documents/test-doc-id/content/',
|
||||
{
|
||||
body: JSON.stringify({
|
||||
id: 'test-doc-id',
|
||||
content: 'test-content',
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
renderHook(() => useSaveDoc(docId, yDoc, true), {
|
||||
wrapper: AppWrapper,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { useUpdateDoc } from '@/docs/doc-management/';
|
||||
import { KEY_DOC_CONTENT } from '@/docs//doc-management/api/useDocContent';
|
||||
import { useDocContentUpdate } from '@/docs/doc-management/api/useDocContentUpdate';
|
||||
import { KEY_LIST_DOC_VERSIONS } from '@/docs/doc-versioning/api/useDocVersions';
|
||||
import { toBase64 } from '@/utils/string';
|
||||
import { isFirefox } from '@/utils/userAgent';
|
||||
@@ -14,11 +15,16 @@ export const useSaveDoc = (
|
||||
yDoc: Y.Doc,
|
||||
isConnectedToCollabServer: boolean,
|
||||
) => {
|
||||
const { mutate: updateDoc } = useUpdateDoc({
|
||||
listInvalidQueries: [KEY_LIST_DOC_VERSIONS],
|
||||
const isSavingRef = useRef(false);
|
||||
const { mutate: updateDocContent } = useDocContentUpdate({
|
||||
listInvalidQueries: [KEY_LIST_DOC_VERSIONS, KEY_DOC_CONTENT],
|
||||
onSuccess: () => {
|
||||
isSavingRef.current = false;
|
||||
setIsLocalChange(false);
|
||||
},
|
||||
onError: () => {
|
||||
isSavingRef.current = false;
|
||||
},
|
||||
});
|
||||
const [isLocalChange, setIsLocalChange] = useState<boolean>(false);
|
||||
|
||||
@@ -64,18 +70,19 @@ export const useSaveDoc = (
|
||||
}, [yDoc]);
|
||||
|
||||
const saveDoc = useCallback(() => {
|
||||
if (!isLocalChange) {
|
||||
if (!isLocalChange || isSavingRef.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
updateDoc({
|
||||
isSavingRef.current = true;
|
||||
updateDocContent({
|
||||
id: docId,
|
||||
content: toBase64(Y.encodeStateAsUpdate(yDoc)),
|
||||
websocket: isConnectedToCollabServer,
|
||||
});
|
||||
|
||||
return true;
|
||||
}, [isLocalChange, updateDoc, docId, yDoc, isConnectedToCollabServer]);
|
||||
}, [isLocalChange, updateDocContent, docId, yDoc, isConnectedToCollabServer]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { UseQueryOptions, useQuery } from '@tanstack/react-query';
|
||||
import { validate as uuidValidate } from 'uuid';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
export type DocContentParams = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const getDocContent = async ({
|
||||
id,
|
||||
}: DocContentParams): Promise<string> => {
|
||||
if (!uuidValidate(id)) {
|
||||
throw new Error(`Invalid doc id in getDocContent: ${id}`);
|
||||
}
|
||||
|
||||
const response = await fetchAPI(`documents/${id}/content/`, {
|
||||
headers: {
|
||||
accept: 'text/plain,application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError('Failed to get the doc', await errorCauses(response));
|
||||
}
|
||||
|
||||
return response.text();
|
||||
};
|
||||
|
||||
export const KEY_DOC_CONTENT = 'doc-content';
|
||||
|
||||
export function useDocContent(
|
||||
param: DocContentParams,
|
||||
queryConfig?: UseQueryOptions<string, APIError, string>,
|
||||
) {
|
||||
return useQuery<string, APIError, string>({
|
||||
queryKey: queryConfig?.queryKey ?? [KEY_DOC_CONTENT, param],
|
||||
queryFn: () => getDocContent(param),
|
||||
...queryConfig,
|
||||
});
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
UseMutationOptions,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
import { Doc } from '../types';
|
||||
|
||||
import { KEY_CAN_EDIT } from './useDocCanEdit';
|
||||
|
||||
interface UpdateDocContentParams {
|
||||
id: Doc['id'];
|
||||
content: string; // Base64 encoded content
|
||||
websocket?: boolean;
|
||||
}
|
||||
|
||||
export const updateDocContent = async ({
|
||||
id,
|
||||
content,
|
||||
websocket,
|
||||
}: UpdateDocContentParams): Promise<void> => {
|
||||
const response = await fetchAPI(`documents/${id}/content/`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
content,
|
||||
websocket,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError(
|
||||
'Failed to update the doc content',
|
||||
await errorCauses(response),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type UseDocContentUpdate = UseMutationOptions<
|
||||
void,
|
||||
APIError,
|
||||
UpdateDocContentParams
|
||||
> & {
|
||||
listInvalidQueries?: string[];
|
||||
};
|
||||
|
||||
export function useDocContentUpdate(queryConfig?: UseDocContentUpdate) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, UpdateDocContentParams>({
|
||||
mutationFn: updateDocContent,
|
||||
...queryConfig,
|
||||
onSuccess: (data, variables, onMutateResult, context) => {
|
||||
queryConfig?.listInvalidQueries?.forEach((queryKey) => {
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [queryKey],
|
||||
});
|
||||
});
|
||||
|
||||
if (queryConfig?.onSuccess) {
|
||||
void queryConfig.onSuccess(data, variables, onMutateResult, context);
|
||||
}
|
||||
},
|
||||
onError: (error, variables, onMutateResult, context) => {
|
||||
// If error it means the user is probably not allowed to edit the doc
|
||||
// so we invalidate the canEdit query to update the UI accordingly
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_CAN_EDIT],
|
||||
});
|
||||
|
||||
if (queryConfig?.onError) {
|
||||
queryConfig.onError(error, variables, onMutateResult, context);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -17,8 +17,9 @@ import { toBase64 } from '@/utils/string';
|
||||
import { useProviderStore } from '../stores';
|
||||
import { Doc } from '../types';
|
||||
|
||||
import { KEY_DOC_CONTENT } from './useDocContent';
|
||||
import { useDocContentUpdate } from './useDocContentUpdate';
|
||||
import { KEY_LIST_DOC } from './useDocs';
|
||||
import { useUpdateDoc } from './useUpdateDoc';
|
||||
|
||||
interface DuplicateDocPayload {
|
||||
docId: string;
|
||||
@@ -62,8 +63,8 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
|
||||
const { t } = useTranslation();
|
||||
const { provider } = useProviderStore();
|
||||
|
||||
const { mutateAsync: updateDoc } = useUpdateDoc({
|
||||
listInvalidQueries: [KEY_LIST_DOC_VERSIONS],
|
||||
const { mutateAsync: updateDocContent } = useDocContentUpdate({
|
||||
listInvalidQueries: [KEY_LIST_DOC_VERSIONS, KEY_DOC_CONTENT],
|
||||
});
|
||||
|
||||
return useMutation<DuplicateDocResponse, APIError, DuplicateDocParams>({
|
||||
@@ -75,7 +76,7 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
|
||||
provider.document.guid === variables.docId;
|
||||
|
||||
if (canSave) {
|
||||
await updateDoc({
|
||||
await updateDocContent({
|
||||
id: variables.docId,
|
||||
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
|
||||
});
|
||||
|
||||
@@ -8,12 +8,10 @@ import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
import { Doc } from '../types';
|
||||
|
||||
import { KEY_CAN_EDIT } from './useDocCanEdit';
|
||||
|
||||
export type UpdateDocParams = Pick<Doc, 'id'> &
|
||||
Partial<Pick<Doc, 'content' | 'title'>> & {
|
||||
websocket?: boolean;
|
||||
};
|
||||
export interface UpdateDocParams {
|
||||
id: Doc['id'];
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const updateDoc = async ({
|
||||
id,
|
||||
@@ -33,7 +31,7 @@ export const updateDoc = async ({
|
||||
return response.json() as Promise<Doc>;
|
||||
};
|
||||
|
||||
type UseUpdateDoc = UseMutationOptions<Doc, APIError, Partial<Doc>> & {
|
||||
type UseUpdateDoc = UseMutationOptions<Doc, APIError, UpdateDocParams> & {
|
||||
listInvalidQueries?: string[];
|
||||
};
|
||||
|
||||
@@ -54,12 +52,6 @@ export function useUpdateDoc(queryConfig?: UseUpdateDoc) {
|
||||
}
|
||||
},
|
||||
onError: (error, variables, onMutateResult, context) => {
|
||||
// If error it means the user is probably not allowed to edit the doc
|
||||
// so we invalidate the canEdit query to update the UI accordingly
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_CAN_EDIT],
|
||||
});
|
||||
|
||||
if (queryConfig?.onError) {
|
||||
queryConfig.onError(error, variables, onMutateResult, context);
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import { useConfig } from '@/core';
|
||||
import { KEY_LIST_DOC_TRASHBIN } from '@/docs/docs-grid';
|
||||
import { useKeyboardAction } from '@/hooks';
|
||||
|
||||
import { KEY_DOC } from '../api';
|
||||
import { KEY_LIST_DOC } from '../api/useDocs';
|
||||
import { useRemoveDoc } from '../api/useRemoveDoc';
|
||||
import { useDocUtils } from '../hooks';
|
||||
@@ -44,7 +45,7 @@ export const ModalRemoveDoc = ({
|
||||
isError,
|
||||
error,
|
||||
} = useRemoveDoc({
|
||||
listInvalidQueries: [KEY_LIST_DOC, KEY_LIST_DOC_TRASHBIN],
|
||||
listInvalidQueries: [KEY_LIST_DOC, KEY_LIST_DOC_TRASHBIN, KEY_DOC],
|
||||
options: {
|
||||
onSuccess: () => {
|
||||
if (onSuccess) {
|
||||
|
||||
+62
-8
@@ -1,29 +1,83 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useCollaborationUrl } from '@/core/config';
|
||||
import {
|
||||
KEY_DOC_CONTENT,
|
||||
useDocContent,
|
||||
} from '@/docs/doc-management/api/useDocContent';
|
||||
import { useProviderStore } from '@/docs/doc-management/stores/useProviderStore';
|
||||
import { useBroadcastStore } from '@/stores/useBroadcastStore';
|
||||
|
||||
import { useProviderStore } from '../stores/useProviderStore';
|
||||
import { Base64 } from '../types';
|
||||
import { KEY_DOC } from '../api';
|
||||
|
||||
export const useCollaboration = (room?: string, initialContent?: Base64) => {
|
||||
export const useCollaboration = (room: string) => {
|
||||
const collaborationUrl = useCollaborationUrl(room);
|
||||
const { addTask } = useBroadcastStore();
|
||||
const queryClient = useQueryClient();
|
||||
const { setBroadcastProvider, cleanupBroadcast } = useBroadcastStore();
|
||||
const { provider, createProvider, destroyProvider } = useProviderStore();
|
||||
const {
|
||||
provider,
|
||||
createProvider,
|
||||
destroyProvider,
|
||||
isReady,
|
||||
hasLostConnection,
|
||||
resetLostConnection,
|
||||
} = useProviderStore();
|
||||
const { data: docContent } = useDocContent(
|
||||
{ id: room },
|
||||
{
|
||||
staleTime: 30000, // 30 seconds - We keep the data fresh as it is a highly collaborative page
|
||||
queryKey: [KEY_DOC_CONTENT, { id: room }],
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* When the provider detects a lost connection, we invalidate the document query to trigger a refetch.
|
||||
* Because it can be because the user has access to the document that are modified
|
||||
* (e.g., permissions changed, document deleted, user removed)
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!room || !collaborationUrl || provider) {
|
||||
if (hasLostConnection && room) {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC, { id: room }],
|
||||
});
|
||||
resetLostConnection();
|
||||
}
|
||||
}, [hasLostConnection, room, queryClient, resetLostConnection]);
|
||||
|
||||
/**
|
||||
* We add a broadcast task to reset the query cache
|
||||
* when the document visibility changes.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!room || !isReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newProvider = createProvider(collaborationUrl, room, initialContent);
|
||||
addTask(`${KEY_DOC}-${room}`, () => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC, { id: room }],
|
||||
});
|
||||
});
|
||||
}, [addTask, room, queryClient, isReady]);
|
||||
|
||||
/**
|
||||
* Set the provider when the collaboration URL and the document content are available.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!room || !collaborationUrl || provider || docContent === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newProvider = createProvider(collaborationUrl, room, docContent);
|
||||
setBroadcastProvider(newProvider);
|
||||
}, [
|
||||
provider,
|
||||
collaborationUrl,
|
||||
room,
|
||||
initialContent,
|
||||
createProvider,
|
||||
docContent,
|
||||
room,
|
||||
setBroadcastProvider,
|
||||
]);
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ export interface Doc {
|
||||
title?: string;
|
||||
children?: Doc[];
|
||||
childrenCount?: number;
|
||||
content?: Base64;
|
||||
created_at: string;
|
||||
creator: string;
|
||||
deleted_at: string | null;
|
||||
@@ -82,9 +81,12 @@ export interface Doc {
|
||||
children_list: boolean;
|
||||
collaboration_auth: boolean;
|
||||
comment: boolean;
|
||||
content_patch: boolean;
|
||||
content_retrieve: boolean;
|
||||
destroy: boolean;
|
||||
duplicate: boolean;
|
||||
favorite: boolean;
|
||||
formatted_content: boolean;
|
||||
invite_owner: boolean;
|
||||
link_configuration: boolean;
|
||||
media_auth: boolean;
|
||||
|
||||
+4
-8
@@ -10,12 +10,8 @@ import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { useEditorStore } from '@/docs/doc-editor/stores';
|
||||
import {
|
||||
Doc,
|
||||
base64ToYDoc,
|
||||
useProviderStore,
|
||||
useUpdateDoc,
|
||||
} from '@/docs/doc-management/';
|
||||
import { Doc, base64ToYDoc, useProviderStore } from '@/docs/doc-management/';
|
||||
import { useDocContentUpdate } from '@/docs/doc-management/api/useDocContentUpdate';
|
||||
|
||||
import { useDocVersion } from '../api';
|
||||
import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
|
||||
@@ -49,7 +45,7 @@ export const ModalConfirmationVersion = ({
|
||||
const { toast } = useToastProvider();
|
||||
const { provider } = useProviderStore();
|
||||
const { threadStore } = useEditorStore();
|
||||
const { mutate: updateDoc } = useUpdateDoc({
|
||||
const { mutate: updateDocContent } = useDocContentUpdate({
|
||||
listInvalidQueries: [KEY_LIST_DOC_VERSIONS],
|
||||
onSuccess: () => {
|
||||
const onDisplaySuccess = () => {
|
||||
@@ -104,7 +100,7 @@ export const ModalConfirmationVersion = ({
|
||||
return;
|
||||
}
|
||||
|
||||
updateDoc({
|
||||
updateDocContent({
|
||||
id: docId,
|
||||
content: version.content,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Doc } from '../doc-management/types';
|
||||
|
||||
export interface APIListVersions {
|
||||
count: number;
|
||||
is_truncated: boolean;
|
||||
@@ -15,7 +13,7 @@ export interface Versions {
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
content: Doc['content'];
|
||||
content: string; // Base64 encoded content
|
||||
last_modified: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,6 @@ export class ApiPlugin implements WorkboxPlugin {
|
||||
const newResponse: Doc = {
|
||||
title: '',
|
||||
id: uuid,
|
||||
content: '',
|
||||
created_at: new Date().toISOString(),
|
||||
creator: 'dummy-id',
|
||||
deleted_at: null,
|
||||
@@ -190,9 +189,12 @@ export class ApiPlugin implements WorkboxPlugin {
|
||||
children_list: true,
|
||||
collaboration_auth: true,
|
||||
comment: true,
|
||||
content_patch: true,
|
||||
content_retrieve: true,
|
||||
destroy: true,
|
||||
duplicate: true,
|
||||
favorite: true,
|
||||
formatted_content: true,
|
||||
invite_owner: true,
|
||||
link_configuration: true,
|
||||
media_auth: true,
|
||||
|
||||
@@ -12,10 +12,8 @@ import {
|
||||
Doc,
|
||||
DocPage403,
|
||||
KEY_DOC,
|
||||
useCollaboration,
|
||||
useDoc,
|
||||
useDocStore,
|
||||
useProviderStore,
|
||||
useTrans,
|
||||
} from '@/docs/doc-management/';
|
||||
import { KEY_AUTH, setAuthUrl, useAuth } from '@/features/auth';
|
||||
@@ -24,7 +22,6 @@ import { getDocChildren, subPageToTree } from '@/features/docs/doc-tree/';
|
||||
import { DocEditorSkeleton, useSkeletonStore } from '@/features/skeletons';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
|
||||
import { useBroadcastStore } from '@/stores/useBroadcastStore';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const DocEditor = dynamic(
|
||||
@@ -78,7 +75,6 @@ interface DocProps {
|
||||
}
|
||||
|
||||
const DocPage = ({ id }: DocProps) => {
|
||||
const { hasLostConnection, resetLostConnection } = useProviderStore();
|
||||
const { isSkeletonVisible, setIsSkeletonVisible } = useSkeletonStore();
|
||||
const {
|
||||
data: docQuery,
|
||||
@@ -88,7 +84,7 @@ const DocPage = ({ id }: DocProps) => {
|
||||
} = useDoc(
|
||||
{ id },
|
||||
{
|
||||
staleTime: 0,
|
||||
staleTime: 30000, // 30 seconds - We keep the data fresh as it is a highly collaborative page
|
||||
queryKey: [KEY_DOC, { id }],
|
||||
retryDelay: 1000,
|
||||
retry: (failureCount, error) => {
|
||||
@@ -103,10 +99,8 @@ const DocPage = ({ id }: DocProps) => {
|
||||
|
||||
const [doc, setDoc] = useState<Doc>();
|
||||
const { setCurrentDoc } = useDocStore();
|
||||
const { addTask } = useBroadcastStore();
|
||||
const queryClient = useQueryClient();
|
||||
const { replace, asPath } = useRouter();
|
||||
useCollaboration(doc?.id, doc?.content);
|
||||
const { t } = useTranslation();
|
||||
const { authenticated } = useAuth();
|
||||
const { untitledDocument } = useTrans();
|
||||
@@ -144,16 +138,6 @@ const DocPage = ({ id }: DocProps) => {
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
// Invalidate when provider store reports a lost connection
|
||||
useEffect(() => {
|
||||
if (hasLostConnection && doc?.id) {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC, { id: doc.id }],
|
||||
});
|
||||
resetLostConnection();
|
||||
}
|
||||
}, [hasLostConnection, doc?.id, queryClient, resetLostConnection]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!docQuery || isFetching) {
|
||||
return;
|
||||
@@ -174,22 +158,6 @@ const DocPage = ({ id }: DocProps) => {
|
||||
};
|
||||
}, [setCurrentDoc, setIsSkeletonVisible]);
|
||||
|
||||
/**
|
||||
* We add a broadcast task to reset the query cache
|
||||
* when the document visibility changes.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!doc?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
addTask(`${KEY_DOC}-${doc.id}`, () => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC, { id: doc.id }],
|
||||
});
|
||||
});
|
||||
}, [addTask, doc?.id, queryClient]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isError || !error?.status || [403].includes(error.status)) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user