@capytale/meta-player 0.6.14 → 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 +1 -1
- package/src/MetaPlayer.tsx +38 -32
- package/src/features/activityJS/Saver.tsx +8 -12
- package/src/features/activitySettings/ActivitySettingsSetter.tsx +2 -2
- package/src/features/functionalities/PreviewDialog.tsx +8 -1
- package/src/features/navbar/capytale-menu/Countdown.tsx +5 -2
- package/src/features/navbar/capytale-menu/index.tsx +4 -5
- package/src/features/navbar/sidebars/AttachedFilesSidebarContent.tsx +7 -8
- package/src/features/pedago/InstructionsEditor.tsx +4 -5
- package/src/features/pedago/SharedNotesEditor.tsx +92 -95
- package/src/features/toast.tsx +38 -0
- package/src/index.ts +4 -0
package/package.json
CHANGED
package/src/MetaPlayer.tsx
CHANGED
|
@@ -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 =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
<
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
setPreviewFile,
|
|
7
7
|
} from "./functionalitiesSlice";
|
|
8
8
|
import { Dialog } from "primereact/dialog";
|
|
9
|
+
import { useThemeType } from "../theming/hooks";
|
|
9
10
|
|
|
10
11
|
const HighlightedCode = memo(({ code }: { code: string }) => {
|
|
11
12
|
const codeRef = useRef<HTMLDivElement>(null);
|
|
@@ -19,11 +20,17 @@ const HighlightedCode = memo(({ code }: { code: string }) => {
|
|
|
19
20
|
}
|
|
20
21
|
}, [code]);
|
|
21
22
|
|
|
23
|
+
const theme = useThemeType();
|
|
24
|
+
|
|
22
25
|
return (
|
|
23
26
|
<pre>
|
|
24
27
|
<code
|
|
25
28
|
ref={codeRef}
|
|
26
|
-
style={{
|
|
29
|
+
style={{
|
|
30
|
+
padding: 0,
|
|
31
|
+
background: theme === "light" ? "white" : "#1f2937",
|
|
32
|
+
maxWidth: "100%",
|
|
33
|
+
}}
|
|
27
34
|
>
|
|
28
35
|
{code}
|
|
29
36
|
</code>
|
|
@@ -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 {
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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,
|