@capytale/meta-player 0.9.8 → 0.9.10

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.9.8",
3
+ "version": "0.9.10",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -51,7 +51,7 @@
51
51
  "sass": "^1.75.0",
52
52
  "typescript": "^5.7.2",
53
53
  "typescript-eslint": "^8.22.0",
54
- "vite": "^7.3.1"
54
+ "vite": "^7.3.5"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^18.3.1",
@@ -1,5 +1,11 @@
1
- import { FC, ReactNode, useCallback, useMemo, useRef, useState } from "react";
2
- import { useInterval } from "primereact/hooks";
1
+ import {
2
+ FC,
3
+ ReactNode,
4
+ useCallback,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ } from "react";
3
9
  import { Toast } from "primereact/toast";
4
10
  import { useActivityJS } from "../../activityJS/ActivityJSProvider";
5
11
  import { useAppSelector } from "../../../app/hooks";
@@ -9,8 +15,8 @@ import {
9
15
  } from "../../activityData/activityDataSlice";
10
16
  import { Button } from "primereact/button";
11
17
  import { useSave } from "../../activityData/hooks";
12
- import serverClock from "../../../utils/server-clock";
13
18
  import { selectShowSaveForStudents } from "../../layout/layoutSlice";
19
+ import useRemainingTimeMs from "./useRemainingTimeMs";
14
20
 
15
21
  // TODO use https://capytale2.ac-paris.fr/vanilla/time-s.php
16
22
  // https://forge.apps.education.fr/capytale/activity-js/-/blob/main/src/backend/capytale/clock.ts?ref_type=heads
