🛂(frontend) right pad editor
Manage the right on the pad editor. If a use cannot edit a pad, the pad editor will be read-only. It will not save automatically. A message will be displayed to the user.
This commit is contained in:
@@ -168,4 +168,33 @@ test.describe('Pad Editor', () => {
|
|||||||
|
|
||||||
await expect(page.getByText('Hello World Pad persisted 2')).toBeVisible();
|
await expect(page.getByText('Hello World Pad persisted 2')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it cannot edit if not the owner', async ({ page, browserName }) => {
|
||||||
|
const [padName] = await createPad(
|
||||||
|
page,
|
||||||
|
'pad-right-edit',
|
||||||
|
browserName,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByText('My account').click();
|
||||||
|
await page.getByText('Logout').first().click();
|
||||||
|
|
||||||
|
await page.getByLabel('Restart login').click();
|
||||||
|
|
||||||
|
const browserNames = ['chromium', 'webkit'];
|
||||||
|
const newBrowserName = browserNames.find((name) => name !== browserName)!;
|
||||||
|
|
||||||
|
await keyCloakSignIn(page, newBrowserName);
|
||||||
|
|
||||||
|
const panel = page.getByLabel('Pads panel').first();
|
||||||
|
await panel.getByText(padName).click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByText(
|
||||||
|
"Read only, you don't have the right to update this document.",
|
||||||
|
),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ interface BlockNoteContentProps {
|
|||||||
export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
|
export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
|
||||||
const { userData } = useAuthStore();
|
const { userData } = useAuthStore();
|
||||||
const { setEditor, padsStore } = usePadStore();
|
const { setEditor, padsStore } = usePadStore();
|
||||||
useSavePad(pad.id, provider.doc);
|
useSavePad(pad.id, provider.doc, pad.abilities.partial_update);
|
||||||
|
|
||||||
const storedEditor = padsStore?.[pad.id]?.editor;
|
const storedEditor = padsStore?.[pad.id]?.editor;
|
||||||
const editor = useMemo(() => {
|
const editor = useMemo(() => {
|
||||||
@@ -70,7 +70,11 @@ export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
|
|||||||
};
|
};
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<BlockNoteView editor={editor} formattingToolbar={false}>
|
<BlockNoteView
|
||||||
|
editor={editor}
|
||||||
|
formattingToolbar={false}
|
||||||
|
editable={pad.abilities.partial_update}
|
||||||
|
>
|
||||||
<BlockNoteToolbar />
|
<BlockNoteToolbar />
|
||||||
</BlockNoteView>
|
</BlockNoteView>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Alert, VariantType } from '@openfun/cunningham-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Box, Card, Text } from '@/components';
|
import { Box, Card, Text } from '@/components';
|
||||||
@@ -24,6 +25,13 @@ export const PadEditor = ({ pad }: PadEditorProps) => {
|
|||||||
</Text>
|
</Text>
|
||||||
<PadToolBox pad={pad} />
|
<PadToolBox pad={pad} />
|
||||||
</Box>
|
</Box>
|
||||||
|
{!pad.abilities.partial_update && (
|
||||||
|
<Box className="m-b" $css="margin-top:0;">
|
||||||
|
<Alert
|
||||||
|
type={VariantType.WARNING}
|
||||||
|
>{`Read only, you don't have the right to update this document.`}</Alert>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Card
|
<Card
|
||||||
$margin={{ all: 'big', top: 'none' }}
|
$margin={{ all: 'big', top: 'none' }}
|
||||||
$padding="big"
|
$padding="big"
|
||||||
|
|||||||
@@ -6,12 +6,16 @@ import { useUpdatePad } from '@/features/pads/pad-management/';
|
|||||||
|
|
||||||
import { toBase64 } from '../utils';
|
import { toBase64 } from '../utils';
|
||||||
|
|
||||||
const useSavePad = (padId: string, doc: Y.Doc) => {
|
const useSavePad = (padId: string, doc: Y.Doc, canSave: boolean) => {
|
||||||
const { mutate: updatePad } = useUpdatePad();
|
const { mutate: updatePad } = useUpdatePad();
|
||||||
const [initialDoc, setInitialDoc] = useState<string>(
|
const [initialDoc, setInitialDoc] = useState<string>(
|
||||||
toBase64(Y.encodeStateAsUpdate(doc)),
|
toBase64(Y.encodeStateAsUpdate(doc)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialDoc(toBase64(Y.encodeStateAsUpdate(doc)));
|
||||||
|
}, [doc]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update initial doc when doc is updated by other users,
|
* Update initial doc when doc is updated by other users,
|
||||||
* so only the user typing will trigger the save.
|
* so only the user typing will trigger the save.
|
||||||
@@ -42,7 +46,7 @@ const useSavePad = (padId: string, doc: Y.Doc) => {
|
|||||||
/**
|
/**
|
||||||
* Save only if the doc has changed.
|
* Save only if the doc has changed.
|
||||||
*/
|
*/
|
||||||
if (initialDoc === newDoc) {
|
if (initialDoc === newDoc || !canSave) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +56,7 @@ const useSavePad = (padId: string, doc: Y.Doc) => {
|
|||||||
id: padId,
|
id: padId,
|
||||||
content: newDoc,
|
content: newDoc,
|
||||||
});
|
});
|
||||||
}, [initialDoc, padId, doc, updatePad]);
|
}, [initialDoc, padId, doc, updatePad, canSave]);
|
||||||
|
|
||||||
const timeout = useRef<NodeJS.Timeout>();
|
const timeout = useRef<NodeJS.Timeout>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ module.exports = {
|
|||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user