Skip to content

Commit 34ce276

Browse files
committed
✨(frontend) subdocs can manage link reach
The subdocs can now have their own link reach properties, dissociated from the parent document.
1 parent 04273c3 commit 34ce276

File tree

8 files changed

+177
-143
lines changed

8 files changed

+177
-143
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to
1111
### Added
1212

1313
- ✨(backend) allow masking documents from the list view #1171
14+
- ✨(frontend) subdocs can manage link reach #1190
1415
- ✨(frontend) add duplicate action to doc tree #1175
1516

1617
### Changed

src/frontend/apps/e2e/__tests__/app-impress/doc-inherited-share.spec.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ test.describe('Inherited share accesses', () => {
3131

3232
await verifyDocName(page, parentTitle);
3333
});
34-
});
3534

36-
test.describe('Inherited share link', () => {
3735
test('it checks if the link is inherited', async ({ page, browserName }) => {
3836
await page.goto('/');
3937
// Create root doc
@@ -47,12 +45,50 @@ test.describe('Inherited share link', () => {
4745
// Create sub page
4846
await createRootSubPage(page, browserName, 'sub-page');
4947

50-
// // verify share link is restricted and reader
48+
// Verify share link is like the parent document
5149
await page.getByRole('button', { name: 'Share' }).click();
52-
// await expect(page.getByText('Inherited share')).toBeVisible();
5350
const docVisibilityCard = page.getByLabel('Doc visibility card');
54-
await expect(docVisibilityCard).toBeVisible();
51+
52+
await expect(docVisibilityCard.getByText('Connected')).toBeVisible();
53+
await expect(docVisibilityCard.getByText('Reading')).toBeVisible();
54+
55+
// Verify inherited link
56+
await docVisibilityCard.getByText('Connected').click();
57+
await expect(
58+
page.getByRole('menuitem', { name: 'Private' }),
59+
).toBeDisabled();
60+
61+
// Update child link
62+
await page.getByRole('menuitem', { name: 'Public' }).click();
63+
64+
await docVisibilityCard.getByText('Reading').click();
65+
await page.getByRole('menuitem', { name: 'Editing' }).click();
66+
67+
await expect(docVisibilityCard.getByText('Connected')).toBeHidden();
68+
await expect(docVisibilityCard.getByText('Reading')).toBeHidden();
69+
await expect(
70+
docVisibilityCard.getByText('Public', {
71+
exact: true,
72+
}),
73+
).toBeVisible();
74+
await expect(docVisibilityCard.getByText('Editing')).toBeVisible();
75+
await expect(
76+
docVisibilityCard.getByText(
77+
'The link sharing rules differ from the parent document',
78+
),
79+
).toBeVisible();
80+
81+
// Restore inherited link
82+
await page.getByRole('button', { name: 'Restore' }).click();
83+
5584
await expect(docVisibilityCard.getByText('Connected')).toBeVisible();
5685
await expect(docVisibilityCard.getByText('Reading')).toBeVisible();
86+
await expect(docVisibilityCard.getByText('Public')).toBeHidden();
87+
await expect(docVisibilityCard.getByText('Editing')).toBeHidden();
88+
await expect(
89+
docVisibilityCard.getByText(
90+
'The link sharing rules differ from the parent document',
91+
),
92+
).toBeHidden();
5793
});
5894
});

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
12
import { useMutation, useQueryClient } from '@tanstack/react-query';
3+
import { useTranslation } from 'react-i18next';
24

35
import { APIError, errorCauses, fetchAPI } from '@/api';
46
import { Doc, KEY_DOC } from '@/docs/doc-management';
@@ -39,6 +41,8 @@ export function useUpdateDocLink({
3941
}: UpdateDocLinkProps = {}) {
4042
const queryClient = useQueryClient();
4143
const { broadcast } = useBroadcastStore();
44+
const { toast } = useToastProvider();
45+
const { t } = useTranslation();
4246

4347
return useMutation<Doc, APIError, UpdateDocLinkParams>({
4448
mutationFn: updateDocLink,
@@ -52,6 +56,14 @@ export function useUpdateDocLink({
5256
// Broadcast to every user connected to the document
5357
broadcast(`${KEY_DOC}-${variable.id}`);
5458

59+
toast(
60+
t('The document visibility has been updated.'),
61+
VariantType.SUCCESS,
62+
{
63+
duration: 2000,
64+
},
65+
);
66+
5567
onSuccess?.(data);
5668
},
5769
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Button } from '@openfun/cunningham-react';
2+
import { useTranslation } from 'react-i18next';
3+
import { css } from 'styled-components';
4+
5+
import { Box, Text } from '@/components';
6+
import { useCunninghamTheme } from '@/cunningham';
7+
import {
8+
Doc,
9+
KEY_DOC,
10+
KEY_LIST_DOC,
11+
useUpdateDocLink,
12+
} from '@/docs/doc-management';
13+
14+
import Desync from './../assets/desynchro.svg';
15+
import Undo from './../assets/undo.svg';
16+
17+
interface DocDesynchronizedProps {
18+
doc: Doc;
19+
}
20+
21+
export const DocDesynchronized = ({ doc }: DocDesynchronizedProps) => {
22+
const { t } = useTranslation();
23+
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
24+
25+
const { mutate: updateDocLink } = useUpdateDocLink({
26+
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
27+
});
28+
29+
return (
30+
<Box
31+
$background={colorsTokens['primary-100']}
32+
$padding="3xs"
33+
$direction="row"
34+
$align="center"
35+
$justify="space-between"
36+
$gap={spacingsTokens['4xs']}
37+
$color={colorsTokens['primary-800']}
38+
$css={css`
39+
border: 1px solid ${colorsTokens['primary-300']};
40+
border-radius: ${spacingsTokens['2xs']};
41+
`}
42+
>
43+
<Box $direction="row" $align="center" $gap={spacingsTokens['3xs']}>
44+
<Desync />
45+
<Text $size="xs" $theme="primary" $variation="800" $weight="400">
46+
{t('The link sharing rules differ from the parent document')}
47+
</Text>
48+
</Box>
49+
{doc.abilities.accesses_manage && (
50+
<Button
51+
onClick={() =>
52+
updateDocLink({
53+
id: doc.id,
54+
link_reach: doc.ancestors_link_reach,
55+
link_role: doc?.ancestors_link_role || undefined,
56+
})
57+
}
58+
size="small"
59+
color="primary-text"
60+
icon={<Undo />}
61+
>
62+
{t('Restore')}
63+
</Button>
64+
)}
65+
</Box>
66+
);
67+
};

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,18 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
5151

5252
const { isDesktop } = useResponsiveStore();
5353

54+
/**
55+
* The modal content height is calculated based on the viewport height.
56+
* The formula is:
57+
* 100dvh - 2em - 12px - 34px
58+
* - 34px is the height of the modal title in mobile
59+
* - 2em is the padding of the modal content
60+
* - 12px is the padding of the modal footer
61+
* - 690px is the height of the content in desktop
62+
* This ensures that the modal content is always visible and does not overflow.
63+
*/
5464
const modalContentHeight = isDesktop
55-
? 'min(690px, calc(100dvh - 2em - 12px - 34px))' // 100dvh - 2em - 12px is the max cunningham modal height. 690px is the height of the content in desktop ad 34px is the height of the modal title in mobile
65+
? 'min(690px, calc(100dvh - 2em - 12px - 34px))'
5666
: `calc(100dvh - 34px)`;
5767
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
5868
const [userQuery, setUserQuery] = useState('');
@@ -230,13 +240,7 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
230240
</Box>
231241

232242
<Box ref={handleRef}>
233-
{showFooter && (
234-
<DocShareModalFooter
235-
doc={doc}
236-
onClose={onClose}
237-
canEditVisibility={canShare}
238-
/>
239-
)}
243+
{showFooter && <DocShareModalFooter doc={doc} onClose={onClose} />}
240244
</Box>
241245
</Box>
242246
</Modal>

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@ import { Doc, useCopyDocLink } from '@/docs/doc-management';
77

88
import { DocVisibility } from './DocVisibility';
99

10-
type Props = {
10+
type DocShareModalFooterProps = {
1111
doc: Doc;
1212
onClose: () => void;
13-
canEditVisibility?: boolean;
1413
};
1514

1615
export const DocShareModalFooter = ({
1716
doc,
1817
onClose,
19-
canEditVisibility = true,
20-
}: Props) => {
18+
}: DocShareModalFooterProps) => {
2119
const copyDocLink = useCopyDocLink(doc.id);
2220
const { t } = useTranslation();
2321
return (
@@ -29,7 +27,7 @@ export const DocShareModalFooter = ({
2927
>
3028
<HorizontalSeparator $withPadding={true} customPadding="12px" />
3129

32-
<DocVisibility doc={doc} canEdit={canEditVisibility} />
30+
<DocVisibility doc={doc} />
3331
<HorizontalSeparator customPadding="12px" />
3432

3533
<Box

0 commit comments

Comments
 (0)