@@ -40,7 +46,9 @@ const Countdown: FC = () => {
40
46
  oneMinuteWarningShown.current = true;
41
47
  toast.current?.show({
42
48
  severity: "warn",
43
- summary: showWarningsBecauseNoSave ? "Temps presque écoulé" : "Activité non sauvegardée",
49
+ summary: showWarningsBecauseNoSave
50
+ ? "Temps presque écoulé"
51
+ : "Activité non sauvegardée",
44
52
  detail: (
45
53
  <>
46
54
  <div>
@@ -89,7 +97,9 @@ const Countdown: FC = () => {
89
97
  fifteenSecondsWarningShown.current = true;
90
98
  toast.current?.show({
91
99
  severity: "error",
92
- summary: showWarningsBecauseNoSave ? "Temps presque écoulé" : "Activité non sauvegardée",
100
+ summary: showWarningsBecauseNoSave
101
+ ? "Temps presque écoulé"
102
+ : "Activité non sauvegardée",
93
103
  detail: (
94
104
  <>
95
105
  <div>
@@ -139,32 +149,25 @@ const Countdown: FC = () => {
139
149
  ? null
140
150
  : activityJS.activitySession?.activityBunch.access_timerange.value?.end;
141
151
 
142
- const [diffMs, setDiffMs] = useState<number | null>(null);
152
+ const diffMs = useRemainingTimeMs();
143
153
 
144
- const updateDiffMs = useCallback(() => {
145
- if (!deadline) {
146
- return;
147
- }
148
- const deadlineUTC = deadline.getTime();
149
- const nowUTC = serverClock.now();
150
- const newDiffMs = deadlineUTC - nowUTC;
151
- setDiffMs(newDiffMs);
152
- if (mode !== "assignment") {
154
+ useEffect(() => {
155
+ if (!diffMs || mode !== "assignment") {
153
156
  return;
154
157
  }
155
158
  if (
156
- newDiffMs != null &&
157
- newDiffMs < MS_PER_MINUTE &&
158
- newDiffMs > 35000 &&
159
+ diffMs != null &&
160
+ diffMs < MS_PER_MINUTE &&
161
+ diffMs > 35000 &&
159
162
  (isDirty || showWarningsBecauseNoSave) &&
160
163
  !oneMinuteWarningShown.current
161
164
  ) {
162
165
  showOneMinuteWarning();
163
166
  }
164
167
  if (
165
- newDiffMs != null &&
166
- newDiffMs < 30000 &&
167
- newDiffMs > 0 &&
168
+ diffMs != null &&
169
+ diffMs < 30000 &&
170
+ diffMs > 0 &&
168
171
  shouldSaveAt30s.current &&
169
172
  !save30sAttempted.current
170
173
  ) {
@@ -176,14 +179,14 @@ const Countdown: FC = () => {
176
179
  }
177
180
  if (
178
181
  (isDirty || showWarningsBecauseNoSave) &&
179
- newDiffMs != null &&
180
- newDiffMs < 15000 &&
181
- newDiffMs > 0 &&
182
+ diffMs != null &&
183
+ diffMs < 15000 &&
184
+ diffMs > 0 &&
182
185
  !fifteenSecondsWarningShown.current
183
186
  ) {
184
187
  showFifteenSecondsWarning();
185
188
  }
186
- if (newDiffMs <= 0 && !zeroSecondsWarningShown.current) {
189
+ if (diffMs <= 0 && !zeroSecondsWarningShown.current) {
187
190
  zeroSecondsWarningShown.current = true;
188
191
  toast.current?.clear();
189
192
  setTimeout(() => {
@@ -195,9 +198,7 @@ const Countdown: FC = () => {
195
198
  });
196
199
  }, 500);
197
200
  }
198
- }, [deadline, mode, isDirty, showWarningsBecauseNoSave]);
199
-
200
- useInterval(updateDiffMs, 1000, !!deadline);
201
+ }, [diffMs, mode, isDirty, showWarningsBecauseNoSave]);
201
202
 
202
203
  const displayed = useMemo<ReactNode>(() => {
203
204
  if (!deadline || diffMs == null) {
@@ -16,6 +16,7 @@ import {
16
16
  import { useWindowSize } from "@uidotdev/usehooks";
17
17
  import { selectShowSaveForStudents } from "../../layout/layoutSlice";
18
18
  import settings from "../../../settings";
19
+ import useRemainingTimeMs from "./useRemainingTimeMs";
19
20
 
20
21
  const CountdownAndSaveButton: FC = () => {
21
22
  const dispatch = useAppDispatch();
@@ -31,12 +32,21 @@ const CountdownAndSaveButton: FC = () => {
31
32
  );
32
33
  const hasEvaluations = useAppSelector(selectHasEvaluations);
33
34
 
35
+ const remainingTimeMs = useRemainingTimeMs();
36
+ const timeOver = useMemo(
37
+ () => remainingTimeMs !== null && remainingTimeMs <= 0,
38
+ [remainingTimeMs],
39
+ );
40
+
34
41
  const mode = useAppSelector(selectMode);
35
42
  const showSaveButton = useAppSelector(selectShowSaveButton);
36
43
  const showSaveForStudents = useAppSelector(selectShowSaveForStudents);
37
44
  const hasSaveButton = useMemo(
38
- () => showSaveButton && !(mode === "assignment" && !showSaveForStudents),
39
- [showSaveButton, mode, showSaveForStudents],
45
+ () =>
46
+ showSaveButton &&
47
+ !(mode === "assignment" && !showSaveForStudents) &&
48
+ !timeOver,
49
+ [showSaveButton, mode, showSaveForStudents, timeOver],
40
50
  );
41
51
 
42
52
  const [editForbiddenDialogVisible, setEditForbiddenDialogVisible] =
@@ -0,0 +1,32 @@
1
+ import { useCallback, useState } from "react";
2
+ import { useActivityJS } from "../../activityJS/ActivityJSProvider";
3
+ import serverClock from "../../../utils/server-clock";
4
+ import { useInterval } from "primereact/hooks";
5
+
6
+ const useRemainingTimeMs = () => {
7
+ const activityJS = useActivityJS();
8
+ const deadlineMode =
9
+ activityJS.activitySession?.activityBunch.access_tr_mode.value;
10
+ const deadline =
11
+ !deadlineMode || deadlineMode === "none"
12
+ ? null
13
+ : activityJS.activitySession?.activityBunch.access_timerange.value?.end;
14
+
15
+ const [diffMs, setDiffMs] = useState<number | null>(null);
16
+
17
+ const updateDiffMs = useCallback(() => {
18
+ if (!deadline) {
19
+ return;
20
+ }
21
+ const deadlineUTC = deadline.getTime();
22
+ const nowUTC = serverClock.now();
23
+ const newDiffMs = deadlineUTC - nowUTC;
24
+ setDiffMs(newDiffMs);
25
+ }, [deadline]);
26
+
27
+ useInterval(updateDiffMs, 1000, !!deadline);
28
+
29
+ return diffMs;
30
+ };
31
+
32
+ export default useRemainingTimeMs;