diff --git a/.github/workflows/deploy_to_ecs_prod.yml b/.github/workflows/deploy_to_ecs_prod.yml index ef050ff76..85c6dae04 100644 --- a/.github/workflows/deploy_to_ecs_prod.yml +++ b/.github/workflows/deploy_to_ecs_prod.yml @@ -53,6 +53,7 @@ jobs: VUE_APP_MIXPANEL_PROJECT_TOKEN: ${{ secrets.VUE_APP_MIXPANEL_PROJECT_TOKEN }} VUE_APP_SENTRY_DSN: ${{ secrets.VUE_APP_SENTRY_DSN }} VUE_APP_GOOGLE_API_KEY: ${{ secrets.VUE_APP_GOOGLE_API_KEY }} + NODE_OPTIONS: --openssl-legacy-provider run: | # Build a docker container and push it to ECR docker build \ diff --git a/Dockerfile b/Dockerfile index 44de8f7f2..45f3d19dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ # base stage -FROM node:lts-alpine as base-stage +# FROM node:lts-alpine as base-stage +FROM node:16-alpine as base-stage WORKDIR /app COPY package.json ./ # install dependencies for npm run test:unit diff --git a/src/App.vue b/src/App.vue index 8462d2aa6..b7be5acff 100644 --- a/src/App.vue +++ b/src/App.vue @@ -173,7 +173,17 @@
this.createSession()) .then(() => this.logData()); diff --git a/src/services/Config/GlobalDefaultSettings.js b/src/services/Config/GlobalDefaultSettings.js index c65493166..6a31ee0a8 100644 --- a/src/services/Config/GlobalDefaultSettings.js +++ b/src/services/Config/GlobalDefaultSettings.js @@ -16,6 +16,11 @@ export let settingsMetadata = { description: "settings.menu.description.skipEnabled", type: "checkbox", }, + firstTimeLanguagePickerPopup: { + title: "settings.menu.title.firstTimeLanguagePickerPopup", + description: "settings.menu.description.firstTimeLanguagePickerPopup", + type: "checkbox", + }, }; /** @@ -51,6 +56,17 @@ let globalDefaultSetings = new Map( }) ), }, + ui: { + scope: ["org-admin", "super-admin"], + children: new Map( + Object.entries({ + firstTimeLanguagePickerPopup: { + scope: ["org-admin", "super-admin"], + value: true, + }, + }) + ), + }, }) ), }, diff --git a/src/services/Config/User.js b/src/services/Config/User.js index cdd831b23..6cffb0a24 100644 --- a/src/services/Config/User.js +++ b/src/services/Config/User.js @@ -22,7 +22,7 @@ export default { userConfig["locale"] || process.env.VUE_APP_I18N_LOCALE; clearInterval(redirectId); } - }, 10); + }, 50); }, updateLocale() { diff --git a/src/services/Functional/Utilities/Settings.js b/src/services/Functional/Utilities/Settings.js index 7aa0f51b0..a3bcc60d1 100644 --- a/src/services/Functional/Utilities/Settings.js +++ b/src/services/Functional/Utilities/Settings.js @@ -50,23 +50,47 @@ export default { }, /** - * checks if the provided object is a valid settings object + * checks if the provided object is a valid settings object and patches it if it is not * @param {Object} config - the object that needs to be checked for validity - * @param {Array} keysToCheck - these keys should exist for the object to be valid - * @returns {Boolean} - if the given config object contains valid settings + * @returns {Array} - returns a boolean indicating if the object is valid and a Map object, null otherwise */ - hasValidSettings(config, keysToCheck = ["player"]) { + patchInvalidIncompleteSettings(config) { // settings key should be present inside config object if (config == null || !("settings" in config) || config.settings == null) - return false; + // return [false, null]; + return [true, clonedeep(globalDefaultSettings)] // decoded settings object should be an instance of Map let decodedSettings = this.decodeMapFromPayload(clonedeep(config.settings)); - if (!(decodedSettings instanceof Map)) return false; + if (!(decodedSettings instanceof Map)) return [ + true, + clonedeep(globalDefaultSettings) + ]; // certain keys should be present in the settings Map - for (const key of keysToCheck) if (!decodedSettings.has(key)) return false; - return true; + + const defaultSettings = clonedeep(globalDefaultSettings) + for (let [headerName, headerDetails] of defaultSettings) { + if (!decodedSettings.has(headerName)) { + decodedSettings.set(headerName, headerDetails) + continue + } + + for (let [tabName, tabDetails] of headerDetails.children) { + if (!decodedSettings.get(headerName).children.has(tabName)) { + decodedSettings.get(headerName).children.set(tabName, tabDetails) + continue + } + + for (let [leafName, leafDetails] of tabDetails.children) { + if (!decodedSettings.get(headerName).children.get(tabName).children.has(leafName)) { + decodedSettings.get(headerName).children.get(tabName).children.set(leafName, leafDetails) + } + } + } + } + + return [true, decodedSettings] }, /** @@ -75,21 +99,9 @@ export default { * @param {Object} config - Config of a plio */ setPlioSettings(config) { - let plioSettings = new Map(); - if (!this.hasValidSettings(config)) { - // if the provided config is not valid, set plio's settings using the global defaults - plioSettings.set( - "player", - clonedeep(globalDefaultSettings.get("player")) - ); - } else { - // if the provided config is valid, use it to set plio's settings - plioSettings.set( - "player", - this.decodeMapFromPayload(clonedeep(config.settings)).get("player") - ); - } - return plioSettings; + const result = this.patchInvalidIncompleteSettings(config) + if (result[0] == true) return result[1] + return null }, /** diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js index bcd456aee..9474335ee 100644 --- a/src/store/modules/auth.js +++ b/src/store/modules/auth.js @@ -138,12 +138,21 @@ const actions = { state.accessToken.access_token ); if (response != undefined) { - // use the config of a user if it exists otherwise use the global defaults - if ("settings" in response.data.config) - dispatch( - "setUserSettings", - SettingsUtilities.decodeMapFromPayload(response.data.config.settings) - ); + // use the config of a user (and patch for completeness) if it exists otherwise use the global defaults + if ("settings" in response.data.config) { + const result = SettingsUtilities.patchInvalidIncompleteSettings(response.data.config) + if (result[1] !== null) { + dispatch( + "setUserSettings", + result[1] + ); + } else { + dispatch( + "setUserSettings", + SettingsUtilities.decodeMapFromPayload(response.data.config.settings) + ); + } + } else dispatch("setUserSettings", clonedeep(globalDefaultSettings)); // use the config of organization(s) if it exists otherwise use the global defaults @@ -240,31 +249,40 @@ export default { function getWorkspaceSettings(workspaceDetails = null) { if ( workspaceDetails == null || - !("config" in workspaceDetails) || - !SettingsUtilities.hasValidSettings(workspaceDetails.config) + !("config" in workspaceDetails) ) { - let workspaceSettings = clonedeep(globalDefaultSettings); - for (let [headerName, headerDetails] of workspaceSettings) { - if (!SettingsUtilities.isSettingApplicableToWorkspace(headerDetails)) { - workspaceSettings.delete(headerName); - continue; - } - for (let [tabName, tabDetails] of headerDetails.children) { - if (!SettingsUtilities.isSettingApplicableToWorkspace(tabDetails)) { - headerDetails.children.delete(tabName); + const result = SettingsUtilities.patchInvalidIncompleteSettings(workspaceDetails.config) + if (result[0] == true) { + let workspaceSettings = result[1] + for (let [headerName, headerDetails] of workspaceSettings) { + if (!SettingsUtilities.isSettingApplicableToWorkspace(headerDetails)) { + workspaceSettings.delete(headerName); continue; } - for (let [leafName, leafDetails] of tabDetails.children) { - if (!SettingsUtilities.isSettingApplicableToWorkspace(leafDetails)) { - tabDetails.children.delete(leafName); + for (let [tabName, tabDetails] of headerDetails.children) { + if (!SettingsUtilities.isSettingApplicableToWorkspace(tabDetails)) { + headerDetails.children.delete(tabName); continue; } + for (let [leafName, leafDetails] of tabDetails.children) { + if (!SettingsUtilities.isSettingApplicableToWorkspace(leafDetails)) { + tabDetails.children.delete(leafName); + continue; + } + } } } + + return workspaceSettings; } - return workspaceSettings; } - return SettingsUtilities.decodeMapFromPayload( - workspaceDetails.config.settings - ); + + const result = SettingsUtilities.patchInvalidIncompleteSettings(workspaceDetails.config) + if (result[1] !== null) { + return result[1]; + } else { + return SettingsUtilities.decodeMapFromPayload( + workspaceDetails.config.settings + ); + } } diff --git a/src/store/modules/generic.js b/src/store/modules/generic.js index 6368aa53e..23a4b85e6 100644 --- a/src/store/modules/generic.js +++ b/src/store/modules/generic.js @@ -1,4 +1,5 @@ const state = { + isFirstTimeLanguagePickerShownBySetting: null, isSharePlioDialogShown: false, isEmbedPlioDialogShown: false, // whether to show the dialog with info on embedding plio plioLinkToShare: null, @@ -22,6 +23,9 @@ const getters = { isTabScreen: (state) => { return state.windowInnerWidth < 640; }, + isFirstTimeLanguagePickerShownBySetting: (state) => { + return state.isFirstTimeLanguagePickerShownBySetting; + }, }; const actions = { showSharePlioDialog({ commit, dispatch }, plioLink) { @@ -59,6 +63,12 @@ const actions = { hideSpinner({ commit }) { commit("hideSpinner"); }, + setFirstTimeLanguagePickerShownBySetting({ commit }) { + commit("setFirstTimeLanguagePickerShownBySetting"); + }, + unsetFirstTimeLanguagePickerShownBySetting({ commit }) { + commit("unsetFirstTimeLanguagePickerShownBySetting"); + }, }; const mutations = { @@ -92,6 +102,12 @@ const mutations = { hideSpinner(state) { state.isSpinnerShown = false; }, + setFirstTimeLanguagePickerShownBySetting(state) { + state.isFirstTimeLanguagePickerShownBySetting = true; + }, + unsetFirstTimeLanguagePickerShownBySetting(state) { + state.isFirstTimeLanguagePickerShownBySetting = false; + }, }; export default {