@capytale/meta-player 0.6.15 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capytale/meta-player",
3
- "version": "0.6.15",
3
+ "version": "0.6.16",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -34,6 +34,7 @@ import {
34
34
  import { initialState as layoutInitialState } from "./features/layout/layoutSlice";
35
35
  import ExitWarning from "./features/activityData/ExitWarning";
36
36
  import type { AntiCheatOptions, UIOptions } from "./types";
37
+ import { ToastProvider } from "./features/toast";
37
38
 
38
39
  type MetaPlayerProps = PropsWithChildren<{
39
40
  activityJSOptions?: LoadOptions;
@@ -45,11 +46,14 @@ const MetaPlayer: FC<MetaPlayerProps> = (props) => {
45
46
  const primeSettings: Partial<APIOptions> = {
46
47
  ripple: true,
47
48
  };
48
- const antiCheatOptions: AntiCheatOptions = useMemo(() => ({
49
- preserveDom: true,
50
- hasIframes: false,
51
- ...props.antiCheatOptions,
52
- }), [props.antiCheatOptions]);
49
+ const antiCheatOptions: AntiCheatOptions = useMemo(
50
+ () => ({
51
+ preserveDom: true,
52
+ hasIframes: false,
53
+ ...props.antiCheatOptions,
54
+ }),
55
+ [props.antiCheatOptions],
56
+ );
53
57
  const uiOptions: UIOptions = {
54
58
  closePedagoByDefault: false,
55
59
  noWorkflow: false,
@@ -70,33 +74,35 @@ const MetaPlayer: FC<MetaPlayerProps> = (props) => {
70
74
  );
71
75
  return (
72
76
  <PrimeReactProvider value={primeSettings}>
73
- <Provider store={store}>
74
- <ThemeSwitcher />
75
- <ExitWarning />
76
- <ErrorBoundary fallback={<div>Une erreur est survenue</div>}>
77
- <ActivityJSProvider
78
- options={props.activityJSOptions}
79
- loadingFallback={<div>Chargement de l'activité...</div>}
80
- errorFallback={({ children }) => (
81
- <div style={{ marginLeft: "1rem", marginRight: "1rem" }}>
82
- <p>Erreur lors du chargement de l'activité : {children}</p>
83
- <p>
84
- Êtes-vous bien connecté(e) à Capytale ? Avez-vous bien le
85
- droit d'accéder à cette ressource ?
86
- </p>
87
- <p>
88
- <a href="/">Retour à l'accueil</a>
89
- </p>
90
- </div>
91
- )}
92
- >
93
- <Saver />
94
- <MetaPlayerContent antiCheatOptions={antiCheatOptions}>
95
- <App>{props.children}</App>
96
- </MetaPlayerContent>
97
- </ActivityJSProvider>
98
- </ErrorBoundary>
99
- </Provider>
77
+ <ToastProvider>
78
+ <Provider store={store}>
79
+ <ThemeSwitcher />
80
+ <ExitWarning />
81
+ <ErrorBoundary fallback={<div>Une erreur est survenue</div>}>
82
+ <ActivityJSProvider
83
+ options={props.activityJSOptions}
84
+ loadingFallback={<div>Chargement de l'activité...</div>}
85
+ errorFallback={({ children }) => (
86
+ <div style={{ marginLeft: "1rem", marginRight: "1rem" }}>
87
+ <p>Erreur lors du chargement de l'activité : {children}</p>
88
+ <p>
89
+ Êtes-vous bien connecté(e) à Capytale ? Avez-vous bien le
90
+ droit d'accéder à cette ressource ?
91
+ </p>
92
+ <p>
93
+ <a href="/">Retour à l'accueil</a>
94
+ </p>
95
+ </div>
96
+ )}
97
+ >
98
+ <Saver />
99
+ <MetaPlayerContent antiCheatOptions={antiCheatOptions}>
100
+ <App>{props.children}</App>
101
+ </MetaPlayerContent>
102
+ </ActivityJSProvider>
103
+ </ErrorBoundary>
104
+ </Provider>
105
+ </ToastProvider>
100
106
  </PrimeReactProvider>
101
107
  );
102
108
  };
@@ -1,4 +1,4 @@
1
- import { FC, useEffect, useRef } from "react";
1
+ import { FC, useEffect } from "react";
2
2
  import { useAppDispatch, useAppSelector } from "../../app/hooks";
3
3
  import {
4
4
  selectSharedNotesContent,
@@ -14,8 +14,8 @@ import {
14
14
  selectInstructionsType,
15
15
  } from "../activityData/activityDataSlice";
16
16
  import { useActivityJS } from "./ActivityJSProvider";
17
- import { Toast } from "primereact/toast";
18
17
  import { selectBeforeSave, selectAfterSave } from "./saverSlice";
18
+ import { useToast } from "../toast";
19
19
 
20
20
  const Saver: FC<{}> = () => {
21
21
  const dispatch = useAppDispatch();
@@ -29,7 +29,7 @@ const Saver: FC<{}> = () => {
29
29
  const beforeSave = useAppSelector(selectBeforeSave);
30
30
  const afterSave = useAppSelector(selectAfterSave);
31
31
 
32
- const toast = useRef<Toast>(null);
32
+ const toast = useToast();
33
33
 
34
34
  const executePrepareSave = () => {};
35
35
  const save = async () => {
@@ -45,7 +45,7 @@ const Saver: FC<{}> = () => {
45
45
  } catch (e) {
46
46
  console.error("Error in beforeSave callback", e);
47
47
  dispatch(setSaveState("idle"));
48
- toast.current!.show({
48
+ toast.show({
49
49
  summary: "Erreur : enregistrement annulé",
50
50
  detail: (e as any).toString(),
51
51
  severity: "error",
@@ -105,7 +105,7 @@ const Saver: FC<{}> = () => {
105
105
  }
106
106
  dispatch(setIsPlayerDirty(false));
107
107
  dispatch(setIsMPDirty(false));
108
- toast.current!.show({
108
+ toast.show({
109
109
  summary: "Sauvegarde réussie",
110
110
  detail: "L'activité a bien été enregistrée.",
111
111
  severity: "success",
@@ -120,7 +120,7 @@ const Saver: FC<{}> = () => {
120
120
  } catch (e) {
121
121
  console.error("Error in afterSave callback", e);
122
122
  dispatch(setSaveState("idle"));
123
- toast.current!.show({
123
+ toast.show({
124
124
  summary: "Erreur après sauvegarde",
125
125
  detail: (e as any).toString(),
126
126
  severity: "error",
@@ -131,7 +131,7 @@ const Saver: FC<{}> = () => {
131
131
  } catch (error: any) {
132
132
  console.error("Error saving", error);
133
133
  dispatch(setSaveState("idle"));
134
- toast.current!.show({
134
+ toast.show({
135
135
  summary: "Erreur lors de l'enregistrement",
136
136
  detail: error.toString(),
137
137
  severity: "error",
@@ -148,11 +148,7 @@ const Saver: FC<{}> = () => {
148
148
  save();
149
149
  }
150
150
  }, [saveState]);
151
- return (
152
- <>
153
- <Toast position="bottom-right" ref={toast} />
154
- </>
155
- );
151
+ return null;
156
152
  };
157
153
 
158
154
  export default Saver;
@@ -5,7 +5,7 @@ import { setSettings } from "./activitySettingsSlice";
5
5
  import { deepEqual } from "../../utils/equality";
6
6
 
7
7
  type ActivitySettingsSetterProps = {
8
- settings: ActivitySettings;
8
+ settings: ActivitySettings | null;
9
9
  };
10
10
 
11
11
  const ActivitySettingsSetter: FC<ActivitySettingsSetterProps> = ({
@@ -18,7 +18,7 @@ const ActivitySettingsSetter: FC<ActivitySettingsSetterProps> = ({
18
18
  return;
19
19
  }
20
20
  oldSettings.current = settings;
21
- dispatch(setSettings(settings));
21
+ dispatch(setSettings(settings || undefined));
22
22
  }, [dispatch, settings]);
23
23
  return null;
24
24
  };
@@ -3,7 +3,10 @@ import { useInterval } from "primereact/hooks";
3
3
  import { Toast } from "primereact/toast";
4
4
  import { useActivityJS } from "../../activityJS/ActivityJSProvider";
5
5
  import { useAppSelector } from "../../../app/hooks";
6
- import { selectIsDirty, selectMode } from "../../activityData/activityDataSlice";
6
+ import {
7
+ selectIsDirty,
8
+ selectMode,
9
+ } from "../../activityData/activityDataSlice";
7
10
  import { Button } from "primereact/button";
8
11
  import { useSave } from "../../activityData/hooks";
9
12
  import serverClock from "../../../utils/server-clock";
@@ -17,7 +20,7 @@ const MS_PER_DAY = MS_PER_MINUTE * 60 * 24;
17
20
  const MS_PER_YEAR = MS_PER_DAY * 365;
18
21
 
19
22
  const Countdown: FC = () => {
20
- const toast = useRef<Toast>(null);
23
+ const toast = useRef<Toast>(null); // Using its own toast because it may clear it
21
24
  const mode = useAppSelector(selectMode);
22
25
  const isDirty = useAppSelector(selectIsDirty);
23
26
  const oneMinuteWarningShown = useRef(false);
@@ -1,7 +1,6 @@
1
1
  import { Button } from "primereact/button";
2
2
  import { ButtonGroup } from "primereact/buttongroup";
3
3
  import { ConfirmDialog, confirmDialog } from "primereact/confirmdialog";
4
- import { Toast } from "primereact/toast";
5
4
  import { OverlayPanel } from "primereact/overlaypanel";
6
5
 
7
6
  import styles from "./style.module.scss";
@@ -25,6 +24,7 @@ import { useActivityJS } from "../../activityJS/ActivityJSProvider";
25
24
  import { selectShowWorkflow } from "../../layout/layoutSlice";
26
25
  import CountdownAndSaveButton from "./CountdownAndSaveButton";
27
26
  import CloneDialog from "./CloneDialog";
27
+ import { useToast } from "../../toast";
28
28
 
29
29
  const CapytaleMenu: React.FC = () => {
30
30
  const dispatch = useAppDispatch();
@@ -40,7 +40,7 @@ const CapytaleMenu: React.FC = () => {
40
40
  [windowsSize.width],
41
41
  );
42
42
  const isInIframe = useMemo(() => capytaleUI.isInCapytaletIframe(), []);
43
- const toast = useRef<Toast>(null);
43
+ const toast = useToast();
44
44
  const changeWorkflow = (value: wf) => {
45
45
  activityJS.activitySession!.activityBunch.assignmentNode!.workflow = value;
46
46
  dispatch(setSaveState("should-save"));
@@ -60,7 +60,6 @@ const CapytaleMenu: React.FC = () => {
60
60
  const overlayPanelWorkflow = useRef<OverlayPanel>(null);
61
61
  return (
62
62
  <>
63
- <Toast ref={toast} position="bottom-right" />
64
63
  <div className={styles.capytaleMenu}>
65
64
  {isInIframe && (
66
65
  <Button
@@ -116,7 +115,7 @@ const CapytaleMenu: React.FC = () => {
116
115
  }}
117
116
  onClick={async () => {
118
117
  await copyToClipboard(sharingInfo.code!);
119
- toast.current!.show({
118
+ toast.show({
120
119
  summary: "Code copié",
121
120
  detail:
122
121
  "Le code de partage a été copié dans le presse-papier.",
@@ -138,7 +137,7 @@ const CapytaleMenu: React.FC = () => {
138
137
  }}
139
138
  onClick={() => {
140
139
  copyToClipboard(sharingInfo.codeLink!).then(() => {
141
- toast.current!.show({
140
+ toast.show({
142
141
  summary: "URL copiée",
143
142
  detail:
144
143
  "L'URL de partage a été copiée dans le presse-papier.",
@@ -1,4 +1,4 @@
1
- import { FC, useRef } from "react";
1
+ import { FC } from "react";
2
2
  import {
3
3
  useAttachedFiles,
4
4
  usePreviewImageFile,
@@ -10,13 +10,13 @@ import {
10
10
  selectAttachedFilesOptions,
11
11
  } from "../../functionalities/functionalitiesSlice";
12
12
  import { Button } from "primereact/button";
13
- import { Toast } from "primereact/toast";
14
13
 
15
14
  import styles from "./AttachedFilesSidebarContent.module.scss";
16
15
  import { copyToClipboard } from "../../../utils/clipboard";
17
16
  import { downloadFile } from "../../../utils/download";
18
17
  import { useFileUpload } from "use-file-upload";
19
18
  import mime from "mime";
19
+ import { useToast } from "../../toast";
20
20
 
21
21
  const AttachedFilesSidebarContent: FC = () => {
22
22
  const filesData = useAttachedFiles();
@@ -76,13 +76,12 @@ const AttachedFileLinks: FC<{ fileData: AttachedFileData }> = ({
76
76
  fileData,
77
77
  }) => {
78
78
  const attachedFilesOptions = useAppSelector(selectAttachedFilesOptions);
79
- const toast = useRef<Toast>(null);
79
+ const toast = useToast();
80
80
  const previewTextFile = usePreviewTextFile();
81
81
  const previewImageFile = usePreviewImageFile();
82
82
 
83
83
  return (
84
84
  <div className={styles.fileRow}>
85
- <Toast ref={toast} position="bottom-right" />
86
85
  <Button
87
86
  label={fileData.name}
88
87
  severity={fileData.isTemporary ? "warning" : "secondary"}
@@ -105,7 +104,7 @@ const AttachedFileLinks: FC<{ fileData: AttachedFileData }> = ({
105
104
  } else if (mimeType?.startsWith("image")) {
106
105
  previewImageFile(fileData.name, fileData.urlOrId);
107
106
  } else {
108
- toast.current?.show({
107
+ toast.show({
109
108
  severity: "info",
110
109
  summary: `Impossible de prévisualiser {fileData.name}`,
111
110
  detail: `Aperçu non disponible pour ce type de fichier (${mimeType})`,
@@ -127,7 +126,7 @@ const AttachedFileLinks: FC<{ fileData: AttachedFileData }> = ({
127
126
  return;
128
127
  } else if (fileData.downloadMode === "default") {
129
128
  downloadFile(fileData.urlOrId, fileData.name);
130
- toast.current?.show({
129
+ toast.show({
131
130
  severity: "success",
132
131
  summary: "Téléchargement lancé",
133
132
  detail: fileData.name,
@@ -151,14 +150,14 @@ const AttachedFileLinks: FC<{ fileData: AttachedFileData }> = ({
151
150
  // copy link to clipboard
152
151
  copyToClipboard(fileData.urlOrId, (success, error) => {
153
152
  if (success) {
154
- toast.current?.show({
153
+ toast.show({
155
154
  severity: "success",
156
155
  summary: "Lien copié",
157
156
  detail: fileData.name,
158
157
  life: 3000,
159
158
  });
160
159
  } else {
161
- toast.current?.show({
160
+ toast.show({
162
161
  severity: "error",
163
162
  summary: "Erreur lors de la copie du lien",
164
163
  detail: error,
@@ -10,14 +10,14 @@ import {
10
10
  setLexicalInstructionsState,
11
11
  } from "../activityData/activityDataSlice";
12
12
  import { forwardRef, useImperativeHandle, useMemo, useRef } from "react";
13
- import { Toast } from "primereact/toast";
14
13
  import settings from "../../settings";
15
14
  import { Button } from "primereact/button";
15
+ import { useToast } from "../toast";
16
16
 
17
17
  const InstructionsEditor: React.FC = forwardRef((_props, ref) => {
18
18
  const [Editor, getState, _canSave] = useCapytaleRichTextEditor();
19
19
  const dispatch = useAppDispatch();
20
- const toast = useRef<Toast>(null);
20
+ const toast = useToast();
21
21
  const mode = useAppSelector(selectMode);
22
22
  const isEditable = mode === "create";
23
23
  const initialInstructions = useAppSelector(selectInstructions);
@@ -47,7 +47,6 @@ const InstructionsEditor: React.FC = forwardRef((_props, ref) => {
47
47
 
48
48
  return (
49
49
  <>
50
- <Toast position="bottom-right" ref={toast} />
51
50
  <>
52
51
  {instructionsType === "none" && (
53
52
  <>
@@ -76,7 +75,7 @@ const InstructionsEditor: React.FC = forwardRef((_props, ref) => {
76
75
  }}
77
76
  onJsonSizeLimitExceeded={() => {
78
77
  dispatch(setCanSaveInstructions(false));
79
- toast.current!.show({
78
+ toast.show({
80
79
  summary: "Erreur",
81
80
  detail: `Le contenu de la consigne est trop volumineux. Veuillez le réduire avant de l'enregistrer.\nPeut-être avez-vous inséré une image trop volumineuse ?`,
82
81
  severity: "error",
@@ -85,7 +84,7 @@ const InstructionsEditor: React.FC = forwardRef((_props, ref) => {
85
84
  }}
86
85
  onJsonSizeLimitMet={() => {
87
86
  dispatch(setCanSaveInstructions(true));
88
- toast.current!.show({
87
+ toast.show({
89
88
  summary: "Succès",
90
89
  detail: `Le contenu de la consigne ne dépasse plus la taille limite.`,
91
90
  severity: "success",
@@ -11,15 +11,15 @@ import {
11
11
  setSharedNotesType,
12
12
  } from "../activityData/activityDataSlice";
13
13
  import { forwardRef, useImperativeHandle, useRef, useState } from "react";
14
- import { Toast } from "primereact/toast";
15
14
  import settings from "../../settings";
16
15
  import { Button } from "primereact/button";
17
16
  import { Dialog } from "primereact/dialog";
17
+ import { useToast } from "../toast";
18
18
 
19
19
  const SharedNotesEditor: React.FC = forwardRef((_props, ref) => {
20
20
  const [Editor, getState, _canSave] = useCapytaleRichTextEditor();
21
21
  const dispatch = useAppDispatch();
22
- const toast = useRef<Toast>(null);
22
+ const toast = useToast();
23
23
  const mode = useAppSelector(selectMode);
24
24
  const workflow = useAppSelector(selectWorkflow);
25
25
  const isEditable =
@@ -43,103 +43,100 @@ const SharedNotesEditor: React.FC = forwardRef((_props, ref) => {
43
43
 
44
44
  return (
45
45
  <>
46
- <Toast position="bottom-right" ref={toast} />
47
- <>
48
- {sharedNotesType === "none" && (
49
- <>
50
- <p>Pas de notes partagées.</p>
51
- {mode === "create" && (
52
- <Button
53
- label="Créer des notes partagées"
54
- onClick={() => dispatch(setSharedNotesType("rich"))}
55
- />
56
- )}
57
- <div style={{ textAlign: "start", padding: "16px" }}>
58
- <h3>Que sont les notes partagées ?</h3>
59
- <p>
60
- Les notes partagées permettent aux élèves de communiquer avec
61
- leur enseignant. L'enseignant donne le contenu initial des notes
62
- partagées, et les élèves peuvent le modifier, notamment pour
63
- répondre à des questions, expliciter une démarche ou faire des
64
- remarques sur leur travail. L'enseignant peut également modifier
65
- les notes partagées pour répondre aux élèves.
66
- </p>
67
- <p>
68
- Les notes partagées peuvent contenir du texte, des images, des
69
- liens, etc., tout comme les consignes. La différence principale
70
- avec les consignes est que les élèves peuvent modifier le
71
- contenu des notes partagées. Elles sont un outil de
72
- communication supplémentaire entre l'enseignant et les élèves.
73
- </p>
74
- </div>
46
+ {sharedNotesType === "none" && (
47
+ <>
48
+ <p>Pas de notes partagées.</p>
49
+ {mode === "create" && (
75
50
  <Button
76
- severity="secondary"
77
- size="small"
78
- icon={"pi pi-question-circle"}
79
- onClick={() => {
80
- setHelpDialogVisible(true);
81
- }}
82
- outlined
83
- label="En savoir plus"
84
- style={{ marginBottom: "16px" }}
51
+ label="Créer des notes partagées"
52
+ onClick={() => dispatch(setSharedNotesType("rich"))}
85
53
  />
86
- <Dialog
87
- id="metaPlayerHelpDialog"
88
- header="Documentation"
89
- visible={helpDialogVisible}
90
- onHide={() => setHelpDialogVisible(false)}
91
- maximizable={true}
92
- >
93
- <iframe
94
- src={
95
- "https://capytale2.ac-paris.fr/wiki/doku.php?id=notes_partagees"
96
- }
97
- style={{ width: "100%", height: "100%" }}
98
- title="Documentation"
99
- />
100
- </Dialog>
101
- </>
102
- )}
103
- {sharedNotesType === "rich" && (
104
- <Editor
105
- placeholderText={
106
- mode === "create"
107
- ? "Écrivez le contenu initial des notes partagées ici..."
108
- : "Les notes partagées sont vides, vous pouvez les remplir ici..."
109
- }
110
- htmlInitialContent={initialEditorState ? undefined : ""}
111
- initialEditorState={initialEditorState || undefined}
112
- jsonSizeLimit={settings.STATEMENT_MAX_SIZE}
113
- onChange={(editorState) => {
114
- dispatch(setLexicalSharedNotesState(editorState));
115
- if (initialStateOnChangeDone.current) {
116
- dispatch(setIsMPDirty(true));
117
- } else {
118
- initialStateOnChangeDone.current = true;
119
- }
120
- }}
121
- onJsonSizeLimitExceeded={() => {
122
- dispatch(setCanSaveSharedNotes(false));
123
- toast.current!.show({
124
- summary: "Erreur",
125
- detail: `Le contenu des notes partagées est trop volumineux. Veuillez le réduire avant de l'enregistrer.\nPeut-être avez-vous inséré une image trop volumineuse ?`,
126
- severity: "error",
127
- life: 10000,
128
- });
129
- }}
130
- onJsonSizeLimitMet={() => {
131
- dispatch(setCanSaveSharedNotes(true));
132
- toast.current!.show({
133
- summary: "Succès",
134
- detail: `Le contenu des notes partagées ne dépasse plus la taille limite.`,
135
- severity: "success",
136
- life: 4000,
137
- });
54
+ )}
55
+ <div style={{ textAlign: "start", padding: "16px" }}>
56
+ <h3>Que sont les notes partagées ?</h3>
57
+ <p>
58
+ Les notes partagées permettent aux élèves de communiquer avec leur
59
+ enseignant. L'enseignant donne le contenu initial des notes
60
+ partagées, et les élèves peuvent le modifier, notamment pour
61
+ répondre à des questions, expliciter une démarche ou faire des
62
+ remarques sur leur travail. L'enseignant peut également modifier
63
+ les notes partagées pour répondre aux élèves.
64
+ </p>
65
+ <p>
66
+ Les notes partagées peuvent contenir du texte, des images, des
67
+ liens, etc., tout comme les consignes. La différence principale
68
+ avec les consignes est que les élèves peuvent modifier le contenu
69
+ des notes partagées. Elles sont un outil de communication
70
+ supplémentaire entre l'enseignant et les élèves.
71
+ </p>
72
+ </div>
73
+ <Button
74
+ severity="secondary"
75
+ size="small"
76
+ icon={"pi pi-question-circle"}
77
+ onClick={() => {
78
+ setHelpDialogVisible(true);
138
79
  }}
139
- isEditable={isEditable}
80
+ outlined
81
+ label="En savoir plus"
82
+ style={{ marginBottom: "16px" }}
140
83
  />
141
- )}
142
- </>
84
+ <Dialog
85
+ id="metaPlayerHelpDialog"
86
+ header="Documentation"
87
+ visible={helpDialogVisible}
88
+ onHide={() => setHelpDialogVisible(false)}
89
+ maximizable={true}
90
+ >
91
+ <iframe
92
+ src={
93
+ "https://capytale2.ac-paris.fr/wiki/doku.php?id=notes_partagees"
94
+ }
95
+ style={{ width: "100%", height: "100%" }}
96
+ title="Documentation"
97
+ />
98
+ </Dialog>
99
+ </>
100
+ )}
101
+ {sharedNotesType === "rich" && (
102
+ <Editor
103
+ placeholderText={
104
+ mode === "create"
105
+ ? "Écrivez le contenu initial des notes partagées ici..."
106
+ : "Les notes partagées sont vides, vous pouvez les remplir ici..."
107
+ }
108
+ htmlInitialContent={initialEditorState ? undefined : ""}
109
+ initialEditorState={initialEditorState || undefined}
110
+ jsonSizeLimit={settings.STATEMENT_MAX_SIZE}
111
+ onChange={(editorState) => {
112
+ dispatch(setLexicalSharedNotesState(editorState));
113
+ if (initialStateOnChangeDone.current) {
114
+ dispatch(setIsMPDirty(true));
115
+ } else {
116
+ initialStateOnChangeDone.current = true;
117
+ }
118
+ }}
119
+ onJsonSizeLimitExceeded={() => {
120
+ dispatch(setCanSaveSharedNotes(false));
121
+ toast.show({
122
+ summary: "Erreur",
123
+ detail: `Le contenu des notes partagées est trop volumineux. Veuillez le réduire avant de l'enregistrer.\nPeut-être avez-vous inséré une image trop volumineuse ?`,
124
+ severity: "error",
125
+ life: 10000,
126
+ });
127
+ }}
128
+ onJsonSizeLimitMet={() => {
129
+ dispatch(setCanSaveSharedNotes(true));
130
+ toast.show({
131
+ summary: "Succès",
132
+ detail: `Le contenu des notes partagées ne dépasse plus la taille limite.`,
133
+ severity: "success",
134
+ life: 4000,
135
+ });
136
+ }}
137
+ isEditable={isEditable}
138
+ />
139
+ )}
143
140
  </>
144
141
  );
145
142
  });
@@ -0,0 +1,38 @@
1
+ import { Toast } from "primereact/toast";
2
+ import {
3
+ createContext,
4
+ FC,
5
+ PropsWithChildren,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+
12
+ const ToastContext = createContext<Toast | null>(null);
13
+
14
+ export const ToastProvider: FC<PropsWithChildren<{}>> = ({ children }) => {
15
+ const toast = useRef<Toast>(null);
16
+ const [loaded, setLoaded] = useState(false);
17
+ useEffect(() => {
18
+ setLoaded(true);
19
+ }, []);
20
+ return (
21
+ <>
22
+ <Toast ref={toast} position="bottom-right" />
23
+ {loaded && (
24
+ <ToastContext.Provider value={toast.current}>
25
+ {children}
26
+ </ToastContext.Provider>
27
+ )}
28
+ </>
29
+ );
30
+ };
31
+
32
+ export const useToast = () => {
33
+ const context = useContext(ToastContext);
34
+ if (!context) {
35
+ throw new Error("useToast must be used within a ToastProvider");
36
+ }
37
+ return context;
38
+ };
package/src/index.ts CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  usePreviewImageFile,
34
34
  } from "./features/functionalities/hooks";
35
35
  import { Toast } from "./external/prime";
36
+ import { useToast } from "./features/toast";
36
37
  import type { ToastMessage } from "./external/prime";
37
38
  import type {
38
39
  AttachedFileData,
@@ -40,6 +41,7 @@ import type {
40
41
  } from "./features/functionalities/functionalitiesSlice";
41
42
 
42
43
  import type { ActivityMode } from "@capytale/activity.js/activity/activitySession";
44
+ import type { ActivitySettings } from "./features/activitySettings/types";
43
45
  import type { Workflow } from "./features/activityData/activityJsData";
44
46
  import type { BinaryDataMode } from "./features/activityJS/ActivityJSProvider";
45
47
 
@@ -70,10 +72,12 @@ export {
70
72
  IsDirtySetter,
71
73
  AttachedFilesFunctionality,
72
74
  useRefreshAttachedFiles,
75
+ useToast,
73
76
  Toast,
74
77
  };
75
78
  export type {
76
79
  ActivityMode,
80
+ ActivitySettings,
77
81
  AttachedFileData,
78
82
  BinaryDataMode,
79
83
  ToastMessage,