diff --git a/.github/workflows/docker-hub.yml b/.github/workflows/docker-hub.yml
index 84b23c7e..5971fcfa 100644
--- a/.github/workflows/docker-hub.yml
+++ b/.github/workflows/docker-hub.yml
@@ -79,7 +79,9 @@ jobs:
context: .
file: ./src/frontend/Dockerfile
target: frontend-production
- build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
+ build-args: |
+ DOCKER_USER=${{ env.DOCKER_USER }}:-1000
+ PUBLISH_AS_MIT=false
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92de297c..d3acb9fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,8 @@ and this project adheres to
## Added
-✨ Add a custom callout block to the editor #892
+- ✨ Add a custom callout block to the editor #892
+- 🚩(frontend) version MIT only #911
## [3.2.1] - 2025-05-06
diff --git a/docker-compose.yml b/docker-compose.yml
index 01d3b06c..e6016191 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -155,8 +155,7 @@ services:
target: frontend-production
args:
API_ORIGIN: "http://localhost:8071"
- Y_PROVIDER_URL: "ws://localhost:4444"
- MEDIA_URL: "http://localhost:8083"
+ PUBLISH_AS_MIT: "false"
SW_DEACTIVATED: "true"
image: impress:frontend-development
ports:
diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile
index 9452501e..6aa3fcbe 100644
--- a/src/frontend/Dockerfile
+++ b/src/frontend/Dockerfile
@@ -39,6 +39,9 @@ ENV NEXT_PUBLIC_API_ORIGIN=${API_ORIGIN}
ARG SW_DEACTIVATED
ENV NEXT_PUBLIC_SW_DEACTIVATED=${SW_DEACTIVATED}
+ARG PUBLISH_AS_MIT
+ENV NEXT_PUBLIC_PUBLISH_AS_MIT=${PUBLISH_AS_MIT}
+
RUN yarn build
# ---- Front-end image ----
diff --git a/src/frontend/apps/impress/.env b/src/frontend/apps/impress/.env
index 3cf0e897..bcf7592f 100644
--- a/src/frontend/apps/impress/.env
+++ b/src/frontend/apps/impress/.env
@@ -1,2 +1,3 @@
NEXT_PUBLIC_API_ORIGIN=
NEXT_PUBLIC_SW_DEACTIVATED=
+NEXT_PUBLIC_PUBLISH_AS_MIT=true
diff --git a/src/frontend/apps/impress/.env.development b/src/frontend/apps/impress/.env.development
index f26afb11..248c7265 100644
--- a/src/frontend/apps/impress/.env.development
+++ b/src/frontend/apps/impress/.env.development
@@ -1,2 +1,3 @@
NEXT_PUBLIC_API_ORIGIN=http://localhost:8071
+NEXT_PUBLIC_PUBLISH_AS_MIT=false
NEXT_PUBLIC_SW_DEACTIVATED=true
diff --git a/src/frontend/apps/impress/src/custom-next.d.ts b/src/frontend/apps/impress/src/custom-next.d.ts
index 2f5af638..261cf96c 100644
--- a/src/frontend/apps/impress/src/custom-next.d.ts
+++ b/src/frontend/apps/impress/src/custom-next.d.ts
@@ -20,6 +20,7 @@ declare module '*.svg?url' {
namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_API_ORIGIN?: string;
+ NEXT_PUBLIC_PUBLISH_AS_MIT?: string;
NEXT_PUBLIC_SW_DEACTIVATED?: string;
}
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBox.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBox.spec.tsx
index 99373361..35faa9a8 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBox.spec.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBox.spec.tsx
@@ -42,6 +42,7 @@ const doc = {
beforeEach(() => {
Analytics.clearAnalytics();
+ process.env.NEXT_PUBLIC_PUBLISH_AS_MIT = 'false';
});
describe('DocToolBox "Copy as HTML" option', () => {
@@ -51,7 +52,9 @@ describe('DocToolBox "Copy as HTML" option', () => {
render(, {
wrapper: AppWrapper,
});
- const optionsButton = screen.getByLabelText('Open the document options');
+ const optionsButton = await screen.findByLabelText(
+ 'Open the document options',
+ );
await userEvent.click(optionsButton);
expect(await screen.findByText('Copy as HTML')).toBeInTheDocument();
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxAGPL.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxAGPL.spec.tsx
new file mode 100644
index 00000000..2624889b
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxAGPL.spec.tsx
@@ -0,0 +1,28 @@
+import { render, screen } from '@testing-library/react';
+import React from 'react';
+
+import { AppWrapper } from '@/tests/utils';
+
+import { DocToolBox } from '../components/DocToolBox';
+
+const doc = {
+ nb_accesses: 1,
+ abilities: {
+ versions_list: true,
+ destroy: true,
+ },
+};
+
+jest.mock('@/features/docs/doc-export/', () => ({
+ ModalExport: () => ModalExport,
+}));
+
+it('DocToolBox dynamic import: loads DocToolBox when NEXT_PUBLIC_PUBLISH_AS_MIT is false', async () => {
+ process.env.NEXT_PUBLIC_PUBLISH_AS_MIT = 'false';
+
+ render(, {
+ wrapper: AppWrapper,
+ });
+
+ expect(await screen.findByText('download')).toBeInTheDocument();
+});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxMIT.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxMIT.spec.tsx
new file mode 100644
index 00000000..b7901f2d
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxMIT.spec.tsx
@@ -0,0 +1,35 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import React from 'react';
+
+import { AppWrapper } from '@/tests/utils';
+
+const doc = {
+ nb_accesses: 1,
+ abilities: {
+ versions_list: true,
+ destroy: true,
+ },
+};
+
+jest.mock('@/features/docs/doc-export/', () => ({
+ ModalExport: () => ModalExport,
+}));
+
+it('DocToolBox dynamic import: loads DocToolBox when NEXT_PUBLIC_PUBLISH_AS_MIT is true', async () => {
+ process.env.NEXT_PUBLIC_PUBLISH_AS_MIT = 'true';
+
+ const { DocToolBox } = await import('../components/DocToolBox');
+
+ render(, {
+ wrapper: AppWrapper,
+ });
+
+ await waitFor(
+ () => {
+ expect(screen.queryByText('download')).not.toBeInTheDocument();
+ },
+ {
+ timeout: 1000,
+ },
+ );
+});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
index 77a1fe50..3cdadfab 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
@@ -1,170 +1,47 @@
-import {
- Button,
- VariantType,
- useModal,
- useToastProvider,
-} from '@openfun/cunningham-react';
+import { Button, useModal } from '@openfun/cunningham-react';
import { useQueryClient } from '@tanstack/react-query';
-import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
-import {
- Box,
- DropdownMenu,
- DropdownMenuOption,
- Icon,
- IconOptions,
-} from '@/components';
+import { Box, Icon } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
-import { useEditorStore } from '@/docs/doc-editor/';
-import { ModalExport } from '@/docs/doc-export/';
-import {
- Doc,
- KEY_DOC,
- KEY_LIST_DOC,
- ModalRemoveDoc,
- useCopyDocLink,
- useCreateFavoriteDoc,
- useDeleteFavoriteDoc,
-} from '@/docs/doc-management';
-import { DocShareModal } from '@/docs/doc-share';
-import {
- KEY_LIST_DOC_VERSIONS,
- ModalSelectVersion,
-} from '@/docs/doc-versioning';
-import { useAnalytics } from '@/libs';
+import { Doc } from '@/docs/doc-management';
+import { KEY_LIST_DOC_VERSIONS } from '@/docs/doc-versioning';
import { useResponsiveStore } from '@/stores';
interface DocToolBoxProps {
doc: Doc;
}
+const DocToolBoxLicence = dynamic(() =>
+ process.env.NEXT_PUBLIC_PUBLISH_AS_MIT === 'false'
+ ? import('./DocToolBoxLicenceAGPL').then((mod) => mod.DocToolBoxLicenceAGPL)
+ : import('./DocToolBoxLicenceMIT').then((mod) => mod.DocToolBoxLicenceMIT),
+);
+
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const { t } = useTranslation();
const hasAccesses = doc.nb_accesses_direct > 1 && doc.abilities.accesses_view;
const queryClient = useQueryClient();
- const { spacingsTokens, colorsTokens } = useCunninghamTheme();
+ const { spacingsTokens } = useCunninghamTheme();
- const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
- const [isModalExportOpen, setIsModalExportOpen] = useState(false);
- const selectHistoryModal = useModal();
+ const modalHistory = useModal();
const modalShare = useModal();
- const { isSmallMobile, isDesktop } = useResponsiveStore();
- const { editor } = useEditorStore();
- const { toast } = useToastProvider();
- const copyDocLink = useCopyDocLink(doc.id);
- const { isFeatureFlagActivated } = useAnalytics();
- const removeFavoriteDoc = useDeleteFavoriteDoc({
- listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
- });
- const makeFavoriteDoc = useCreateFavoriteDoc({
- listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
- });
-
- const options: DropdownMenuOption[] = [
- ...(isSmallMobile
- ? [
- {
- label: t('Share'),
- icon: 'group',
- callback: modalShare.open,
- },
- {
- label: t('Export'),
- icon: 'download',
- callback: () => {
- setIsModalExportOpen(true);
- },
- },
- {
- label: t('Copy link'),
- icon: 'add_link',
- callback: copyDocLink,
- },
- ]
- : []),
- {
- label: doc.is_favorite ? t('Unpin') : t('Pin'),
- icon: 'push_pin',
- callback: () => {
- if (doc.is_favorite) {
- removeFavoriteDoc.mutate({ id: doc.id });
- } else {
- makeFavoriteDoc.mutate({ id: doc.id });
- }
- },
- testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
- },
- {
- label: t('Version history'),
- icon: 'history',
- disabled: !doc.abilities.versions_list,
- callback: () => {
- selectHistoryModal.open();
- },
- show: isDesktop,
- },
-
- {
- label: t('Copy as {{format}}', { format: 'Markdown' }),
- icon: 'content_copy',
- callback: () => {
- void copyCurrentEditorToClipboard('markdown');
- },
- },
- {
- label: t('Copy as {{format}}', { format: 'HTML' }),
- icon: 'content_copy',
- callback: () => {
- void copyCurrentEditorToClipboard('html');
- },
- show: isFeatureFlagActivated('CopyAsHTML'),
- },
- {
- label: t('Delete document'),
- icon: 'delete',
- disabled: !doc.abilities.destroy,
- callback: () => {
- setIsModalRemoveOpen(true);
- },
- },
- ];
-
- const copyCurrentEditorToClipboard = async (
- asFormat: 'html' | 'markdown',
- ) => {
- if (!editor) {
- toast(t('Editor unavailable'), VariantType.ERROR, { duration: 3000 });
- return;
- }
-
- try {
- const editorContentFormatted =
- asFormat === 'html'
- ? await editor.blocksToHTMLLossy()
- : await editor.blocksToMarkdownLossy();
- await navigator.clipboard.writeText(editorContentFormatted);
- toast(t('Copied to clipboard'), VariantType.SUCCESS, { duration: 3000 });
- } catch (error) {
- console.error(error);
- toast(t('Failed to copy to clipboard'), VariantType.ERROR, {
- duration: 3000,
- });
- }
- };
+ const { isSmallMobile } = useResponsiveStore();
useEffect(() => {
- if (selectHistoryModal.isOpen) {
+ if (modalHistory.isOpen) {
return;
}
void queryClient.resetQueries({
queryKey: [KEY_LIST_DOC_VERSIONS],
});
- }, [selectHistoryModal.isOpen, queryClient]);
+ }, [modalHistory.isOpen, queryClient]);
return (
{
>
)}
- {!isSmallMobile && (
-
- }
- onClick={() => {
- setIsModalExportOpen(true);
- }}
- size={isSmallMobile ? 'small' : 'medium'}
- />
- )}
-
-
-
-
-
- {modalShare.isOpen && (
- modalShare.close()} doc={doc} />
- )}
- {isModalExportOpen && (
- setIsModalExportOpen(false)} doc={doc} />
- )}
- {isModalRemoveOpen && (
- setIsModalRemoveOpen(false)} doc={doc} />
- )}
- {selectHistoryModal.isOpen && (
- selectHistoryModal.close()}
+
- )}
+
);
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceAGPL.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceAGPL.tsx
new file mode 100644
index 00000000..c8ded02a
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceAGPL.tsx
@@ -0,0 +1,192 @@
+import { Button, useModal } from '@openfun/cunningham-react';
+import { useQueryClient } from '@tanstack/react-query';
+import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { css } from 'styled-components';
+
+import {
+ DropdownMenu,
+ DropdownMenuOption,
+ Icon,
+ IconOptions,
+} from '@/components';
+import { useCunninghamTheme } from '@/cunningham';
+import { ModalExport } from '@/docs/doc-export/';
+import {
+ Doc,
+ KEY_DOC,
+ KEY_LIST_DOC,
+ ModalRemoveDoc,
+ useCopyDocLink,
+ useCreateFavoriteDoc,
+ useDeleteFavoriteDoc,
+} from '@/docs/doc-management';
+import {
+ KEY_LIST_DOC_VERSIONS,
+ ModalSelectVersion,
+} from '@/docs/doc-versioning';
+import { useAnalytics } from '@/libs';
+import { useResponsiveStore } from '@/stores';
+
+import { DocShareModal } from '../../doc-share';
+import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard';
+
+type ModalType = ReturnType;
+
+interface DocToolBoxLicenceProps {
+ doc: Doc;
+ modalHistory: ModalType;
+ modalShare: ModalType;
+}
+
+export const DocToolBoxLicenceAGPL = ({
+ doc,
+ modalHistory,
+ modalShare,
+}: DocToolBoxLicenceProps) => {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+
+ const { colorsTokens } = useCunninghamTheme();
+
+ const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
+ const [isModalExportOpen, setIsModalExportOpen] = useState(false);
+
+ const { isSmallMobile, isDesktop } = useResponsiveStore();
+ const copyDocLink = useCopyDocLink(doc.id);
+ const { isFeatureFlagActivated } = useAnalytics();
+ const removeFavoriteDoc = useDeleteFavoriteDoc({
+ listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
+ });
+ const makeFavoriteDoc = useCreateFavoriteDoc({
+ listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
+ });
+ const copyCurrentEditorToClipboard = useCopyCurrentEditorToClipboard();
+
+ const options: DropdownMenuOption[] = [
+ ...(isSmallMobile
+ ? [
+ {
+ label: t('Share'),
+ icon: 'group',
+ callback: modalShare.open,
+ },
+ {
+ label: t('Export'),
+ icon: 'download',
+ callback: () => {
+ setIsModalExportOpen(true);
+ },
+ },
+ {
+ label: t('Copy link'),
+ icon: 'add_link',
+ callback: copyDocLink,
+ },
+ ]
+ : []),
+ {
+ label: doc.is_favorite ? t('Unpin') : t('Pin'),
+ icon: 'push_pin',
+ callback: () => {
+ if (doc.is_favorite) {
+ removeFavoriteDoc.mutate({ id: doc.id });
+ } else {
+ makeFavoriteDoc.mutate({ id: doc.id });
+ }
+ },
+ testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
+ },
+ {
+ label: t('Version history'),
+ icon: 'history',
+ disabled: !doc.abilities.versions_list,
+ callback: () => {
+ modalHistory.open();
+ },
+ show: isDesktop,
+ },
+
+ {
+ label: t('Copy as {{format}}', { format: 'Markdown' }),
+ icon: 'content_copy',
+ callback: () => {
+ void copyCurrentEditorToClipboard('markdown');
+ },
+ },
+ {
+ label: t('Copy as {{format}}', { format: 'HTML' }),
+ icon: 'content_copy',
+ callback: () => {
+ void copyCurrentEditorToClipboard('html');
+ },
+ show: isFeatureFlagActivated('CopyAsHTML'),
+ },
+ {
+ label: t('Delete document'),
+ icon: 'delete',
+ disabled: !doc.abilities.destroy,
+ callback: () => {
+ setIsModalRemoveOpen(true);
+ },
+ },
+ ];
+
+ useEffect(() => {
+ if (modalHistory.isOpen) {
+ return;
+ }
+
+ void queryClient.resetQueries({
+ queryKey: [KEY_LIST_DOC_VERSIONS],
+ });
+ }, [modalHistory.isOpen, queryClient]);
+
+ return (
+ <>
+ {!isSmallMobile && (
+ }
+ onClick={() => {
+ setIsModalExportOpen(true);
+ }}
+ size={isSmallMobile ? 'small' : 'medium'}
+ />
+ )}
+
+
+
+
+ {modalShare.isOpen && (
+ modalShare.close()} doc={doc} />
+ )}
+ {isModalExportOpen && (
+ setIsModalExportOpen(false)} doc={doc} />
+ )}
+ {isModalRemoveOpen && (
+ setIsModalRemoveOpen(false)} doc={doc} />
+ )}
+ {modalHistory.isOpen && (
+ modalHistory.close()} doc={doc} />
+ )}
+ >
+ );
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceMIT.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceMIT.tsx
new file mode 100644
index 00000000..5159cb42
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBoxLicenceMIT.tsx
@@ -0,0 +1,165 @@
+import { useModal } from '@openfun/cunningham-react';
+import { useQueryClient } from '@tanstack/react-query';
+import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { css } from 'styled-components';
+
+import { DropdownMenu, DropdownMenuOption, IconOptions } from '@/components';
+import { useCunninghamTheme } from '@/cunningham';
+import {
+ Doc,
+ KEY_DOC,
+ KEY_LIST_DOC,
+ ModalRemoveDoc,
+ useCopyDocLink,
+ useCreateFavoriteDoc,
+ useDeleteFavoriteDoc,
+} from '@/docs/doc-management';
+import {
+ KEY_LIST_DOC_VERSIONS,
+ ModalSelectVersion,
+} from '@/docs/doc-versioning';
+import { useAnalytics } from '@/libs';
+import { useResponsiveStore } from '@/stores';
+
+import { DocShareModal } from '../../doc-share';
+import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard';
+
+type ModalType = ReturnType;
+
+interface DocToolBoxLicenceProps {
+ doc: Doc;
+ modalHistory: ModalType;
+ modalShare: ModalType;
+}
+
+export const DocToolBoxLicenceMIT = ({
+ doc,
+ modalHistory,
+ modalShare,
+}: DocToolBoxLicenceProps) => {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+
+ const { colorsTokens } = useCunninghamTheme();
+
+ const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
+
+ const { isSmallMobile, isDesktop } = useResponsiveStore();
+ const copyDocLink = useCopyDocLink(doc.id);
+ const { isFeatureFlagActivated } = useAnalytics();
+ const removeFavoriteDoc = useDeleteFavoriteDoc({
+ listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
+ });
+ const makeFavoriteDoc = useCreateFavoriteDoc({
+ listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
+ });
+ const copyCurrentEditorToClipboard = useCopyCurrentEditorToClipboard();
+
+ const options: DropdownMenuOption[] = [
+ ...(isSmallMobile
+ ? [
+ {
+ label: t('Share'),
+ icon: 'group',
+ callback: modalShare.open,
+ },
+ {
+ label: t('Copy link'),
+ icon: 'add_link',
+ callback: copyDocLink,
+ },
+ ]
+ : []),
+ {
+ label: doc.is_favorite ? t('Unpin') : t('Pin'),
+ icon: 'push_pin',
+ callback: () => {
+ if (doc.is_favorite) {
+ removeFavoriteDoc.mutate({ id: doc.id });
+ } else {
+ makeFavoriteDoc.mutate({ id: doc.id });
+ }
+ },
+ testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
+ },
+ {
+ label: t('Version history'),
+ icon: 'history',
+ disabled: !doc.abilities.versions_list,
+ callback: () => {
+ modalHistory.open();
+ },
+ show: isDesktop,
+ },
+
+ {
+ label: t('Copy as {{format}}', { format: 'Markdown' }),
+ icon: 'content_copy',
+ callback: () => {
+ void copyCurrentEditorToClipboard('markdown');
+ },
+ },
+ {
+ label: t('Copy as {{format}}', { format: 'HTML' }),
+ icon: 'content_copy',
+ callback: () => {
+ void copyCurrentEditorToClipboard('html');
+ },
+ show: isFeatureFlagActivated('CopyAsHTML'),
+ },
+ {
+ label: t('Delete document'),
+ icon: 'delete',
+ disabled: !doc.abilities.destroy,
+ callback: () => {
+ setIsModalRemoveOpen(true);
+ },
+ },
+ ];
+
+ useEffect(() => {
+ if (modalHistory.isOpen) {
+ return;
+ }
+
+ void queryClient.resetQueries({
+ queryKey: [KEY_LIST_DOC_VERSIONS],
+ });
+ }, [modalHistory.isOpen, queryClient]);
+
+ return (
+ <>
+
+
+
+
+ {modalShare.isOpen && (
+ modalShare.close()} doc={doc} />
+ )}
+ {isModalRemoveOpen && (
+ setIsModalRemoveOpen(false)} doc={doc} />
+ )}
+ {modalHistory.isOpen && (
+ modalHistory.close()} doc={doc} />
+ )}
+ >
+ );
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx
new file mode 100644
index 00000000..8402fffe
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx
@@ -0,0 +1,31 @@
+import { VariantType, useToastProvider } from '@openfun/cunningham-react';
+import { useTranslation } from 'react-i18next';
+
+import { useEditorStore } from '../../doc-editor';
+
+export const useCopyCurrentEditorToClipboard = () => {
+ const { editor } = useEditorStore();
+ const { toast } = useToastProvider();
+ const { t } = useTranslation();
+
+ return async (asFormat: 'html' | 'markdown') => {
+ if (!editor) {
+ toast(t('Editor unavailable'), VariantType.ERROR, { duration: 3000 });
+ return;
+ }
+
+ try {
+ const editorContentFormatted =
+ asFormat === 'html'
+ ? await editor.blocksToHTMLLossy()
+ : await editor.blocksToMarkdownLossy();
+ await navigator.clipboard.writeText(editorContentFormatted);
+ toast(t('Copied to clipboard'), VariantType.SUCCESS, { duration: 3000 });
+ } catch (error) {
+ console.error(error);
+ toast(t('Failed to copy to clipboard'), VariantType.ERROR, {
+ duration: 3000,
+ });
+ }
+ };
+};