Skip to content

Commit bbf48f0

Browse files
committed
⚡️(frontend) improve tree stability
Improve tree stability by limiting the requests, we now only load the tree request one time then we let the treeContext handle the state without mutating it directly. We do not do the doc subpage request anymore, the treeContext has already the data we need, we just need to update the tree node when needed.
1 parent b28ff8f commit bbf48f0

File tree

15 files changed

+190
-246
lines changed

15 files changed

+190
-246
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to
1616

1717
- ♻️(frontend) redirect to doc after duplicate #1175
1818
- 🔧(project) change env.d system by using local files #1200
19+
- ⚡️(frontend) improve tree stability #1207
1920

2021
### Fixed
2122

src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable jsx-a11y/click-events-have-key-events */
22
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
3+
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
34
import { Tooltip } from '@openfun/cunningham-react';
4-
import { useQueryClient } from '@tanstack/react-query';
55
import React, { useCallback, useEffect, useState } from 'react';
66
import { useTranslation } from 'react-i18next';
77
import { css } from 'styled-components';
@@ -12,7 +12,6 @@ import {
1212
Doc,
1313
KEY_DOC,
1414
KEY_LIST_DOC,
15-
KEY_SUB_PAGE,
1615
useDocStore,
1716
useTrans,
1817
useUpdateDoc,
@@ -50,10 +49,10 @@ export const DocTitleText = () => {
5049

5150
const DocTitleInput = ({ doc }: DocTitleProps) => {
5251
const { isDesktop } = useResponsiveStore();
53-
const queryClient = useQueryClient();
5452
const { t } = useTranslation();
5553
const { colorsTokens } = useCunninghamTheme();
5654
const [titleDisplay, setTitleDisplay] = useState(doc.title);
55+
const treeContext = useTreeContext<Doc>();
5756

5857
const { untitledDocument } = useTrans();
5958

@@ -64,10 +63,16 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
6463
onSuccess(updatedDoc) {
6564
// Broadcast to every user connected to the document
6665
broadcast(`${KEY_DOC}-${updatedDoc.id}`);
67-
queryClient.setQueryData(
68-
[KEY_SUB_PAGE, { id: updatedDoc.id }],
69-
updatedDoc,
70-
);
66+
67+
if (!treeContext) {
68+
return;
69+
}
70+
71+
if (treeContext.root?.id === updatedDoc.id) {
72+
treeContext?.setRoot(updatedDoc);
73+
} else {
74+
treeContext?.treeData.updateNode(updatedDoc.id, updatedDoc);
75+
}
7176
},
7277
});
7378

src/frontend/apps/impress/src/features/docs/doc-management/api/useDoc.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const getDoc = async ({ id }: DocParams): Promise<Doc> => {
1919
};
2020

2121
export const KEY_DOC = 'doc';
22-
export const KEY_SUB_PAGE = 'sub-page';
2322
export const KEY_DOC_VISIBILITY = 'doc-visibility';
2423

2524
export function useDoc(

src/frontend/apps/impress/src/features/docs/doc-share/components/DocRoleDropdown.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
2-
import { useQueryClient } from '@tanstack/react-query';
32
import { useMemo } from 'react';
43
import { useTranslation } from 'react-i18next';
54
import { css } from 'styled-components';
65

76
import { DropdownMenu, DropdownMenuOption, Text } from '@/components';
8-
import {
9-
Access,
10-
Doc,
11-
KEY_SUB_PAGE,
12-
Role,
13-
useTrans,
14-
} from '@/docs/doc-management/';
7+
import { Access, Doc, Role, useTrans } from '@/docs/doc-management/';
158

169
import { useDeleteDocAccess, useDeleteDocInvitation } from '../api';
1710
import { Invitation, isInvitation } from '../types';
@@ -39,19 +32,9 @@ export const DocRoleDropdown = ({
3932
}: DocRoleDropdownProps) => {
4033
const { t } = useTranslation();
4134
const { transRole, translatedRoles } = useTrans();
42-
const queryClient = useQueryClient();
4335
const { toast } = useToastProvider();
4436

4537
const { mutate: removeDocInvitation } = useDeleteDocInvitation({
46-
onSuccess: () => {
47-
if (!doc) {
48-
return;
49-
}
50-
51-
void queryClient.invalidateQueries({
52-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
53-
});
54-
},
5538
onError: (error) => {
5639
toast(
5740
error?.data?.role?.[0] ?? t('Error during delete invitation'),
@@ -64,14 +47,6 @@ export const DocRoleDropdown = ({
6447
});
6548

6649
const { mutate: removeDocAccess } = useDeleteDocAccess({
67-
onSuccess: () => {
68-
if (!doc) {
69-
return;
70-
}
71-
void queryClient.invalidateQueries({
72-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
73-
});
74-
},
7550
onError: () => {
7651
toast(t('Error while deleting invitation'), VariantType.ERROR, {
7752
duration: 4000,

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberList.tsx

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import {
33
VariantType,
44
useToastProvider,
55
} from '@openfun/cunningham-react';
6-
import { useQueryClient } from '@tanstack/react-query';
76
import { useState } from 'react';
87
import { useTranslation } from 'react-i18next';
98
import { css } from 'styled-components';
109

1110
import { APIError } from '@/api';
1211
import { Box } from '@/components';
1312
import { useCunninghamTheme } from '@/cunningham';
14-
import { Doc, KEY_SUB_PAGE, Role } from '@/docs/doc-management';
13+
import { Doc, Role } from '@/docs/doc-management';
1514
import { User } from '@/features/auth';
1615

1716
import { useCreateDocAccess, useCreateDocInvitation } from '../api';
@@ -45,7 +44,6 @@ export const DocShareAddMemberList = ({
4544
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
4645
const [invitationRole, setInvitationRole] = useState<Role>(Role.EDITOR);
4746
const canShare = doc.abilities.accesses_manage;
48-
const queryClient = useQueryClient();
4947
const { mutateAsync: createInvitation } = useCreateDocInvitation();
5048
const { mutateAsync: createDocAccess } = useCreateDocAccess();
5149

@@ -91,32 +89,14 @@ export const DocShareAddMemberList = ({
9189
};
9290

9391
return isInvitationMode
94-
? createInvitation(
95-
{
96-
...payload,
97-
email: user.email,
98-
},
99-
{
100-
onSuccess: () => {
101-
void queryClient.invalidateQueries({
102-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
103-
});
104-
},
105-
},
106-
)
107-
: createDocAccess(
108-
{
109-
...payload,
110-
memberId: user.id,
111-
},
112-
{
113-
onSuccess: () => {
114-
void queryClient.invalidateQueries({
115-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
116-
});
117-
},
118-
},
119-
);
92+
? createInvitation({
93+
...payload,
94+
email: user.email,
95+
})
96+
: createDocAccess({
97+
...payload,
98+
memberId: user.id,
99+
});
120100
});
121101

122102
const settledPromises = await Promise.allSettled(promises);

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareInvitation.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
2-
import { useQueryClient } from '@tanstack/react-query';
32
import { useMemo } from 'react';
43
import { useTranslation } from 'react-i18next';
54
import { css } from 'styled-components';
@@ -15,7 +14,7 @@ import {
1514
} from '@/components';
1615
import { QuickSearchData, QuickSearchGroup } from '@/components/quick-search';
1716
import { useCunninghamTheme } from '@/cunningham';
18-
import { Doc, KEY_SUB_PAGE, Role } from '@/docs/doc-management';
17+
import { Doc, Role } from '@/docs/doc-management';
1918
import { User } from '@/features/auth';
2019

2120
import {
@@ -38,7 +37,6 @@ export const DocShareInvitationItem = ({
3837
invitation,
3938
}: DocShareInvitationItemProps) => {
4039
const { t } = useTranslation();
41-
const queryClient = useQueryClient();
4240
const { spacingsTokens } = useCunninghamTheme();
4341
const invitedUser: User = {
4442
id: invitation.email,
@@ -52,11 +50,6 @@ export const DocShareInvitationItem = ({
5250
const canUpdate = doc.abilities.accesses_manage;
5351

5452
const { mutate: updateDocInvitation } = useUpdateDocInvitation({
55-
onSuccess: () => {
56-
void queryClient.invalidateQueries({
57-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
58-
});
59-
},
6053
onError: (error) => {
6154
toast(
6255
error?.data?.role?.[0] ?? t('Error during update invitation'),
@@ -69,11 +62,6 @@ export const DocShareInvitationItem = ({
6962
});
7063

7164
const { mutate: removeDocInvitation } = useDeleteDocInvitation({
72-
onSuccess: () => {
73-
void queryClient.invalidateQueries({
74-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
75-
});
76-
},
7765
onError: (error) => {
7866
toast(
7967
error?.data?.role?.[0] ?? t('Error during delete invitation'),

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareMember.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
2-
import { useQueryClient } from '@tanstack/react-query';
32
import { useMemo } from 'react';
43
import { useTranslation } from 'react-i18next';
54

65
import { Box } from '@/components';
76
import { QuickSearchData } from '@/components/quick-search';
87
import { QuickSearchGroup } from '@/components/quick-search/QuickSearchGroup';
98
import { useCunninghamTheme } from '@/cunningham';
10-
import { Access, Doc, KEY_SUB_PAGE, Role } from '@/docs/doc-management/';
9+
import { Access, Doc, Role } from '@/docs/doc-management/';
1110

1211
import { useDocAccesses, useUpdateDocAccess } from '../api';
1312
import { useWhoAmI } from '../hooks/';
@@ -26,7 +25,6 @@ export const DocShareMemberItem = ({
2625
isInherited = false,
2726
}: Props) => {
2827
const { t } = useTranslation();
29-
const queryClient = useQueryClient();
3028
const { isLastOwner } = useWhoAmI(access);
3129
const { toast } = useToastProvider();
3230

@@ -39,14 +37,6 @@ export const DocShareMemberItem = ({
3937
: undefined;
4038

4139
const { mutate: updateDocAccess } = useUpdateDocAccess({
42-
onSuccess: () => {
43-
if (!doc) {
44-
return;
45-
}
46-
void queryClient.invalidateQueries({
47-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
48-
});
49-
},
5040
onError: () => {
5141
toast(t('Error while updating the member role.'), VariantType.ERROR, {
5242
duration: 4000,

src/frontend/apps/impress/src/features/docs/doc-tree/api/useDocTree.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ export type DocsTreeParams = {
99
};
1010

1111
export const getDocTree = async ({ docId }: DocsTreeParams): Promise<Doc> => {
12-
const searchParams = new URLSearchParams();
13-
14-
const response = await fetchAPI(
15-
`documents/${docId}/tree/?${searchParams.toString()}`,
16-
);
12+
const response = await fetchAPI(`documents/${docId}/tree/`);
1713

1814
if (!response.ok) {
1915
throw new APIError(
@@ -29,10 +25,7 @@ export const KEY_DOC_TREE = 'doc-tree';
2925

3026
export function useDocTree(
3127
params: DocsTreeParams,
32-
queryConfig?: Omit<
33-
UseQueryOptions<Doc, APIError, Doc>,
34-
'queryKey' | 'queryFn'
35-
>,
28+
queryConfig?: UseQueryOptions<Doc, APIError, Doc>,
3629
) {
3730
return useQuery<Doc, APIError, Doc>({
3831
queryKey: [KEY_DOC_TREE, params],

src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,12 @@ import {
44
useTreeContext,
55
} from '@gouvfr-lasuite/ui-kit';
66
import { useRouter } from 'next/navigation';
7-
import { useEffect, useRef, useState } from 'react';
7+
import { useState } from 'react';
88
import { css } from 'styled-components';
99

1010
import { Box, Icon, Text } from '@/components';
1111
import { useCunninghamTheme } from '@/cunningham';
12-
import {
13-
Doc,
14-
KEY_SUB_PAGE,
15-
useDoc,
16-
useTrans,
17-
} from '@/features/docs/doc-management';
12+
import { Doc, useTrans } from '@/features/docs/doc-management';
1813
import { useLeftPanelStore } from '@/features/left-panel';
1914
import { useResponsiveStore } from '@/stores';
2015

@@ -31,8 +26,7 @@ const ItemTextCss = css`
3126
-webkit-box-orient: vertical;
3227
`;
3328

34-
type Props = TreeViewNodeProps<Doc>;
35-
export const DocSubPageItem = (props: Props) => {
29+
export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
3630
const doc = props.node.data.value as Doc;
3731
const treeContext = useTreeContext<Doc>();
3832
const { untitledDocument } = useTrans();
@@ -44,28 +38,6 @@ export const DocSubPageItem = (props: Props) => {
4438
const router = useRouter();
4539
const { togglePanel } = useLeftPanelStore();
4640

47-
const isInitialLoad = useRef(false);
48-
const { data: docQuery } = useDoc(
49-
{ id: doc.id },
50-
{
51-
initialData: doc,
52-
queryKey: [KEY_SUB_PAGE, { id: doc.id }],
53-
refetchOnMount: false,
54-
refetchOnWindowFocus: false,
55-
},
56-
);
57-
58-
useEffect(() => {
59-
if (docQuery && isInitialLoad.current === true) {
60-
treeContext?.treeData.updateNode(docQuery.id, docQuery);
61-
}
62-
63-
if (docQuery) {
64-
isInitialLoad.current = true;
65-
}
66-
// eslint-disable-next-line react-hooks/exhaustive-deps
67-
}, [docQuery]);
68-
6941
const afterCreate = (createdDoc: Doc) => {
7042
const actualChildren = node.data.children ?? [];
7143

0 commit comments

Comments
 (0)