diff --git a/kolibri/plugins/learn/assets/src/views/LibraryPage/index.vue b/kolibri/plugins/learn/assets/src/views/LibraryPage/index.vue index f27dc398866..1161825853e 100644 --- a/kolibri/plugins/learn/assets/src/views/LibraryPage/index.vue +++ b/kolibri/plugins/learn/assets/src/views/LibraryPage/index.vue @@ -266,8 +266,9 @@ const currentInstance = getCurrentInstance().proxy; const store = currentInstance.$store; const router = currentInstance.$router; - const { tourActive, isTourActive, startTour, endTour, resumeTour } = useTour(); - const { isUserLoggedIn, isCoach, isAdmin, isSuperuser, isLearner, user_id } = useUser(); + const { tourActive, isTourActive, startTour, endTour, resumeTour, waitForStepElements } = + useTour(); + const { isUserLoggedIn, isCoach, isAdmin, isSuperuser, isLearner, currentUserId } = useUser(); const { allowDownloadOnMeteredConnection } = useDeviceSettings(); const { @@ -434,7 +435,8 @@ startTour, endTour, resumeTour, - userId: user_id, + currentUserId, + waitForStepElements, }; }, props: { @@ -466,7 +468,7 @@ welcomeModalVisible() { return ( this.welcomeModalVisibleState && - window.localStorage.getItem(`${welcomeDismissalKey}-${this.userId}`) !== 'true' + window.localStorage.getItem(`${welcomeDismissalKey}-${this.currentUserId}`) !== 'true' ); }, showOtherLibraries() { @@ -543,11 +545,15 @@ }, loading(newVal, oldVal) { if (oldVal && !newVal) { - const isTourStarted = this.resumeTour(this.userId, 'LibraryPage'); + const isTourStarted = this.resumeTour(this.currentUserId, 'LibraryPage'); if (isTourStarted) { - setTimeout(() => { - this.startTour('LibraryPage'); - }, 3000); + this.waitForStepElements('LibraryPage') + .then(() => { + this.startTour('LibraryPage'); + }) + .catch(error => { + this.$store.dispatch('handleApiError', { error }); + }); } } }, @@ -555,7 +561,9 @@ created() { const welcomeDismissalKey = 'DEVICE_WELCOME_MODAL_DISMISSED'; - if (window.sessionStorage.getItem(`${welcomeDismissalKey}-${this.userId}`) !== 'true') { + if ( + window.sessionStorage.getItem(`${welcomeDismissalKey}-${this.currentUserId}`) !== 'true' + ) { this.$store.commit('SET_WELCOME_MODAL_VISIBLE', true); } @@ -573,7 +581,7 @@ }, methods: { hideWelcomeModal() { - window.localStorage.setItem(`${welcomeDismissalKey}-${this.userId}`, true); + window.localStorage.setItem(`${welcomeDismissalKey}-${this.currentUserId}`, true); this.$store.commit('SET_WELCOME_MODAL_VISIBLE', false); this.startTour('LibraryPage'); }, diff --git a/packages/kolibri/components/onboarding/TooltipTour.vue b/packages/kolibri/components/onboarding/TooltipTour.vue index b4086f213ca..e021361461c 100644 --- a/packages/kolibri/components/onboarding/TooltipTour.vue +++ b/packages/kolibri/components/onboarding/TooltipTour.vue @@ -31,12 +31,12 @@ name: 'TooltipTour', setup() { const { saveTourProgress, completeTour, currentStepIndex } = useTour(); - const { user_id } = useUser(); + const { currentUserId } = useUser(); return { saveTourProgress, completeTour, currentStepIndex, - userId: user_id, + currentUserId, }; }, props: { @@ -164,11 +164,11 @@ nextStep() { if (this.currentStepIndex < this.steps.length - 1) { this.currentStepIndex++; - this.saveTourProgress(this.userId, this.page, this.currentStepIndex, true); + this.saveTourProgress(this.currentUserId, this.page, this.currentStepIndex, true); this.showTooltip(); } else { // Check if current page is the last key in onboardingSteps - this.saveTourProgress(this.userId, this.page, this.currentStepIndex, false); + this.saveTourProgress(this.currentUserId, this.page, this.currentStepIndex, false); const pageKeys = Object.keys(onboardingSteps); const isLastPage = this.page === pageKeys[pageKeys.length - 1]; if (isLastPage) { @@ -180,7 +180,7 @@ prevStep() { if (this.currentStepIndex > 0) { this.currentStepIndex--; - this.saveTourProgress(this.userId, this.page, this.currentStepIndex, true); + this.saveTourProgress(this.currentUserId, this.page, this.currentStepIndex, true); this.showTooltip(); } }, diff --git a/packages/kolibri/composables/useTour.js b/packages/kolibri/composables/useTour.js index 44faa29b71a..dbfcd9c4a30 100644 --- a/packages/kolibri/composables/useTour.js +++ b/packages/kolibri/composables/useTour.js @@ -49,6 +49,7 @@ function completeTour() { function isTourCompleted() { return localStorage.getItem(TOUR_COMPLETE_KEY) === 'true'; } + function resumeTour(userId, page) { const progress = getTourProgress(userId); if ((progress && progress.isTourActive === false) || !progress) { @@ -56,7 +57,8 @@ function resumeTour(userId, page) { } const pageKeys = Object.keys(onboardingSteps); const currentPageIndex = pageKeys.indexOf(page); - const welcomeDismissed = localStorage.getItem('DEVICE_WELCOME_MODAL_DISMISSED'); + const welcomeDismissalKey = 'DEVICE_WELCOME_MODAL_DISMISSED'; + const welcomeDismissed = localStorage.getItem(`${welcomeDismissalKey}-${userId}`); const prevPage = currentPageIndex === 0 ? null : pageKeys[currentPageIndex - 1]; const prevPageSteps = prevPage ? onboardingSteps[prevPage].steps : []; const isLastStepOfPrevPage = prevPageSteps && progress.stepIndex === prevPageSteps.length - 1; @@ -78,6 +80,34 @@ function resumeTour(userId, page) { return false; } +function waitForStepElements(page) { + return new Promise((resolve, reject) => { + const steps = onboardingSteps[page]?.steps || []; + if (!steps.length) { + return reject(new Error(`No steps found for page: ${page}`)); + } + + const allAvailable = () => + steps.every(step => document.querySelector(`[data-onboarding-id="${step.key}"]`)); + + if (allAvailable()) { + return resolve(true); + } + + const observer = new MutationObserver(() => { + if (allAvailable()) { + observer.disconnect(); + resolve(true); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); + }); +} + export default function useTour() { return { tourActive, @@ -91,5 +121,6 @@ export default function useTour() { tourActiveMap, resumeTour, currentStepIndex, + waitForStepElements, }; }