fcad-core-dragon 2.0.1 → 2.0.2-beta.2
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/.editorconfig +33 -33
- package/.eslintignore +29 -29
- package/.eslintrc.cjs +81 -81
- package/CHANGELOG +17 -1
- package/bk.scss +117 -117
- package/package.json +1 -1
- package/src/$locales/en.json +18 -4
- package/src/$locales/fr.json +17 -3
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +36 -341
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +16 -21
- package/src/components/AppBasePage.vue +45 -14
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +45 -0
- package/src/components/AppCompAudio.vue +12 -3
- package/src/components/AppCompButtonProgress.vue +13 -2
- package/src/components/AppCompCarousel.vue +12 -4
- package/src/components/AppCompInputCheckBoxNx.vue +324 -0
- package/src/components/AppCompInputDropdownNx.vue +295 -0
- package/src/components/AppCompInputRadioNx.vue +264 -0
- package/src/components/AppCompInputTextNx.vue +148 -0
- package/src/components/AppCompInputTextTableNx.vue +198 -0
- package/src/components/AppCompInputTextToFillDropdownNx.vue +291 -0
- package/src/components/AppCompInputTextToFillNx.vue +277 -0
- package/src/components/AppCompJauge.vue +11 -4
- package/src/components/AppCompMenu.vue +7 -14
- package/src/components/AppCompMenuItem.vue +7 -5
- package/src/components/AppCompNavigation.vue +21 -21
- package/src/components/AppCompNoteCall.vue +1 -0
- package/src/components/AppCompNoteCredit.vue +2 -1
- package/src/components/AppCompPlayBarNext.vue +94 -41
- package/src/components/AppCompPlayBarProgress.vue +82 -82
- package/src/components/AppCompPopUpNext.vue +6 -6
- package/src/components/AppCompQuiz.vue +500 -0
- package/src/components/AppCompQuizRecall.vue +113 -66
- package/src/components/AppCompSettingsMenu.vue +172 -172
- package/src/components/AppCompTableOfContent.vue +39 -10
- package/src/components/AppCompVideoPlayer.vue +1 -1
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/composables/useQuiz.js +62 -179
- package/src/directives/nvdaFix.js +53 -0
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +227 -30
- package/src/mixins/$mediaMixins.js +1 -11
- package/src/module/stores/appStore.js +29 -11
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/bus.js +8 -8
- package/src/plugins/gsap.js +14 -14
- package/src/plugins/i18n.js +44 -44
- package/src/plugins/idb.js +1 -1
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/shared/generalfuncs.js +134 -0
- package/src/shared/validators.js +308 -234
- package/src/components/AppCompInputCheckBoxNext.vue +0 -205
- package/src/components/AppCompInputDropdownNext.vue +0 -201
- package/src/components/AppCompInputRadioNext.vue +0 -158
- package/src/components/AppCompInputTextNext.vue +0 -124
- package/src/components/AppCompInputTextTableNext.vue +0 -142
- package/src/components/AppCompInputTextToFillDropdownNext.vue +0 -238
- package/src/components/AppCompInputTextToFillNext.vue +0 -171
- package/src/components/AppCompQuizNext.vue +0 -2908
|
@@ -6,42 +6,51 @@
|
|
|
6
6
|
** data object should containt following attributes: quizId,activityId,pageId,hypertext_done',hypertext_undone,title,titletag
|
|
7
7
|
-->
|
|
8
8
|
<template>
|
|
9
|
-
<section class="quizRecall">
|
|
9
|
+
<section v-if="quizRecallData" class="quizRecall">
|
|
10
10
|
<!--Optionnal title, out of quiz-answer-conditionning, default tag H4, but can be change by user-->
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
{{ quizRecall.title }}
|
|
17
|
-
</component>
|
|
18
|
-
<!--Quiz answer conditionning-->
|
|
19
|
-
<app-base-error-display
|
|
20
|
-
v-if="hasError.length"
|
|
21
|
-
:error-group="'component'"
|
|
22
|
-
:error-title="`ERREUR: COMPOSANT QUIZ RECALL`"
|
|
23
|
-
:errors-list="hasError"
|
|
24
|
-
></app-base-error-display>
|
|
11
|
+
<!--Show skeleton while app data is not ready-->
|
|
12
|
+
<template v-if="!isReady">
|
|
13
|
+
<app-base-skeleton :skeleton-lines="2" />
|
|
14
|
+
</template>
|
|
15
|
+
<!--Show app data when ready-->
|
|
25
16
|
<template v-else>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
17
|
+
<component
|
|
18
|
+
:is="quizRecall.titletag"
|
|
19
|
+
v-if="quizRecall.title"
|
|
20
|
+
class="quizRecall-title"
|
|
21
|
+
>
|
|
22
|
+
{{ quizRecall.title }} -
|
|
23
|
+
</component>
|
|
24
|
+
<!--Quiz answer conditionning-->
|
|
25
|
+
<app-base-error-display
|
|
26
|
+
v-if="hasError.length"
|
|
27
|
+
:error-group="'component'"
|
|
28
|
+
:error-title="`ERREUR: COMPOSANT QUIZ RECALL`"
|
|
29
|
+
:errors-list="hasError"
|
|
30
|
+
></app-base-error-display>
|
|
31
|
+
<template v-else>
|
|
32
|
+
<template v-if="quizRecall.done == true">
|
|
33
|
+
<div
|
|
34
|
+
v-if="quizRecall.hypertext_done"
|
|
35
|
+
class="quizRecall-text-done"
|
|
36
|
+
v-html="quizRecall.hypertext_done"
|
|
37
|
+
></div>
|
|
38
|
+
<div class="quizRecall-ennonce" v-html="quizRecall.ennonce"></div>
|
|
39
|
+
|
|
40
|
+
<textarea
|
|
41
|
+
:id="`quizRecall-answer-${quizRecallData.quizId}`"
|
|
42
|
+
v-model="quizRecall.answer"
|
|
43
|
+
disabled
|
|
44
|
+
class="form-control"
|
|
45
|
+
></textarea>
|
|
46
|
+
</template>
|
|
47
|
+
<template v-if="quizRecall.done == false">
|
|
48
|
+
<div
|
|
49
|
+
v-if="quizRecall.hypertext_undone"
|
|
50
|
+
class="quizRecall-text-undone"
|
|
51
|
+
v-html="quizRecall.hypertext_undone"
|
|
52
|
+
></div>
|
|
53
|
+
</template>
|
|
45
54
|
</template>
|
|
46
55
|
</template>
|
|
47
56
|
</section>
|
|
@@ -51,6 +60,7 @@
|
|
|
51
60
|
//Recall mixins has the necessary datas and functions to give back quizRecall statu
|
|
52
61
|
import { mapState } from 'pinia'
|
|
53
62
|
import { useAppStore } from '../module/stores/appStore'
|
|
63
|
+
import { nextTick } from 'vue'
|
|
54
64
|
|
|
55
65
|
export default {
|
|
56
66
|
name: 'AppCompQuizRecall',
|
|
@@ -71,7 +81,8 @@ export default {
|
|
|
71
81
|
)}</p>` /*String traduite par défaut*/
|
|
72
82
|
},
|
|
73
83
|
hasError: [],
|
|
74
|
-
quizData: null
|
|
84
|
+
quizData: null,
|
|
85
|
+
isReady: false //Used to display skeleton while app data is not ready
|
|
75
86
|
}
|
|
76
87
|
},
|
|
77
88
|
computed: {
|
|
@@ -79,25 +90,46 @@ export default {
|
|
|
79
90
|
'getAllCompleted',
|
|
80
91
|
'getUserInteraction',
|
|
81
92
|
'getPageInteraction',
|
|
93
|
+
'getAppStatus',
|
|
82
94
|
'getPageData'
|
|
83
95
|
])
|
|
84
96
|
},
|
|
97
|
+
|
|
98
|
+
watch: {
|
|
99
|
+
//Watch for user interaction change to get quizRecall answer
|
|
100
|
+
getUserInteraction: {
|
|
101
|
+
async handler(newValue) {
|
|
102
|
+
if (!this.getUserInteraction) return
|
|
103
|
+
|
|
104
|
+
const { activityId, pageId } = this.quizRecallData
|
|
105
|
+
const interaction = this.getPageInteraction(
|
|
106
|
+
activityId,
|
|
107
|
+
pageId
|
|
108
|
+
).userInteraction
|
|
109
|
+
|
|
110
|
+
if (!interaction) return
|
|
111
|
+
//Get quizRecall answer
|
|
112
|
+
if (this.quizRecallData && this.quizData) {
|
|
113
|
+
await this.getQuizRecallAnswer(interaction)
|
|
114
|
+
} else {
|
|
115
|
+
this.quizRecall.done == false
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
immediate: true,
|
|
119
|
+
deep: true
|
|
120
|
+
}
|
|
121
|
+
},
|
|
85
122
|
created() {
|
|
86
123
|
//Validate quizRecall data
|
|
87
124
|
//(no validation to the quiz, only to the new datas)
|
|
88
|
-
|
|
125
|
+
if (import.meta.env.DEV) {
|
|
126
|
+
this.validateQuizRecallData(this.quizRecallData)
|
|
127
|
+
}
|
|
89
128
|
const { activityId, pageId } = this.quizRecallData
|
|
90
129
|
//Get quizData and validate the quiz type
|
|
91
130
|
if (activityId && pageId) {
|
|
92
131
|
this.getQuizData(activityId, pageId)
|
|
93
132
|
}
|
|
94
|
-
|
|
95
|
-
//Get quizRecall answer
|
|
96
|
-
if (this.quizRecallData && this.quizData) {
|
|
97
|
-
this.getQuizRecallAnswer(this.quizRecallData, this.quizData)
|
|
98
|
-
} else {
|
|
99
|
-
this.quizRecall.done == false
|
|
100
|
-
}
|
|
101
133
|
},
|
|
102
134
|
methods: {
|
|
103
135
|
/**
|
|
@@ -165,19 +197,10 @@ export default {
|
|
|
165
197
|
}
|
|
166
198
|
},
|
|
167
199
|
//Get datas from quizRecallData and userData and add them to quizRecall object
|
|
168
|
-
getQuizRecallAnswer() {
|
|
169
|
-
//
|
|
170
|
-
const {
|
|
171
|
-
|
|
172
|
-
pageId,
|
|
173
|
-
quizId,
|
|
174
|
-
hypertext_done,
|
|
175
|
-
hypertext_undone,
|
|
176
|
-
title,
|
|
177
|
-
titletag
|
|
178
|
-
} = this.quizRecallData
|
|
179
|
-
|
|
180
|
-
let userData = this.getPageInteraction(activityId, pageId)
|
|
200
|
+
async getQuizRecallAnswer(userData) {
|
|
201
|
+
await nextTick() //wait for the DOM to update
|
|
202
|
+
const { quizId, hypertext_done, hypertext_undone, title, titletag } =
|
|
203
|
+
this.quizRecallData
|
|
181
204
|
|
|
182
205
|
//Add hypertext_done and undone to quizRecall
|
|
183
206
|
if (hypertext_done) {
|
|
@@ -197,19 +220,21 @@ export default {
|
|
|
197
220
|
}
|
|
198
221
|
}
|
|
199
222
|
}
|
|
223
|
+
//Get the quiz answers from userData
|
|
224
|
+
const answers = userData.quizAnswers || {}
|
|
225
|
+
let quizAnswer = answers[quizId] || null
|
|
200
226
|
|
|
201
227
|
//If quiz answer exists in userData, quizRecall done is true and add the quiz answer to quizRecall
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const { quizAnswers } = userData.userInteraction
|
|
228
|
+
quizAnswer
|
|
229
|
+
? (this.quizRecall.done = true)
|
|
230
|
+
: (this.quizRecall.done = false)
|
|
206
231
|
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
this.quizRecall
|
|
212
|
-
this.
|
|
232
|
+
if (quizAnswer) {
|
|
233
|
+
this.quizRecall.answer = quizAnswer.value[0].filled || ''
|
|
234
|
+
this.quizRecall.ennonce = this.quizData.ennonce
|
|
235
|
+
}
|
|
236
|
+
this.quizRecall
|
|
237
|
+
this.isReady = true //Set isReady to true to display the component
|
|
213
238
|
},
|
|
214
239
|
|
|
215
240
|
//Validate quizRecallData
|
|
@@ -296,3 +321,25 @@ export default {
|
|
|
296
321
|
}
|
|
297
322
|
}
|
|
298
323
|
</script>
|
|
324
|
+
|
|
325
|
+
<style>
|
|
326
|
+
.skeleton {
|
|
327
|
+
height: 20px;
|
|
328
|
+
width: 200px;
|
|
329
|
+
background-color: #ccc;
|
|
330
|
+
border-radius: 4px;
|
|
331
|
+
animation: pulse 1.5s infinite ease-in-out;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@keyframes pulse {
|
|
335
|
+
0% {
|
|
336
|
+
opacity: 1;
|
|
337
|
+
}
|
|
338
|
+
50% {
|
|
339
|
+
opacity: 0.4;
|
|
340
|
+
}
|
|
341
|
+
100% {
|
|
342
|
+
opacity: 1;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
</style>
|
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<b-sidebar
|
|
3
|
-
id="sidebar-settings"
|
|
4
|
-
title="Settings"
|
|
5
|
-
right
|
|
6
|
-
shadow
|
|
7
|
-
backdrop
|
|
8
|
-
no-header
|
|
9
|
-
@hidden="onClose"
|
|
10
|
-
>
|
|
11
|
-
<div class="pl-3 pr-3">
|
|
12
|
-
<b-form-group :label="$t('user_settings.title')">
|
|
13
|
-
<b-form-checkbox
|
|
14
|
-
v-for="(setting, index) in Object.entries(settingsSelected)"
|
|
15
|
-
:key="index"
|
|
16
|
-
v-model="settingsSelected[setting[0]]"
|
|
17
|
-
:aria-describedby="ariaDescribedby"
|
|
18
|
-
:name="`parameter-${setting[0]}`"
|
|
19
|
-
:text-field="$t(`user_settings.${setting[0]}`)"
|
|
20
|
-
switch
|
|
21
|
-
stacked
|
|
22
|
-
size="lg"
|
|
23
|
-
@change="onSettingsChange"
|
|
24
|
-
>
|
|
25
|
-
<span>{{ $t(`user_settings.${setting[0]}`) }}</span>
|
|
26
|
-
</b-form-checkbox>
|
|
27
|
-
</b-form-group>
|
|
28
|
-
<app-base-button
|
|
29
|
-
v-b-toggle.sidebar-settings
|
|
30
|
-
class="float-right btn-primary"
|
|
31
|
-
:title="$t('user_settings.close')"
|
|
32
|
-
>
|
|
33
|
-
{{ $t('user_settings.close') }}
|
|
34
|
-
</app-base-button>
|
|
35
|
-
</div>
|
|
36
|
-
</b-sidebar>
|
|
37
|
-
</template>
|
|
38
|
-
|
|
39
|
-
<script>
|
|
40
|
-
import { mapState, mapActions } from 'pinia'
|
|
41
|
-
import { useAppStore } from '../module/stores/appStore'
|
|
42
|
-
import AppBaseButton from './AppBaseButton.vue'
|
|
43
|
-
|
|
44
|
-
export default {
|
|
45
|
-
components: { AppBaseButton },
|
|
46
|
-
props: {},
|
|
47
|
-
data() {
|
|
48
|
-
return {
|
|
49
|
-
settingsSelected: {},
|
|
50
|
-
settingsNeedUpdate: false
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
computed: {
|
|
54
|
-
...mapState(useAppStore, [
|
|
55
|
-
'getApplicationSettings',
|
|
56
|
-
'getModuleInfo',
|
|
57
|
-
'getUserInteraction',
|
|
58
|
-
'getConnectionInfo',
|
|
59
|
-
'onboardingEnabled'
|
|
60
|
-
]),
|
|
61
|
-
settingsOptions() {
|
|
62
|
-
return this.settingsOptionsELPlus
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
watch: {
|
|
66
|
-
getApplicationSettings: {
|
|
67
|
-
immediate: true,
|
|
68
|
-
deep: true,
|
|
69
|
-
handler() {
|
|
70
|
-
this.getAppSettingsInStore()
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
created() {
|
|
75
|
-
setTimeout(() => this.getAppSettingsInStore(), 1700)
|
|
76
|
-
|
|
77
|
-
this.$bus.$on('save-settings', (settings) => {
|
|
78
|
-
this.saveSettings(settings)
|
|
79
|
-
})
|
|
80
|
-
},
|
|
81
|
-
methods: {
|
|
82
|
-
...mapActions(useAppStore, [
|
|
83
|
-
'setApplicationSettings',
|
|
84
|
-
'updateUserMetaData'
|
|
85
|
-
]),
|
|
86
|
-
getAppSettingsInStore() {
|
|
87
|
-
this.settingsSelected = { ...this.getApplicationSettings }
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
//Save user settings to the store
|
|
91
|
-
onSettingsChange() {
|
|
92
|
-
this.settingsNeedUpdate = true
|
|
93
|
-
this.saveSettingsToStore(this.settingsSelected)
|
|
94
|
-
},
|
|
95
|
-
saveSettingsToStore(settings) {
|
|
96
|
-
this.setApplicationSettings(settings)
|
|
97
|
-
},
|
|
98
|
-
/**
|
|
99
|
-
* @description Save the user preferred settings to vue store and LRS or local store:
|
|
100
|
-
* LRS: Send a XAPI Statement to the LRS
|
|
101
|
-
* IndexedDB: Set a new Key for preferred settings in the userInteraction in store
|
|
102
|
-
* Update current value of settings
|
|
103
|
-
* @param {Object} appSettings - group of application settings {autoplay, subtitles, onboarding}
|
|
104
|
-
*
|
|
105
|
-
*/
|
|
106
|
-
saveSettings(appSettings) {
|
|
107
|
-
this.saveSettingsToStore(appSettings) //save to store
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
this.getModuleInfo.packageType === 'xapi' &&
|
|
111
|
-
this.getConnectionInfo &&
|
|
112
|
-
this.getConnectionInfo.remote
|
|
113
|
-
) {
|
|
114
|
-
let text
|
|
115
|
-
//Defining the text to display for stmt description and definition
|
|
116
|
-
switch (this.$i18n.locale) {
|
|
117
|
-
case 'fr':
|
|
118
|
-
if (this.getModuleInfo.courseID)
|
|
119
|
-
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
120
|
-
else text = `Le ${this.getModuleInfo.id}`
|
|
121
|
-
break
|
|
122
|
-
case 'en':
|
|
123
|
-
if (this.getModuleInfo.courseID)
|
|
124
|
-
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
125
|
-
else text = `The ${this.getModuleInfo.id}`
|
|
126
|
-
break
|
|
127
|
-
}
|
|
128
|
-
//Creating custom statement
|
|
129
|
-
const stmt = {
|
|
130
|
-
id: (() => {
|
|
131
|
-
return this.getModuleInfo.courseID
|
|
132
|
-
? this.getModuleInfo.courseID
|
|
133
|
-
: null
|
|
134
|
-
})(),
|
|
135
|
-
verb: 'preferred',
|
|
136
|
-
definition: text,
|
|
137
|
-
description: text,
|
|
138
|
-
extensions: [
|
|
139
|
-
{
|
|
140
|
-
id: 'application-settings',
|
|
141
|
-
content: {
|
|
142
|
-
userSettings: { ...appSettings }
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
]
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
setTimeout(() => {
|
|
149
|
-
this.$bus.$emit('send-xapi-statement', stmt) //send xapi statement
|
|
150
|
-
}, 1000)
|
|
151
|
-
} else {
|
|
152
|
-
this.$set(this.getUserInteraction, 'userSettings', appSettings) // adding the usersettings to user Interaction to save to save to the local DB
|
|
153
|
-
this.updateUserMetaData(this.getUserInteraction) //force update of the userInteraction to ensure saving of settings in local DB
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
//update sidebar data
|
|
157
|
-
this.getAppSettingsInStore()
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* @description -Action to perform when the sidebar closed- will require saving when changing occurred
|
|
162
|
-
*/
|
|
163
|
-
onClose() {
|
|
164
|
-
if (this.settingsNeedUpdate) {
|
|
165
|
-
this.saveSettingsToStore(this.settingsSelected)
|
|
166
|
-
this.saveSettings(this.getApplicationSettings)
|
|
167
|
-
this.settingsNeedUpdate = false
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<b-sidebar
|
|
3
|
+
id="sidebar-settings"
|
|
4
|
+
title="Settings"
|
|
5
|
+
right
|
|
6
|
+
shadow
|
|
7
|
+
backdrop
|
|
8
|
+
no-header
|
|
9
|
+
@hidden="onClose"
|
|
10
|
+
>
|
|
11
|
+
<div class="pl-3 pr-3">
|
|
12
|
+
<b-form-group :label="$t('user_settings.title')">
|
|
13
|
+
<b-form-checkbox
|
|
14
|
+
v-for="(setting, index) in Object.entries(settingsSelected)"
|
|
15
|
+
:key="index"
|
|
16
|
+
v-model="settingsSelected[setting[0]]"
|
|
17
|
+
:aria-describedby="ariaDescribedby"
|
|
18
|
+
:name="`parameter-${setting[0]}`"
|
|
19
|
+
:text-field="$t(`user_settings.${setting[0]}`)"
|
|
20
|
+
switch
|
|
21
|
+
stacked
|
|
22
|
+
size="lg"
|
|
23
|
+
@change="onSettingsChange"
|
|
24
|
+
>
|
|
25
|
+
<span>{{ $t(`user_settings.${setting[0]}`) }}</span>
|
|
26
|
+
</b-form-checkbox>
|
|
27
|
+
</b-form-group>
|
|
28
|
+
<app-base-button
|
|
29
|
+
v-b-toggle.sidebar-settings
|
|
30
|
+
class="float-right btn-primary"
|
|
31
|
+
:title="$t('user_settings.close')"
|
|
32
|
+
>
|
|
33
|
+
{{ $t('user_settings.close') }}
|
|
34
|
+
</app-base-button>
|
|
35
|
+
</div>
|
|
36
|
+
</b-sidebar>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script>
|
|
40
|
+
import { mapState, mapActions } from 'pinia'
|
|
41
|
+
import { useAppStore } from '../module/stores/appStore'
|
|
42
|
+
import AppBaseButton from './AppBaseButton.vue'
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
components: { AppBaseButton },
|
|
46
|
+
props: {},
|
|
47
|
+
data() {
|
|
48
|
+
return {
|
|
49
|
+
settingsSelected: {},
|
|
50
|
+
settingsNeedUpdate: false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
computed: {
|
|
54
|
+
...mapState(useAppStore, [
|
|
55
|
+
'getApplicationSettings',
|
|
56
|
+
'getModuleInfo',
|
|
57
|
+
'getUserInteraction',
|
|
58
|
+
'getConnectionInfo',
|
|
59
|
+
'onboardingEnabled'
|
|
60
|
+
]),
|
|
61
|
+
settingsOptions() {
|
|
62
|
+
return this.settingsOptionsELPlus
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
watch: {
|
|
66
|
+
getApplicationSettings: {
|
|
67
|
+
immediate: true,
|
|
68
|
+
deep: true,
|
|
69
|
+
handler() {
|
|
70
|
+
this.getAppSettingsInStore()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
created() {
|
|
75
|
+
setTimeout(() => this.getAppSettingsInStore(), 1700)
|
|
76
|
+
|
|
77
|
+
this.$bus.$on('save-settings', (settings) => {
|
|
78
|
+
this.saveSettings(settings)
|
|
79
|
+
})
|
|
80
|
+
},
|
|
81
|
+
methods: {
|
|
82
|
+
...mapActions(useAppStore, [
|
|
83
|
+
'setApplicationSettings',
|
|
84
|
+
'updateUserMetaData'
|
|
85
|
+
]),
|
|
86
|
+
getAppSettingsInStore() {
|
|
87
|
+
this.settingsSelected = { ...this.getApplicationSettings }
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
//Save user settings to the store
|
|
91
|
+
onSettingsChange() {
|
|
92
|
+
this.settingsNeedUpdate = true
|
|
93
|
+
this.saveSettingsToStore(this.settingsSelected)
|
|
94
|
+
},
|
|
95
|
+
saveSettingsToStore(settings) {
|
|
96
|
+
this.setApplicationSettings(settings)
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* @description Save the user preferred settings to vue store and LRS or local store:
|
|
100
|
+
* LRS: Send a XAPI Statement to the LRS
|
|
101
|
+
* IndexedDB: Set a new Key for preferred settings in the userInteraction in store
|
|
102
|
+
* Update current value of settings
|
|
103
|
+
* @param {Object} appSettings - group of application settings {autoplay, subtitles, onboarding}
|
|
104
|
+
*
|
|
105
|
+
*/
|
|
106
|
+
saveSettings(appSettings) {
|
|
107
|
+
this.saveSettingsToStore(appSettings) //save to store
|
|
108
|
+
|
|
109
|
+
if (
|
|
110
|
+
this.getModuleInfo.packageType === 'xapi' &&
|
|
111
|
+
this.getConnectionInfo &&
|
|
112
|
+
this.getConnectionInfo.remote
|
|
113
|
+
) {
|
|
114
|
+
let text
|
|
115
|
+
//Defining the text to display for stmt description and definition
|
|
116
|
+
switch (this.$i18n.locale) {
|
|
117
|
+
case 'fr':
|
|
118
|
+
if (this.getModuleInfo.courseID)
|
|
119
|
+
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
120
|
+
else text = `Le ${this.getModuleInfo.id}`
|
|
121
|
+
break
|
|
122
|
+
case 'en':
|
|
123
|
+
if (this.getModuleInfo.courseID)
|
|
124
|
+
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
125
|
+
else text = `The ${this.getModuleInfo.id}`
|
|
126
|
+
break
|
|
127
|
+
}
|
|
128
|
+
//Creating custom statement
|
|
129
|
+
const stmt = {
|
|
130
|
+
id: (() => {
|
|
131
|
+
return this.getModuleInfo.courseID
|
|
132
|
+
? this.getModuleInfo.courseID
|
|
133
|
+
: null
|
|
134
|
+
})(),
|
|
135
|
+
verb: 'preferred',
|
|
136
|
+
definition: text,
|
|
137
|
+
description: text,
|
|
138
|
+
extensions: [
|
|
139
|
+
{
|
|
140
|
+
id: 'application-settings',
|
|
141
|
+
content: {
|
|
142
|
+
userSettings: { ...appSettings }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
this.$bus.$emit('send-xapi-statement', stmt) //send xapi statement
|
|
150
|
+
}, 1000)
|
|
151
|
+
} else {
|
|
152
|
+
this.$set(this.getUserInteraction, 'userSettings', appSettings) // adding the usersettings to user Interaction to save to save to the local DB
|
|
153
|
+
this.updateUserMetaData(this.getUserInteraction) //force update of the userInteraction to ensure saving of settings in local DB
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
//update sidebar data
|
|
157
|
+
this.getAppSettingsInStore()
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @description -Action to perform when the sidebar closed- will require saving when changing occurred
|
|
162
|
+
*/
|
|
163
|
+
onClose() {
|
|
164
|
+
if (this.settingsNeedUpdate) {
|
|
165
|
+
this.saveSettingsToStore(this.settingsSelected)
|
|
166
|
+
this.saveSettings(this.getApplicationSettings)
|
|
167
|
+
this.settingsNeedUpdate = false
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
</script>
|