@nualang/nualang-ui-components 0.1.1413 → 0.1.1415
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/dist/Assignments/AssignmentCardsList/AssignmentCardsList.js +4 -2
- package/dist/Cards/Course/Course.js +145 -13
- package/dist/Chat/Messages/Message/Message.js +4 -1
- package/dist/Chat/Messages/Message/Message.test.js +55 -0
- package/dist/Containers/App/App.js +10 -6
- package/dist/Dialogs/CreatePhrase/CreatePhrase.js +3 -1
- package/dist/Dialogs/CreatePhraseGroupDialog/CreatePhraseGroupDialog.js +4 -2
- package/dist/Dialogs/GenerateRoleplay/GenerateRoleplay.js +5 -2
- package/dist/Dialogs/ImageBuilder/ImageBuilderDialog.js +3 -14
- package/dist/Dialogs/ImageBuilder/imageBuilderUtils.js +47 -5
- package/dist/Dialogs/QuickCreateChatbotDialog/QuickCreateChatbotDialog.js +2 -1
- package/dist/Dialogs/QuickCreateClassroomDialog/QuickCreateClassroomDialog.js +4 -2
- package/dist/Dialogs/QuickCreateCourseDialog/QuickCreateCourseDialog.js +4 -2
- package/dist/Dialogs/QuickCreateTopicDialog/QuickCreateTopicDialog.js +4 -2
- package/dist/Editors/Bot/Bot.js +10 -6
- package/dist/Editors/Bot/Editor/BotDesigner/AddImageModal/AddImageModal.js +4 -2
- package/dist/Editors/Bot/Editor/BotDesigner/BotDesigner.js +4 -2
- package/dist/Editors/Bot/Editor/BotDesigner/VarList.js +7 -3
- package/dist/Editors/Bot/Editor/Editor.js +6 -3
- package/dist/Editors/PhraseList/PhraseList.js +6 -3
- package/dist/Editors/Phrases/Phrases.js +4 -2
- package/dist/Editors/Roleplay/Roleplay.js +9 -4
- package/dist/Exercises/ListenAndSpeak/ListenAndSpeak.js +1 -1
- package/dist/Exercises/Listener/Listener.js +1 -1
- package/dist/Exercises/Meaning/Meaning.js +1 -1
- package/dist/Exercises/MeaningReadWrite/MeaningReadWriteGame.js +4 -1
- package/dist/Exercises/Pronouncer/Pronouncer.js +1 -1
- package/dist/Exercises/Roleplay/Roleplay.js +5 -2
- package/dist/Exercises/Translator/Translator.js +1 -1
- package/dist/Forms/CreateBot/Steps/BotInformation/BotInformation.js +17 -4
- package/dist/Forms/CreateClassroom/Steps/Customization/Customization.js +6 -3
- package/dist/Forms/CreateCourse/Steps/Customization/Customization.js +6 -3
- package/dist/Forms/CreateMeetingMultiStepForm/CreateMeetingMultiStepForm.js +6 -3
- package/dist/Forms/CreateMeetingMultiStepForm/MeetingForm.js +6 -3
- package/dist/Forms/CreateRoleplay/Steps/RoleplayInformation/RoleplayInformation.js +8 -4
- package/dist/Forms/CreateTopic/Steps/Customization/Customization.js +6 -3
- package/dist/Forms/ImageSelector/ImageSelector.js +39 -9
- package/dist/Forms/UpdateBot/UpdateBot.js +3 -1
- package/dist/Forms/UpdateClassroom/UpdateClassroom.js +3 -1
- package/dist/Forms/UpdateCourse/UpdateCourse.js +3 -1
- package/dist/Forms/UpdatePhraseList/UpdatePhraseList.js +9 -5
- package/dist/Forms/UpdateRoleplay/UpdateRoleplay.js +4 -2
- package/dist/Forms/UpdateTopic/UpdateTopic.js +3 -1
- package/dist/Forms/VoiceSelector/VoiceSelector.js +29 -14
- package/dist/Forms/VoiceSelector/VoiceSelector.test.js +125 -3
- package/dist/Lists/CourseOutline/CourseOutline.js +3 -1
- package/dist/Lists/Courses/Courses.js +5 -2
- package/dist/Lists/Courses/Courses.test.js +10 -0
- package/dist/Lists/Exercises/Exercises.js +2 -1
- package/dist/Lists/Phrases/Phrases.js +4 -2
- package/dist/Lists/Roleplays/Roleplays.js +5 -2
- package/dist/Live/LiveListener/LiveListener.js +1 -1
- package/dist/Live/LiveMeaning/LiveMeaning.js +1 -1
- package/dist/Live/LiveMeaningReadWrite/LiveMeaningReadWrite.js +1 -1
- package/dist/Live/LiveTranslator/LiveTranslator.js +1 -1
- package/dist/Misc/ChatBubble/ChatBubble.js +2 -0
- package/dist/Misc/HoverText/HoverText.js +7 -2
- package/dist/Navigation/ResponsiveTabs/ResponsiveTabs.js +1 -1
- package/dist/Tables/MeetingPrompstList/MeetingPromptsList.js +4 -2
- package/dist/hooks/useExerciseState.js +2 -0
- package/dist/index.js +1 -1
- package/dist/utils/canvasUtils.js +1 -1
- package/dist/utils/voiceImages.js +19 -4
- package/dist/utils/voices.js +12 -2
- package/package.json +1 -1
- package/dist/Cards/SubmissionsCard/SubmissionsCard.js +0 -138
- package/dist/Cards/index.js +0 -2
- package/dist/Dialogs/Code/Code.js +0 -147
- package/dist/Dialogs/Code/Code.test.js +0 -20
- package/dist/Dialogs/Code/package.json +0 -6
- package/dist/Dialogs/CreateTopic/CreateTopic.js +0 -135
- package/dist/Dialogs/CreateTopic/CreateTopic.test.js +0 -15
- package/dist/Dialogs/CreateTopic/package.json +0 -6
- package/dist/Dialogs/FeedbackDialog/FeedbackDialog.js +0 -123
- package/dist/Dialogs/Icons/Icons.js +0 -18
- package/dist/Dialogs/Icons/package.json +0 -6
- package/dist/Dialogs/InactivePopover/InactivePopover.js +0 -71
- package/dist/Dialogs/InviteDialog/upload-emails-csv.png +0 -0
- package/dist/Dialogs/PDFViewer/pdf24.pdf +0 -0
- package/dist/Dialogs/SearchTopic/SearchTopic.js +0 -87
- package/dist/Dialogs/UploadPhrases/upload-phrases-csv.png +0 -0
- package/dist/Exercises/Roleplay/OriginalRoleplay.js +0 -944
- package/dist/Forms/CreateBot/CreateBot.js +0 -77
- package/dist/Forms/CreateBot/CreateBot.test.js +0 -20
- package/dist/Forms/CreateBot/package.json +0 -6
- package/dist/Forms/CreateClassroom/CreateClassroom.js +0 -56
- package/dist/Forms/CreateClassroom/CreateClassroom.test.js +0 -20
- package/dist/Forms/CreateClassroom/package.json +0 -6
- package/dist/Forms/CreateCourse/CreateCourse.js +0 -66
- package/dist/Forms/CreateCourse/CreateCourse.test.js +0 -20
- package/dist/Forms/CreateCourse/package.json +0 -6
- package/dist/Forms/CreateMeetingMultiStepForm/images/input1.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/input5.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/mode1.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/mode2.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/sampleTemplates.js +0 -44
- package/dist/Forms/CreateRoleplay/CreateRoleplay.js +0 -87
- package/dist/Forms/CreateRoleplay/CreateRoleplay.test.js +0 -20
- package/dist/Forms/CreateRoleplay/package.json +0 -6
- package/dist/Forms/CreateRoleplayHook/CreateRoleplayHook.js +0 -83
- package/dist/Forms/CreateRoleplayHook/CreateRoleplayHook.test.js +0 -20
- package/dist/Forms/CreateRoleplayHook/Steps/Confirmation/Confirmation.js +0 -174
- package/dist/Forms/CreateRoleplayHook/Steps/Confirmation/package.json +0 -6
- package/dist/Forms/CreateRoleplayHook/Steps/RoleplayInformation/RoleplayInformation.js +0 -487
- package/dist/Forms/CreateRoleplayHook/Steps/RoleplayInformation/package.json +0 -6
- package/dist/Forms/CreateRoleplayHook/Steps/RoleplaySettings/RoleplaySettings.js +0 -330
- package/dist/Forms/CreateRoleplayHook/Steps/RoleplaySettings/package.json +0 -6
- package/dist/Forms/CreateRoleplayHook/Steps/index.js +0 -4
- package/dist/Forms/CreateRoleplayHook/package.json +0 -6
- package/dist/Forms/CreateUser/CreateUser.js +0 -77
- package/dist/Forms/FeedbackForm/FeedbackForm.js +0 -1036
- package/dist/Forms/MultiStepFormDialog/MultiStepFormDialog.js +0 -354
- package/dist/Forms/MultiStepFormDialog/MultiStepFormDialog.test.js +0 -20
- package/dist/Forms/MultiStepFormDialog/package.json +0 -6
- package/dist/Forms/MultiStepFormHook/MultiStepFormHook.js +0 -279
- package/dist/Forms/RecordingInfo/RecordingInfo.js +0 -140
- package/dist/Lists/Categories/sample.png +0 -0
- package/dist/Lists/Sections/Sections.js +0 -238
- package/dist/Lists/Sections/Sections.test.js +0 -20
- package/dist/Lists/Sections/package.json +0 -6
- package/dist/Lists/Voices/horse.ogm +0 -0
- package/dist/Misc/AxiosRestExample/AxiosRestExample.js +0 -46
- package/dist/Misc/AxiosRestExample/AxiosRestPostExample.js +0 -65
- package/dist/Misc/ColorSelector/ColorSelector.js +0 -143
- package/dist/Misc/ColorSelector/package.json +0 -6
- package/dist/Misc/EmojiInput/css/emoji-mart.css +0 -497
- package/dist/Misc/MedalSelector/PlaceholderMedal.svg +0 -27
- package/dist/Misc/NotFound/Bot.js +0 -62
- package/dist/Misc/NotFound/Bots.js +0 -75
- package/dist/Misc/NotFound/Discussions.js +0 -52
- package/dist/Misc/NotFound/Roleplay.js +0 -62
- package/dist/Misc/NotFound/Roleplays.js +0 -75
- package/dist/Misc/NotFound/Script.js +0 -47
- package/dist/Misc/Notes/Notes.js +0 -77
- package/dist/Misc/Notes/Notes.test.js +0 -20
- package/dist/Misc/Notes/package.json +0 -6
- package/dist/Misc/NualaCreating/Nuala1.js +0 -352
- package/dist/Misc/OnboardingEnrolment/CheckIfCourse.js +0 -33
- package/dist/Misc/OnboardingEnrolment/ClassCodeInvalid.js +0 -14
- package/dist/Misc/OnboardingEnrolment/ClassroomCodeInvalid.js +0 -14
- package/dist/Misc/OnboardingEnrolment/EnrolKey.js +0 -14
- package/dist/Misc/RoleplayChatWaveForm/RoleplayChatWaveForm.js +0 -199
- package/dist/Misc/RoleplayChatWaveForm/package.json +0 -6
- package/dist/Misc/RoleplayWaveform/RoleplayWaveform.js +0 -95
- package/dist/Misc/RoleplayWaveform/package.json +0 -6
- package/dist/Misc/SpeedDial/SpeedDial.js +0 -49
- package/dist/Misc/SpeedDial/package.json +0 -6
- package/dist/Misc/SubmitTextField/SubmitTextField.js +0 -48
- package/dist/Misc/newGenerateCardBackground/Generate Card Background (standalone) (1).html +0 -186
- package/dist/Navigation/ResponsiveStepper/ResponsiveStepper.js +0 -253
- package/dist/Navigation/ResponsiveStepper/ResponsiveStepper.test.js +0 -20
- package/dist/Navigation/ResponsiveStepper/package.json +0 -6
- package/dist/Navigation/ResponsiveTabs/TabContainer.js +0 -28
- package/dist/Tables/CreatedDiscussionsTable/CreatedDiscussionsTable.js +0 -188
- package/dist/Tables/GameRows/GameRows.js +0 -87
- package/dist/Tables/GameRows/package.json +0 -6
- package/dist/Tables/Games/Games.js +0 -258
- package/dist/Tables/Games/package.json +0 -6
- package/dist/Tables/Phrases/Phrases.js +0 -200
- package/dist/Tables/Phrases/Phrases.test.js +0 -20
- package/dist/Tables/Phrases/package.json +0 -6
- package/dist/Tables/Plans/Plans.js +0 -74
- package/dist/Tables/Plans/plans.json +0 -162
- package/dist/Tables/Plans/plans2.json +0 -151
- package/dist/Tables/Progress/Header.js +0 -559
- package/dist/Tables/Progress/README.md +0 -53
- package/dist/Tables/SubmissionsTableCards/SubmissionsTableCards.js +0 -296
- package/dist/Tables/Users/UsersTable.js +0 -159
- package/dist/img/Bronze Medal.svg +0 -545
- package/dist/img/Bronze.svg +0 -349
- package/dist/img/BronzeBadge.svg +0 -493
- package/dist/img/BronzeBadgeWithoutRibbon.svg +0 -317
- package/dist/img/Frame 51.svg +0 -98
- package/dist/img/Frame 52.svg +0 -131
- package/dist/img/Frame 53.svg +0 -139
- package/dist/img/Gold Badge.svg +0 -362
- package/dist/img/Gold Medal.svg +0 -481
- package/dist/img/GoldBadge.svg +0 -470
- package/dist/img/GoldBadgeWithoutRibbon.svg +0 -324
- package/dist/img/NualaCreateImage.svg +0 -129
- package/dist/img/NualangLiveAnwerQuicklyListening.svg +0 -3608
- package/dist/img/NualangLiveListen.png +0 -0
- package/dist/img/NualangLiveListenQuickly.png +0 -0
- package/dist/img/NualangLiveListenToPhrase.svg +0 -4286
- package/dist/img/NualangLivePickAnswer.svg +0 -134
- package/dist/img/NualangLiveResult.png +0 -0
- package/dist/img/NualangLiveRoleplayAnswer.png +0 -0
- package/dist/img/NualangLiveRoleplayConvo.png +0 -0
- package/dist/img/NualangLiveSeePlace.svg +0 -134
- package/dist/img/NualangLiveTranslate.png +0 -0
- package/dist/img/NualangLiveTranslateQuickly.png +0 -0
- package/dist/img/NualangLiveViewConversations.svg +0 -135
- package/dist/img/Polly.svg +0 -18
- package/dist/img/Silver Badge.svg +0 -523
- package/dist/img/Silver Medal.svg +0 -549
- package/dist/img/SilverBadge.svg +0 -655
- package/dist/img/SilverBadgeWithoutRibbon.svg +0 -491
- package/dist/img/avatars/0-nuala-001.svg +0 -34
- package/dist/img/avatars/0-nuala-002.svg +0 -25
- package/dist/img/avatars/0-nuala-003.svg +0 -27
- package/dist/img/avatars/0-nuala-004.svg +0 -41
- package/dist/img/avatars/0-nuala-005.svg +0 -33
- package/dist/img/avatars/0-nuala-006.svg +0 -32
- package/dist/img/avatars/0-nuala-007.svg +0 -37
- package/dist/img/avatars/0-nuala-008.svg +0 -21
- package/dist/img/avatars/0-nuala-009.svg +0 -21
- package/dist/img/avatars/0-nuala-010.svg +0 -39
- package/dist/img/avatars/0-nuala-011.svg +0 -33
- package/dist/img/avatars/0-nuala-012.svg +0 -43
- package/dist/img/avatars/0-nuala-013.svg +0 -36
- package/dist/img/avatars/0-nuala-014.svg +0 -30
- package/dist/img/avatars/0-nuala-015.svg +0 -28
- package/dist/img/avatars/0-nuala-016.svg +0 -24
- package/dist/img/avatars/0-nuala-017.svg +0 -31
- package/dist/img/avatars/0-nuala-018.svg +0 -29
- package/dist/img/avatars/0-nuala-019.svg +0 -57
- package/dist/img/avatars/0-nuala-020.svg +0 -30
- package/dist/img/avatars/0-nuala-021.svg +0 -28
- package/dist/img/avatars/001-man.svg +0 -104
- package/dist/img/avatars/002-girl.svg +0 -92
- package/dist/img/avatars/006-woman-1.svg +0 -99
- package/dist/img/avatars/028-woman-7.svg +0 -95
- package/dist/img/avatars/029-man-8.svg +0 -95
- package/dist/img/avatars/035-woman-9.svg +0 -86
- package/dist/img/avatars/066-boy-6.svg +0 -91
- package/dist/img/avatars/1-teacher-001.svg +0 -38
- package/dist/img/avatars/1-teacher-002.svg +0 -52
- package/dist/img/avatars/1-teacher-003.svg +0 -86
- package/dist/img/avatars/avatar1.png +0 -0
- package/dist/img/avatars/avatar2.png +0 -0
- package/dist/img/avatars/nuala_10_speaking.svg +0 -45
- package/dist/img/avatars/nuala_11_speaking.svg +0 -41
- package/dist/img/avatars/nuala_12_speaking.svg +0 -49
- package/dist/img/avatars/nuala_13_speaking.svg +0 -44
- package/dist/img/avatars/nuala_14_speaking.svg +0 -36
- package/dist/img/avatars/nuala_15_speaking.svg +0 -36
- package/dist/img/avatars/nuala_16_speaking.svg +0 -32
- package/dist/img/avatars/nuala_17_speaking.svg +0 -39
- package/dist/img/avatars/nuala_18_speaking.svg +0 -37
- package/dist/img/avatars/nuala_19_speaking.svg +0 -65
- package/dist/img/avatars/nuala_1_celebrating.svg +0 -30
- package/dist/img/avatars/nuala_1_speaking.svg +0 -37
- package/dist/img/avatars/nuala_20_speaking.svg +0 -38
- package/dist/img/avatars/nuala_21_speaking.svg +0 -36
- package/dist/img/avatars/nuala_2_speaking.svg +0 -29
- package/dist/img/avatars/nuala_3_speaking.svg +0 -28
- package/dist/img/avatars/nuala_4_speaking.svg +0 -41
- package/dist/img/avatars/nuala_5_speaking.svg +0 -36
- package/dist/img/avatars/nuala_6_speaking.svg +0 -36
- package/dist/img/avatars/nuala_7_speaking.svg +0 -43
- package/dist/img/avatars/nuala_8_speaking.svg +0 -29
- package/dist/img/avatars/nuala_9_speaking.svg +0 -27
- package/dist/img/dashboard.svg +0 -104
- package/dist/img/defaultImg.png +0 -0
- package/dist/img/nua-glyph.svg +0 -6
- package/dist/img/nuala-silhouette.svg +0 -43
- package/dist/img/nuala_1_celebrating.svg +0 -33
- package/dist/img/nuala_2_celebrating.svg +0 -20
- package/dist/img/nuala_3_celebrating.svg +0 -21
- package/dist/img/nuala_4_celebrating.svg +0 -30
- package/dist/img/nuala_5_celebrating.svg +0 -30
- package/dist/img/nuala_celebrating.svg +0 -39
- package/dist/img/nuala_speaking_4.svg +0 -41
- package/dist/img/nualas.svg +0 -185
- package/dist/img/soundWave.png +0 -0
- package/dist/img/stars1.svg +0 -44
- package/dist/img/stars2.svg +0 -30
- package/dist/img/teacher.svg +0 -89
- package/dist/img/work-chat.svg +0 -1
- package/dist/img/world.svg +0 -41
|
@@ -30,7 +30,8 @@ const AssignmentCardsList = ({
|
|
|
30
30
|
isLoadingAssignments,
|
|
31
31
|
isChallengeModeStudent,
|
|
32
32
|
isReadWriteModeStudent,
|
|
33
|
-
scrollableTarget = null
|
|
33
|
+
scrollableTarget = null,
|
|
34
|
+
openSnackbar
|
|
34
35
|
}) => {
|
|
35
36
|
const [lastClickedExerciseId, setLastClickedExerciseId] = useState(null);
|
|
36
37
|
const [filter, setFilter] = useState("all");
|
|
@@ -355,7 +356,8 @@ const AssignmentCardsList = ({
|
|
|
355
356
|
handleViewProgress: handleViewProgress,
|
|
356
357
|
assignedStudents: assignmentMembersById[assignment.assignmentId] || [],
|
|
357
358
|
isChallengeModeStudent: isChallengeModeStudent,
|
|
358
|
-
isReadWriteModeStudent: isReadWriteModeStudent
|
|
359
|
+
isReadWriteModeStudent: isReadWriteModeStudent,
|
|
360
|
+
openSnackbar: openSnackbar
|
|
359
361
|
}, assignment.assignmentId))
|
|
360
362
|
})]
|
|
361
363
|
});
|
|
@@ -2,8 +2,10 @@ import { useState, memo } from "react";
|
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { useInView } from "react-intersection-observer";
|
|
4
4
|
// Mui
|
|
5
|
-
import { Button, Box, Typography, IconButton, Menu, MenuItem, Checkbox, Tooltip, CardActions, CardActionArea, Divider } from "@mui/material";
|
|
5
|
+
import { Button, Box, Typography, IconButton, Menu, MenuItem, Checkbox, Tooltip, CardActions, CardActionArea, Divider, Chip } from "@mui/material";
|
|
6
6
|
import Skeleton from "@mui/material/Skeleton";
|
|
7
|
+
import { styled } from "@mui/material/styles";
|
|
8
|
+
import { tooltipClasses } from "@mui/material/Tooltip";
|
|
7
9
|
import Group from "@mui/icons-material/Group";
|
|
8
10
|
import ExitToApp from "@mui/icons-material/ExitToApp";
|
|
9
11
|
import Close from "@mui/icons-material/Close";
|
|
@@ -159,6 +161,141 @@ function OverflowMenu({
|
|
|
159
161
|
})]
|
|
160
162
|
});
|
|
161
163
|
}
|
|
164
|
+
const DIFFICULTY_ORDER = ["A1", "A2", "B1", "B2", "C1", "C2"];
|
|
165
|
+
const MAX_VISIBLE_DIFFICULTIES = 2;
|
|
166
|
+
const DifficultyOverflowTooltip = styled(({
|
|
167
|
+
className,
|
|
168
|
+
...props
|
|
169
|
+
}) => /*#__PURE__*/_jsx(Tooltip, {
|
|
170
|
+
...props,
|
|
171
|
+
classes: {
|
|
172
|
+
popper: className
|
|
173
|
+
}
|
|
174
|
+
}))(({
|
|
175
|
+
theme
|
|
176
|
+
}) => ({
|
|
177
|
+
[`& .${tooltipClasses.tooltip}`]: {
|
|
178
|
+
backgroundColor: theme.palette.background.paper,
|
|
179
|
+
color: theme.palette.text.primary,
|
|
180
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
181
|
+
borderRadius: theme.shape.borderRadius,
|
|
182
|
+
boxShadow: theme.shadows[4],
|
|
183
|
+
padding: theme.spacing(1.5, 2),
|
|
184
|
+
minWidth: 160,
|
|
185
|
+
maxWidth: 260
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
188
|
+
function normalizeDifficulties(difficulty) {
|
|
189
|
+
const values = Array.isArray(difficulty) ? difficulty : difficulty ? [difficulty] : [];
|
|
190
|
+
return DIFFICULTY_ORDER.filter(level => values.includes(level));
|
|
191
|
+
}
|
|
192
|
+
function difficultyChipSx(theme, compact) {
|
|
193
|
+
return {
|
|
194
|
+
height: compact ? 20 : 24,
|
|
195
|
+
backgroundColor: theme.palette.action.selected,
|
|
196
|
+
color: theme.palette.text.secondary,
|
|
197
|
+
"& .MuiChip-label": {
|
|
198
|
+
fontSize: compact ? "0.7rem" : "0.75rem",
|
|
199
|
+
px: 0.75
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function CourseDifficulty({
|
|
204
|
+
t,
|
|
205
|
+
difficulty,
|
|
206
|
+
compact
|
|
207
|
+
}) {
|
|
208
|
+
const levels = normalizeDifficulties(difficulty);
|
|
209
|
+
if (levels.length === 0) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const levelLabels = levels.map(level => t(`card_${level}`));
|
|
213
|
+
const visibleLabels = levelLabels.slice(0, MAX_VISIBLE_DIFFICULTIES);
|
|
214
|
+
const overflowLabels = levelLabels.slice(MAX_VISIBLE_DIFFICULTIES);
|
|
215
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
216
|
+
component: "span",
|
|
217
|
+
role: "group",
|
|
218
|
+
"aria-label": `${t("difficulty")}: ${levelLabels.join(", ")}`,
|
|
219
|
+
sx: {
|
|
220
|
+
display: "inline-flex",
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
gap: 0.5
|
|
223
|
+
},
|
|
224
|
+
"data-cy": "course-difficulty",
|
|
225
|
+
children: [/*#__PURE__*/_jsx(DifficultyIcon, {
|
|
226
|
+
sx: {
|
|
227
|
+
...classes.infoIcon,
|
|
228
|
+
fontSize: compact ? 18 : undefined
|
|
229
|
+
},
|
|
230
|
+
fontSize: "small"
|
|
231
|
+
}), visibleLabels.map((label, index) => /*#__PURE__*/_jsx(Chip, {
|
|
232
|
+
label: label,
|
|
233
|
+
size: "small",
|
|
234
|
+
"data-cy": `course-difficulty-chip-${index}`,
|
|
235
|
+
sx: theme => difficultyChipSx(theme, compact)
|
|
236
|
+
}, label)), overflowLabels.length > 0 && /*#__PURE__*/_jsx(DifficultyOverflowTooltip, {
|
|
237
|
+
placement: "bottom-start",
|
|
238
|
+
title: /*#__PURE__*/_jsxs(Box, {
|
|
239
|
+
sx: {
|
|
240
|
+
minWidth: 140
|
|
241
|
+
},
|
|
242
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
243
|
+
variant: "caption",
|
|
244
|
+
sx: {
|
|
245
|
+
display: "block",
|
|
246
|
+
color: "text.secondary",
|
|
247
|
+
fontWeight: 700,
|
|
248
|
+
letterSpacing: 0.5,
|
|
249
|
+
textTransform: "uppercase",
|
|
250
|
+
pb: 1,
|
|
251
|
+
borderBottom: "1px solid",
|
|
252
|
+
borderColor: "divider"
|
|
253
|
+
},
|
|
254
|
+
children: t("difficulties")
|
|
255
|
+
}), levelLabels.map((label, index) => /*#__PURE__*/_jsxs(Box, {
|
|
256
|
+
sx: {
|
|
257
|
+
display: "flex",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
gap: 1,
|
|
260
|
+
py: 0.75,
|
|
261
|
+
borderBottom: index < levelLabels.length - 1 ? "1px solid" : "none",
|
|
262
|
+
borderColor: "divider"
|
|
263
|
+
},
|
|
264
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
265
|
+
component: "span",
|
|
266
|
+
sx: {
|
|
267
|
+
color: "text.disabled",
|
|
268
|
+
fontSize: "1.3rem",
|
|
269
|
+
lineHeight: 1
|
|
270
|
+
},
|
|
271
|
+
children: "\u2022"
|
|
272
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
273
|
+
variant: "body2",
|
|
274
|
+
sx: {
|
|
275
|
+
color: "text.primary"
|
|
276
|
+
},
|
|
277
|
+
children: label
|
|
278
|
+
})]
|
|
279
|
+
}, label))]
|
|
280
|
+
}),
|
|
281
|
+
children: /*#__PURE__*/_jsx(Chip, {
|
|
282
|
+
label: `+${overflowLabels.length}`,
|
|
283
|
+
size: "small",
|
|
284
|
+
"aria-label": `${t("difficulty")}: ${levelLabels.join(", ")}`,
|
|
285
|
+
"data-cy": "course-difficulty-overflow",
|
|
286
|
+
sx: theme => ({
|
|
287
|
+
...difficultyChipSx(theme, compact),
|
|
288
|
+
fontWeight: 600
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
})]
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
CourseDifficulty.propTypes = {
|
|
295
|
+
t: PropTypes.func.isRequired,
|
|
296
|
+
difficulty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
297
|
+
compact: PropTypes.bool
|
|
298
|
+
};
|
|
162
299
|
function CourseLoading({
|
|
163
300
|
compact = false
|
|
164
301
|
}) {
|
|
@@ -278,7 +415,7 @@ function CourseCard({
|
|
|
278
415
|
limit: 1000
|
|
279
416
|
}
|
|
280
417
|
}, {
|
|
281
|
-
enabled: !!course?.courseId && isOutlineOpen
|
|
418
|
+
enabled: !!course?.courseId && (isOutlineOpen || visible)
|
|
282
419
|
});
|
|
283
420
|
const {
|
|
284
421
|
membersQuery,
|
|
@@ -356,6 +493,7 @@ function CourseCard({
|
|
|
356
493
|
handleLeaveCourse(courseId);
|
|
357
494
|
};
|
|
358
495
|
const courseSections = sectionsQuery.isSuccess ? sectionsQuery.data.Items : [];
|
|
496
|
+
const topicDifficulties = sectionsQuery.isSuccess ? [...new Set(courseSections.flatMap(section => section.topics || []).map(topic => topic.difficulty).filter(Boolean))] : [];
|
|
359
497
|
const deleteCourse = async () => {
|
|
360
498
|
const confirmed = await confirm(t("delete_course"), t("delete_course_confirmation"), "", true, t("course_deletion_note", {
|
|
361
499
|
sectionCount: courseSections ? courseSections.length : 0
|
|
@@ -497,16 +635,10 @@ function CourseCard({
|
|
|
497
635
|
columnGap: 1.5,
|
|
498
636
|
rowGap: 0.5
|
|
499
637
|
},
|
|
500
|
-
children: [
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
sx: {
|
|
505
|
-
...classes.infoIcon,
|
|
506
|
-
fontSize: compact ? 18 : undefined
|
|
507
|
-
},
|
|
508
|
-
fontSize: "small"
|
|
509
|
-
}), t(`card_${course.difficulty}`)]
|
|
638
|
+
children: [/*#__PURE__*/_jsx(CourseDifficulty, {
|
|
639
|
+
t: t,
|
|
640
|
+
difficulty: topicDifficulties.length > 0 ? topicDifficulties : course?.difficulty,
|
|
641
|
+
compact: compact
|
|
510
642
|
}), (course?.pdf || course.topicsWithPdf && Object.keys(course?.topicsWithPdf)?.length > 0) && /*#__PURE__*/_jsxs(CardInfoSectionItem, {
|
|
511
643
|
variant: "caption",
|
|
512
644
|
component: "span",
|
|
@@ -673,7 +805,7 @@ MemoizedCourse.propTypes = {
|
|
|
673
805
|
courseId: PropTypes.string.isRequired,
|
|
674
806
|
courseName: PropTypes.string.isRequired,
|
|
675
807
|
description: PropTypes.string.isRequired,
|
|
676
|
-
difficulty: PropTypes.string,
|
|
808
|
+
difficulty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
677
809
|
picture: PropTypes.string.isRequired,
|
|
678
810
|
learnLang: PropTypes.string.isRequired,
|
|
679
811
|
forLang: PropTypes.string.isRequired,
|
|
@@ -272,6 +272,7 @@ export default function Message({
|
|
|
272
272
|
speak,
|
|
273
273
|
stopSpeaking,
|
|
274
274
|
voice,
|
|
275
|
+
voiceLanguageCode,
|
|
275
276
|
voicePitch,
|
|
276
277
|
voiceSpeed,
|
|
277
278
|
handleEditMessage,
|
|
@@ -471,6 +472,7 @@ export default function Message({
|
|
|
471
472
|
forLang: forLang,
|
|
472
473
|
handleSpeak: handleSpeak,
|
|
473
474
|
voice: voice,
|
|
475
|
+
voiceLanguageCode: voiceLanguageCode,
|
|
474
476
|
voicePitch: voicePitch,
|
|
475
477
|
voiceSpeed: voiceSpeed,
|
|
476
478
|
disableTranslation: disableTranslation,
|
|
@@ -538,6 +540,7 @@ export default function Message({
|
|
|
538
540
|
forLang: forLang,
|
|
539
541
|
handleSpeak: handleSpeak,
|
|
540
542
|
voice: voice,
|
|
543
|
+
voiceLanguageCode: voiceLanguageCode,
|
|
541
544
|
voicePitch: voicePitch,
|
|
542
545
|
voiceSpeed: voiceSpeed,
|
|
543
546
|
disableTranslation: disableTranslation,
|
|
@@ -623,7 +626,7 @@ export default function Message({
|
|
|
623
626
|
children: /*#__PURE__*/_jsx(IconButton, {
|
|
624
627
|
color: "inherit",
|
|
625
628
|
disabled: isSpeaking || recognizing || muteSound || isMessagePlaying || showTranslation,
|
|
626
|
-
onClick: () => speak(text, voice, false, messageContainerIdSelector,
|
|
629
|
+
onClick: () => speak(text, voice, false, messageContainerIdSelector, voiceLanguageCode, false, voicePitch),
|
|
627
630
|
size: "small",
|
|
628
631
|
"aria-label": messageIndex !== -1 ? accessibilityText ? accessibilityText : `Speak message ${messageIndex + 1}` : accessibilityText ? accessibilityText : `Speak message`,
|
|
629
632
|
sx: {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
2
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
3
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
|
4
|
+
import Message from "./Message";
|
|
5
|
+
|
|
6
|
+
// Messages spreads each message over these props, so a roleplay message carries
|
|
7
|
+
// its own actor's voice and accent; a bot chat supplies one voice for the
|
|
8
|
+
// whole chat. Replaying must use whichever applies, not the language default.
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
const renderMessage = (overrides = {}) => {
|
|
11
|
+
const theme = createTheme();
|
|
12
|
+
const speak = vi.fn();
|
|
13
|
+
render(/*#__PURE__*/_jsx(ThemeProvider, {
|
|
14
|
+
theme: theme,
|
|
15
|
+
children: /*#__PURE__*/_jsx(Message, {
|
|
16
|
+
t: k => k,
|
|
17
|
+
text: "Kia ora",
|
|
18
|
+
speak: speak,
|
|
19
|
+
messageIndex: 0,
|
|
20
|
+
index: 0,
|
|
21
|
+
messageCount: 1,
|
|
22
|
+
messages: [{
|
|
23
|
+
text: "Kia ora"
|
|
24
|
+
}],
|
|
25
|
+
...overrides
|
|
26
|
+
})
|
|
27
|
+
}));
|
|
28
|
+
return speak;
|
|
29
|
+
};
|
|
30
|
+
describe("Message replay", () => {
|
|
31
|
+
afterEach(cleanup);
|
|
32
|
+
it("speaks with the accent the message carries", () => {
|
|
33
|
+
const speak = renderMessage({
|
|
34
|
+
voice: "Aria",
|
|
35
|
+
voiceLanguageCode: "en-NZ"
|
|
36
|
+
});
|
|
37
|
+
fireEvent.click(screen.getByRole("button", {
|
|
38
|
+
name: /Speak message/i
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
// 5th positional argument of speak() is the accent.
|
|
42
|
+
expect(speak).toHaveBeenCalled();
|
|
43
|
+
expect(speak.mock.calls[0][1]).toBe("Aria");
|
|
44
|
+
expect(speak.mock.calls[0][4]).toBe("en-NZ");
|
|
45
|
+
});
|
|
46
|
+
it("sends no accent when nothing supplies one", () => {
|
|
47
|
+
const speak = renderMessage({
|
|
48
|
+
voice: "Joanna"
|
|
49
|
+
});
|
|
50
|
+
fireEvent.click(screen.getByRole("button", {
|
|
51
|
+
name: /Speak message/i
|
|
52
|
+
}));
|
|
53
|
+
expect(speak.mock.calls[0][4]).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -386,16 +386,20 @@ function App({
|
|
|
386
386
|
startIcon: /*#__PURE__*/_jsx(Add, {}),
|
|
387
387
|
color: "secondary",
|
|
388
388
|
id: "appbar-create-button",
|
|
389
|
+
disabled: !subscription?.isSubscriptionLoaded,
|
|
389
390
|
children: t("create")
|
|
390
391
|
}) : /*#__PURE__*/_jsx(Tooltip, {
|
|
391
392
|
title: t("create"),
|
|
392
393
|
enterDelay: 300,
|
|
393
|
-
children: /*#__PURE__*/_jsx(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
395
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
396
|
+
color: "inherit",
|
|
397
|
+
"aria-label": t("create"),
|
|
398
|
+
"aria-haspopup": "true",
|
|
399
|
+
onClick: handleClickCreate,
|
|
400
|
+
disabled: !subscription?.isSubscriptionLoaded,
|
|
401
|
+
children: /*#__PURE__*/_jsx(Add, {})
|
|
402
|
+
})
|
|
399
403
|
})
|
|
400
404
|
})), isLgScreen && /*#__PURE__*/_jsx(WhatsNew, {
|
|
401
405
|
t: t,
|
|
@@ -57,6 +57,7 @@ function CreatePhrase({
|
|
|
57
57
|
verificationStatus,
|
|
58
58
|
makeChatGptApiRequest,
|
|
59
59
|
difficulty,
|
|
60
|
+
openSnackbar,
|
|
60
61
|
...otherProps
|
|
61
62
|
}) {
|
|
62
63
|
const translationInputRef = useRef();
|
|
@@ -723,7 +724,8 @@ function CreatePhrase({
|
|
|
723
724
|
autoSuggest: true,
|
|
724
725
|
autoSuggestedImages: autoSuggestedImages,
|
|
725
726
|
selectedImage: selectedImage,
|
|
726
|
-
verificationStatus: verificationStatus
|
|
727
|
+
verificationStatus: verificationStatus,
|
|
728
|
+
openSnackbar: openSnackbar
|
|
727
729
|
})
|
|
728
730
|
})]
|
|
729
731
|
})]
|
|
@@ -71,7 +71,8 @@ export default function CreatePhraseGroupDialog({
|
|
|
71
71
|
onCreated,
|
|
72
72
|
t = text => text,
|
|
73
73
|
fileSizeLimit,
|
|
74
|
-
verificationStatus
|
|
74
|
+
verificationStatus,
|
|
75
|
+
openSnackbar
|
|
75
76
|
}) {
|
|
76
77
|
const [name, setName] = useState("");
|
|
77
78
|
const [description, setDescription] = useState("");
|
|
@@ -180,7 +181,8 @@ export default function CreatePhraseGroupDialog({
|
|
|
180
181
|
autoCrop: true,
|
|
181
182
|
autoSuggest: false,
|
|
182
183
|
selectedImage: selectedImage,
|
|
183
|
-
verificationStatus: verificationStatus
|
|
184
|
+
verificationStatus: verificationStatus,
|
|
185
|
+
openSnackbar: openSnackbar
|
|
184
186
|
})
|
|
185
187
|
})]
|
|
186
188
|
})
|
|
@@ -110,13 +110,16 @@ function GenerateRoleplayDialog({
|
|
|
110
110
|
if (generatedRoleplay && generatedRoleplay.script) {
|
|
111
111
|
const tempMessages = generatedRoleplay.script.filter(l => !l?.question).map(line => {
|
|
112
112
|
const isActor1 = line.actor === 1;
|
|
113
|
+
const actor = (isActor1 ? generatedRoleplay.actor1 : generatedRoleplay.actor2) || {};
|
|
113
114
|
return {
|
|
114
115
|
...line,
|
|
115
116
|
text: line.text,
|
|
116
117
|
isMyMessage: !isActor1,
|
|
117
118
|
name: getActorName(isActor1),
|
|
118
|
-
userImage:
|
|
119
|
-
voice:
|
|
119
|
+
userImage: actor.picture || null,
|
|
120
|
+
voice: actor.voice || null,
|
|
121
|
+
voiceLanguageCode: actor.voiceLanguageCode || null,
|
|
122
|
+
voicePitch: actor.voicePitch || null
|
|
120
123
|
};
|
|
121
124
|
});
|
|
122
125
|
setPreviewMessages(tempMessages);
|
|
@@ -7,13 +7,8 @@ import ImageBuilderControls from "./ImageBuilderControls";
|
|
|
7
7
|
import ImageBuilderPreview from "./ImageBuilderPreview";
|
|
8
8
|
import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
|
|
9
9
|
import { PRESET_COLORS, ICON_KEYS } from "./iconPaths";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
// Matches placeholder SVG ratio (21144 x 4890), scaled down 10x
|
|
10
|
+
import { generateBuilderImages, getOptimalIconOpacity, shouldUseDarkIcons } from "./imageBuilderUtils";
|
|
13
11
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
-
const BANNER_W = 2114;
|
|
15
|
-
const BANNER_H = 489;
|
|
16
|
-
const CARD_SIZE = 489;
|
|
17
12
|
const useStyles = makeStyles()(theme => ({
|
|
18
13
|
body: {
|
|
19
14
|
display: "flex",
|
|
@@ -51,19 +46,13 @@ export default function ImageBuilderDialog({
|
|
|
51
46
|
cardDataUrl: null
|
|
52
47
|
};
|
|
53
48
|
}
|
|
54
|
-
|
|
49
|
+
return generateBuilderImages({
|
|
55
50
|
color,
|
|
56
51
|
iconKey,
|
|
57
52
|
density: "dense",
|
|
58
53
|
iconOpacity,
|
|
59
|
-
darkIcons
|
|
60
|
-
width: BANNER_W,
|
|
61
|
-
height: BANNER_H
|
|
54
|
+
darkIcons
|
|
62
55
|
});
|
|
63
|
-
return {
|
|
64
|
-
bannerDataUrl: canvas.toDataURL("image/png"),
|
|
65
|
-
cardDataUrl: getCenterCropDataUrl(canvas, CARD_SIZE)
|
|
66
|
-
};
|
|
67
56
|
}, [color, iconKey, iconOpacity, darkIcons]);
|
|
68
57
|
|
|
69
58
|
// Reset state when dialog opens
|
|
@@ -4,6 +4,19 @@ const DENSITY_COLS = {
|
|
|
4
4
|
medium: 10,
|
|
5
5
|
dense: 14
|
|
6
6
|
};
|
|
7
|
+
|
|
8
|
+
// Base (logical) dimensions of a builder image. Matches placeholder SVG ratio
|
|
9
|
+
// (21144 x 4890), scaled down 10x. The card is a centre-cropped square.
|
|
10
|
+
export const BANNER_W = 2114;
|
|
11
|
+
export const BANNER_H = 489;
|
|
12
|
+
export const CARD_SIZE = 489;
|
|
13
|
+
|
|
14
|
+
// Supersampling factor applied to the generated pixel dimensions so the stored
|
|
15
|
+
// image stays crisp when scaled up on large / HiDPI screens. Device-independent
|
|
16
|
+
// on purpose — the image is persisted and shared across devices, so we must not
|
|
17
|
+
// bake window.devicePixelRatio into it. Tune this single knob to trade payload
|
|
18
|
+
// size against sharpness.
|
|
19
|
+
export const OUTPUT_SCALE = 2;
|
|
7
20
|
function hexToRgb(hex) {
|
|
8
21
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
9
22
|
return result ? {
|
|
@@ -87,6 +100,7 @@ export function generatePatternCanvas({
|
|
|
87
100
|
canvas.width = width;
|
|
88
101
|
canvas.height = height;
|
|
89
102
|
const ctx = canvas.getContext("2d");
|
|
103
|
+
ctx.imageSmoothingQuality = "high";
|
|
90
104
|
|
|
91
105
|
// 1. Solid background
|
|
92
106
|
ctx.fillStyle = color;
|
|
@@ -107,10 +121,18 @@ export function generatePatternCanvas({
|
|
|
107
121
|
};
|
|
108
122
|
const maxDim = Math.max(vb.width, vb.height);
|
|
109
123
|
const scale = iconSize / maxDim;
|
|
124
|
+
|
|
125
|
+
// Rasterise the stamp at its final on-canvas size by filling the vector paths
|
|
126
|
+
// through a scaled context. Previously the stamp was drawn at the raw viewBox
|
|
127
|
+
// size (e.g. 24px) and then upscaled ~4x when blitted onto the pattern canvas,
|
|
128
|
+
// which baked a blurry raster upscale into the source image. Filling from
|
|
129
|
+
// vectors at the target size keeps icons crisp while preserving the single
|
|
130
|
+
// stamp (so overlapping sub-paths don't double-composite at globalAlpha < 1).
|
|
110
131
|
const stampCanvas = document.createElement("canvas");
|
|
111
|
-
stampCanvas.width = vb.width;
|
|
112
|
-
stampCanvas.height = vb.height;
|
|
132
|
+
stampCanvas.width = Math.ceil(vb.width * scale);
|
|
133
|
+
stampCanvas.height = Math.ceil(vb.height * scale);
|
|
113
134
|
const stampCtx = stampCanvas.getContext("2d");
|
|
135
|
+
stampCtx.scale(scale, scale);
|
|
114
136
|
stampCtx.fillStyle = darkIcons ? "#000" : "#fff";
|
|
115
137
|
const path2Ds = iconData.paths.map(d => new Path2D(d));
|
|
116
138
|
for (const p of path2Ds) {
|
|
@@ -131,9 +153,8 @@ export function generatePatternCanvas({
|
|
|
131
153
|
ctx.save();
|
|
132
154
|
ctx.translate(x, y);
|
|
133
155
|
ctx.rotate(rotation);
|
|
134
|
-
|
|
135
|
-
ctx.
|
|
136
|
-
ctx.drawImage(stampCanvas, 0, 0);
|
|
156
|
+
// Draw the pre-scaled stamp 1:1 (centred) — no upscaling here.
|
|
157
|
+
ctx.drawImage(stampCanvas, -stampCanvas.width / 2, -stampCanvas.height / 2);
|
|
137
158
|
ctx.restore();
|
|
138
159
|
}
|
|
139
160
|
}
|
|
@@ -156,4 +177,25 @@ export function getCenterCropDataUrl(source, size) {
|
|
|
156
177
|
const sx = (source.width - source.height) / 2;
|
|
157
178
|
ctx.drawImage(source, sx, 0, source.height, source.height, 0, 0, size, size);
|
|
158
179
|
return cropCanvas.toDataURL("image/png");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Generate the builder images (banner + centre-cropped card) at the shared
|
|
184
|
+
* supersampled output resolution. Single source of truth for the builder
|
|
185
|
+
* dimensions so callers don't duplicate the magic numbers.
|
|
186
|
+
*
|
|
187
|
+
* @param {Object} opts – forwarded to generatePatternCanvas (color, iconKey,
|
|
188
|
+
* density, iconOpacity, darkIcons, seed)
|
|
189
|
+
* @returns {{ bannerDataUrl: string, cardDataUrl: string }}
|
|
190
|
+
*/
|
|
191
|
+
export function generateBuilderImages(opts) {
|
|
192
|
+
const canvas = generatePatternCanvas({
|
|
193
|
+
...opts,
|
|
194
|
+
width: BANNER_W * OUTPUT_SCALE,
|
|
195
|
+
height: BANNER_H * OUTPUT_SCALE
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
bannerDataUrl: canvas.toDataURL("image/png"),
|
|
199
|
+
cardDataUrl: getCenterCropDataUrl(canvas, CARD_SIZE * OUTPUT_SCALE)
|
|
200
|
+
};
|
|
159
201
|
}
|
|
@@ -459,7 +459,7 @@ export default function QuickCreateChatbotDialog({
|
|
|
459
459
|
setSelectedVoicePitch(null);
|
|
460
460
|
setBotPicture(picked.voice.userImage || "");
|
|
461
461
|
}, [open, selectedVoice, voices]);
|
|
462
|
-
const selectedVoiceOption = findVoice(voices, selectedVoice)?.voice;
|
|
462
|
+
const selectedVoiceOption = findVoice(voices, selectedVoice, selectedVoiceLanguageCode)?.voice;
|
|
463
463
|
const hasAvailableVoice = Boolean(selectedVoice && selectedVoiceLanguageCode);
|
|
464
464
|
const isValid = Boolean(botTitle.trim()) && Boolean(difficulty) && Boolean(templateId) && Boolean(template && template.botId === templateId) && Number(numberOfQuestions) > 0 && hasAvailableVoice;
|
|
465
465
|
const handleSubmit = async () => {
|
|
@@ -600,6 +600,7 @@ export default function QuickCreateChatbotDialog({
|
|
|
600
600
|
name: "voiceSelector",
|
|
601
601
|
label: t("bot_voice"),
|
|
602
602
|
value: selectedVoice || "",
|
|
603
|
+
valueLanguageCode: selectedVoiceLanguageCode,
|
|
603
604
|
margin: "normal",
|
|
604
605
|
variant: "outlined",
|
|
605
606
|
fullWidth: true,
|
|
@@ -80,7 +80,8 @@ export default function QuickCreateClassroomDialog({
|
|
|
80
80
|
userImage,
|
|
81
81
|
forLang,
|
|
82
82
|
languages,
|
|
83
|
-
forLanguages
|
|
83
|
+
forLanguages,
|
|
84
|
+
openSnackbar
|
|
84
85
|
}) {
|
|
85
86
|
const [classroomName, setClassroomName] = useState("");
|
|
86
87
|
const [classroomDescription, setClassroomDescription] = useState("");
|
|
@@ -275,7 +276,8 @@ export default function QuickCreateClassroomDialog({
|
|
|
275
276
|
autoSuggest: false,
|
|
276
277
|
selectedImage: selectedImage,
|
|
277
278
|
verificationStatus: verificationStatus,
|
|
278
|
-
onBuilderSettingsChange: setBuilderSettings
|
|
279
|
+
onBuilderSettingsChange: setBuilderSettings,
|
|
280
|
+
openSnackbar: openSnackbar
|
|
279
281
|
})
|
|
280
282
|
})]
|
|
281
283
|
})
|
|
@@ -83,7 +83,8 @@ export default function QuickCreateCourseDialog({
|
|
|
83
83
|
difficulties = ["A1", "A2", "B1", "B2", "C1", "C2"],
|
|
84
84
|
fileSizeLimit,
|
|
85
85
|
verificationStatus,
|
|
86
|
-
userImage
|
|
86
|
+
userImage,
|
|
87
|
+
openSnackbar
|
|
87
88
|
}) {
|
|
88
89
|
const defaultSelectedImage = {
|
|
89
90
|
image: "",
|
|
@@ -289,7 +290,8 @@ export default function QuickCreateCourseDialog({
|
|
|
289
290
|
autoSuggest: false,
|
|
290
291
|
selectedImage: selectedImage,
|
|
291
292
|
verificationStatus: verificationStatus,
|
|
292
|
-
onBuilderSettingsChange: setBuilderSettings
|
|
293
|
+
onBuilderSettingsChange: setBuilderSettings,
|
|
294
|
+
openSnackbar: openSnackbar
|
|
293
295
|
})
|
|
294
296
|
})]
|
|
295
297
|
})
|
|
@@ -83,7 +83,8 @@ export default function QuickCreateTopicDialog({
|
|
|
83
83
|
userImage,
|
|
84
84
|
coursePicture,
|
|
85
85
|
coursePictureBuilderColor,
|
|
86
|
-
coursePictureBuilderIcon
|
|
86
|
+
coursePictureBuilderIcon,
|
|
87
|
+
openSnackbar
|
|
87
88
|
}) {
|
|
88
89
|
// Only inherit the parent course image when it's a real user-set image.
|
|
89
90
|
// If the course has no image, or its image was itself generated by the image
|
|
@@ -274,7 +275,8 @@ export default function QuickCreateTopicDialog({
|
|
|
274
275
|
autoSuggest: false,
|
|
275
276
|
selectedImage: selectedImage,
|
|
276
277
|
verificationStatus: verificationStatus,
|
|
277
|
-
onBuilderSettingsChange: setBuilderSettings
|
|
278
|
+
onBuilderSettingsChange: setBuilderSettings,
|
|
279
|
+
openSnackbar: openSnackbar
|
|
278
280
|
})
|
|
279
281
|
})]
|
|
280
282
|
})
|
package/dist/Editors/Bot/Bot.js
CHANGED
|
@@ -486,18 +486,20 @@ function Bot({
|
|
|
486
486
|
return;
|
|
487
487
|
}
|
|
488
488
|
const voice = initialValues.voice;
|
|
489
|
+
const voiceLanguageCode = initialValues.voiceLanguageCode;
|
|
489
490
|
const message = {
|
|
490
491
|
isMyMessage: isMyMessage,
|
|
491
492
|
userImage: isErrorMsg ? BotNualaImage : isMyMessage ? userAttributes && userAttributes.picture ? userAttributes.picture : null : bot && bot.botAvatar ? bot.botAvatar : null,
|
|
492
493
|
name: isErrorMsg ? "Bot" : isMyMessage ? userAttributes && userAttributes.preferred_username ? userAttributes.preferred_username : "Me" : bot && bot.name ? bot.name : "Bot",
|
|
493
494
|
text: text,
|
|
494
|
-
datetime: new Date()
|
|
495
|
+
datetime: new Date(),
|
|
496
|
+
// Recorded so the replay button reproduces what was spoken.
|
|
497
|
+
voice: voice,
|
|
498
|
+
voiceLanguageCode: voiceLanguageCode
|
|
495
499
|
};
|
|
496
500
|
setMessages(prevState => [...prevState, message]);
|
|
497
501
|
if (playAudio !== false && learnLang !== "irish") {
|
|
498
|
-
speak(message.text, voice, false, `#message${messages.length + 1}
|
|
499
|
-
//voiceLanguageCode
|
|
500
|
-
);
|
|
502
|
+
speak(message.text, voice, false, `#message${messages.length + 1}`, voiceLanguageCode);
|
|
501
503
|
}
|
|
502
504
|
};
|
|
503
505
|
const handleSubmitText = async () => {
|
|
@@ -770,7 +772,8 @@ function Bot({
|
|
|
770
772
|
courseId: courseId,
|
|
771
773
|
sectionId: sectionId,
|
|
772
774
|
topicId: topicId,
|
|
773
|
-
initialTopicGoal: initialTopicGoal
|
|
775
|
+
initialTopicGoal: initialTopicGoal,
|
|
776
|
+
openSnackbar: openSnackbar
|
|
774
777
|
}) : null, tabValue === 1 ? /*#__PURE__*/_jsx(Box, {
|
|
775
778
|
className: hidden ? classes.paneFull : classes.pane,
|
|
776
779
|
children: BotPreview
|
|
@@ -791,7 +794,8 @@ function Bot({
|
|
|
791
794
|
subscription: subscription,
|
|
792
795
|
handleBotInstructionsImageUpload: handleBotInstructionsImageUpload,
|
|
793
796
|
isUserInternal: isUserInternal,
|
|
794
|
-
verificationStatus: verificationStatus
|
|
797
|
+
verificationStatus: verificationStatus,
|
|
798
|
+
openSnackbar: openSnackbar
|
|
795
799
|
})
|
|
796
800
|
}) : null]
|
|
797
801
|
}), /*#__PURE__*/_jsx(ResponsiveDialog, {
|
|
@@ -14,7 +14,8 @@ function AddImageModal(props) {
|
|
|
14
14
|
open,
|
|
15
15
|
handleUploadBotExerciseImage,
|
|
16
16
|
botId,
|
|
17
|
-
verificationStatus
|
|
17
|
+
verificationStatus,
|
|
18
|
+
openSnackbar
|
|
18
19
|
} = props;
|
|
19
20
|
const theme = useTheme();
|
|
20
21
|
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
|
@@ -76,7 +77,8 @@ function AddImageModal(props) {
|
|
|
76
77
|
name: "image",
|
|
77
78
|
value: selectedImage.image,
|
|
78
79
|
handleChange: handleImageChange,
|
|
79
|
-
verificationStatus: verificationStatus
|
|
80
|
+
verificationStatus: verificationStatus,
|
|
81
|
+
openSnackbar: openSnackbar
|
|
80
82
|
})]
|
|
81
83
|
}), /*#__PURE__*/_jsxs(DialogActions, {
|
|
82
84
|
children: [/*#__PURE__*/_jsx(Button, {
|