Skip to content

Commit 37e26c0

Browse files
committed
allow user to save back up words from tour
- it will go automatically to next step
1 parent 7d2bc2b commit 37e26c0

File tree

6 files changed

+73
-40
lines changed

6 files changed

+73
-40
lines changed

src/components/Tour.vue

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<template>
22
<div class="tour">
33
<v-tour
4-
name="nimiq-tour"
5-
:steps="steps.map((s) => s.tooltip)"
6-
:options="tourOptions"
4+
name="nimiq-tour"
5+
:steps="vTourSteps"
6+
:options="tourOptions"
77
>
88
<template v-slot="tour">
99
<transition name="fade">
@@ -92,7 +92,7 @@
9292
{{ tour.steps[tour.currentStep].button.text }}
9393
</button>
9494
<button
95-
v-else-if="isLargeScreen && !isLoading"
95+
v-else-if="isLargeScreen && !disableNextStep && !isLoading"
9696
class="right"
9797
@click="goToNextStep()"
9898
tabindex="0"
@@ -212,20 +212,13 @@ export default defineComponent({
212212
useKeyboardNavigation: false, // handled by us
213213
};
214214
215-
// `getTour` function returns an object like:
216-
// { "1": { /** First step */}, "10": { /** Step */}, "2": { /** Second step */}, ... }
217-
// where the key is the step index and the value is the step object that we need to sort
218-
// and store it as an array
219-
let unsortedStepds = getTour(
220-
accountStore.tour?.name, context, { isSmallScreen, isMediumScreen, isLargeScreen });
221-
let steps = Object.keys(unsortedStepds)
222-
.sort((a, b) => (a as unknown as number) - (b as unknown as number))
223-
.map((key) => unsortedStepds[key as unknown as TourStepIndex]);
224-
225215
// Initial state
216+
const steps: Ref<ITourStep[]> = ref([]);
217+
setTourAsArray();
218+
226219
const isLoading = ref(true);
227220
const currentStep: Ref<TourStepIndex> = ref(0);
228-
const nSteps: Ref<number> = ref(Object.keys(steps).length);
221+
const nSteps: Ref<number> = ref(steps.value.length);
229222
const disableNextStep = ref(true);
230223
const showTour = ref(false);
231224
@@ -256,7 +249,7 @@ export default defineComponent({
256249
257250
await context.root.$nextTick(); // to ensure DOM is ready
258251
259-
const step = steps[currentStep.value];
252+
const step = steps.value[currentStep.value];
260253
if (!step) return;
261254
262255
// Update state
@@ -316,7 +309,7 @@ export default defineComponent({
316309
// execute _toggleDisabledButtons but it is kind of random the amount of time
317310
// it takes to render the button. I don't know how to fix it. Waiting 500ms works.
318311
await sleep(500);
319-
_toggleDisabledButtons(steps[currentStep.value]?.ui.disabledButtons, true);
312+
_toggleDisabledButtons(steps.value[currentStep.value]?.ui.disabledButtons, true);
320313
});
321314
322315
function goToPrevStep() {
@@ -335,8 +328,8 @@ export default defineComponent({
335328
newStepIndex: TourStepIndex,
336329
goingForward: boolean,
337330
) {
338-
const { path: currentPath } = steps[currentStepIndex]!;
339-
const { path: newPath, ui: newUI, lifecycle: newLifecycle } = steps[newStepIndex]!;
331+
const { path: currentPath } = steps.value[currentStepIndex]!;
332+
const { path: newPath, ui: newUI, lifecycle: newLifecycle } = steps.value[newStepIndex]!;
340333
341334
tour!.stop();
342335
await sleep(500); // ensures animation ends
@@ -353,6 +346,7 @@ export default defineComponent({
353346
await context.root.$nextTick();
354347
}
355348
349+
_toggleDisabledButtons(steps.value[currentStepIndex].ui.disabledButtons, false);
356350
_updateUI(newUI, newStepIndex);
357351
await context.root.$nextTick();
358352
@@ -407,7 +401,7 @@ export default defineComponent({
407401
const tourInteractableElements = ['.tour', '.tour-manager', '.tooltip'];
408402
409403
// This are the elements that are allowed to be clicked only in current step
410-
const stepInteractableElements = steps[currentStep.value]?.ui.explicitInteractableElements || [];
404+
const stepInteractableElements = steps.value[currentStep.value]?.ui.explicitInteractableElements || [];
411405
412406
const interactableElements = tourInteractableElements.concat(stepInteractableElements)
413407
.map((s) => $(s)).filter((e) => !!e) as HTMLElement[];
@@ -504,8 +498,6 @@ export default defineComponent({
504498
$$(`[data-explicit-interactable="${stepIndex}"]`).forEach((el) => {
505499
el.removeAttribute('data-explicit-interactable');
506500
});
507-
508-
_toggleDisabledButtons(steps[stepIndex].ui.disabledButtons, false);
509501
}
510502
511503
// nofifyManager - if true will notify the manager that the tour has ended
@@ -582,11 +574,7 @@ export default defineComponent({
582574
async function _screenTypeChanged() {
583575
tour!.stop();
584576
585-
unsortedStepds = getTour(
586-
accountStore.tour?.name, context, { isSmallScreen, isMediumScreen, isLargeScreen });
587-
steps = Object.keys(unsortedStepds)
588-
.sort((a, b) => (a as unknown as number) - (b as unknown as number))
589-
.map((key) => unsortedStepds[key as unknown as TourStepIndex]);
577+
setTourAsArray();
590578
591579
// end tour sofly and start it again
592580
await endTour(false, true);
@@ -614,6 +602,10 @@ export default defineComponent({
614602
});
615603
}
616604
605+
function setTourAsArray() {
606+
steps.value = getTour(accountStore.tour?.name, context, { isSmallScreen, isMediumScreen, isLargeScreen });
607+
}
608+
617609
return {
618610
isSmallScreen,
619611
isMediumScreen,
@@ -622,7 +614,7 @@ export default defineComponent({
622614
623615
// tour
624616
tourOptions,
625-
steps,
617+
vTourSteps: computed(() => steps.value.map((s) => s.tooltip)),
626618
showTour,
627619
628620
// control bar
@@ -635,6 +627,10 @@ export default defineComponent({
635627
goToPrevStep,
636628
goToNextStep,
637629
endTour,
630+
631+
// We need to expose this function so that we can call it from the steps.
632+
// In particular /lib/tour/onboarding/06_0_BackupAlertStep.ts
633+
setTourAsArray,
638634
};
639635
},
640636
components: {
@@ -812,10 +808,14 @@ export default defineComponent({
812808
font-size: 14px;
813809
border: 1px solid transparent;
814810
815-
&:focus {
811+
&.right:focus {
816812
border: 1px solid #ffffff55;
817813
}
818814
815+
&.left:focus {
816+
opacity: 1;
817+
}
818+
819819
&.left {
820820
display: flex;
821821
align-items: center;

src/lib/tour/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,19 @@ export function getTour(tour: TourName | undefined, context: SetupContext, scree
4040
default:
4141
}
4242

43-
return Object.values(steps).filter((step) => Boolean(step)) as ITourStep[];
43+
// At this moment we have something like this:
44+
// {
45+
// "1": { /** First step object */},
46+
// "10": { /** This is not the first step object!! */},
47+
// "2": { /** Second step object */},
48+
// ...
49+
// }
50+
// where the key is the step index and the value is the step object that we need to sort
51+
// and store it as an array
52+
return Object.entries(steps)
53+
.filter(([, s]) => Boolean(s)) // Remove undefined steps
54+
.sort(([a], [b]) => parseInt(a, 10) - parseInt(b, 10)) // Sort by key
55+
.map(([, s]) => s) as ITourStep[]; // Just return only the step
4456
}
4557

4658
// Finds the component instance given its name in the Vue tree

src/lib/tour/onboarding/03_FirstTransactionStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function getFirstTransactionStep({ isSmallScreen, txsLen }: IOnboardingGe
5050
: '.vue-recycle-scroller__item-view:nth-child(2)'}`;
5151
},
5252
content: getOnboardingTexts(OnboardingTourStep.FIRST_TRANSACTION)[
53-
txsLen.value === 1 ? 'default' : 'alternative'],
53+
txsLen.value <= 1 ? 'default' : 'alternative'],
5454
params: {
5555
get placement() {
5656
return isSmallScreen.value ? 'bottom-start' : 'left';

src/lib/tour/onboarding/06_0_BackupAlertStep.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { defaultTooltipModifiers, ITourOrigin, IWalletHTMLElements } from '..';
2-
import { IOnboardingGetStepFnArgs, OnboardingTourStep, ITourStep } from '../types';
1+
import { useAccountStore } from '@/stores/Account';
2+
import { watch } from '@vue/composition-api';
3+
import { defaultTooltipModifiers, ITourOrigin, IWalletHTMLElements, searchComponentByName } from '..';
4+
import { IOnboardingGetStepFnArgs, ITourStep, OnboardingTourStep } from '../types';
35
import { getOnboardingTexts } from './OnboardingTourTexts';
46

57
export function getBackupAlertStep(
6-
{ isSmallScreen, startedFrom, toggleHighlightButton }: IOnboardingGetStepFnArgs): ITourStep {
8+
{ isSmallScreen, startedFrom, toggleHighlightButton, root }: IOnboardingGetStepFnArgs): ITourStep {
79
const ui: ITourStep['ui'] = {
810
fadedElements: [
911
IWalletHTMLElements.SIDEBAR_TESTNET,
@@ -75,9 +77,28 @@ export function getBackupAlertStep(
7577
ui,
7678
lifecycle: {
7779
mounted: () => {
80+
const unwatch = watch(useAccountStore().activeAccountInfo, async (newVal) => {
81+
if (newVal?.wordsExported) {
82+
// If user clicks "save recover words" and completes the process, we
83+
// no longer need to show the user this step, therefore, we execute
84+
// setTourAsArray again, and go to the next step which in reality will have
85+
// same index
86+
const nimiqTourInstance = searchComponentByName(root, 'nimiq-tour') as any;
87+
if (!nimiqTourInstance) return;
88+
89+
nimiqTourInstance.setTourAsArray();
90+
nimiqTourInstance.currentStep -= 1;
91+
92+
setTimeout(() => nimiqTourInstance.goToNextStep(), 500);
93+
}
94+
});
95+
7896
// hightlight 'Revover words' button
7997
toggleHighlightButton(IWalletHTMLElements.BUTTON_ADDRESS_BACKUP_ALERT, true, 'orange');
80-
return () => toggleHighlightButton(IWalletHTMLElements.BUTTON_ADDRESS_BACKUP_ALERT, false, 'orange');
98+
return () => {
99+
unwatch();
100+
toggleHighlightButton(IWalletHTMLElements.BUTTON_ADDRESS_BACKUP_ALERT, false, 'orange');
101+
};
81102
},
82103
},
83104
} as ITourStep;

src/lib/tour/onboarding/OnboardingTourTexts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const texts: ITourStepTexts<OnboardingTourStep> = {
2222
alternative: [$t('This is where all your transactions will appear.')],
2323
},
2424
[OnboardingTourStep.FIRST_TRANSACTION]: {
25-
// If user has 0 or 1 tx
25+
// If user has 1 tx
2626
default: [
2727
$t('Here’s your first transaction with your first NIM.'),
2828
$t('Every NIM address comes with an avatar. They help to make sure you got the right one.'),

src/lib/tour/onboarding/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export function getOnboardingTourSteps({ root }: SetupContext, screenTypes: Scre
3333

3434
const { state, activeAccountInfo } = useAccountStore();
3535
const { startedFrom } = (state.tour as { startedFrom: ITourOrigin });
36-
const { type: accountType, wordsExported } = activeAccountInfo.value || {};
37-
const accountIsSecured = accountType === AccountType.BIP39 && !!wordsExported;
36+
const accountIsSecured = computed(() =>
37+
activeAccountInfo.value?.type === AccountType.BIP39 && activeAccountInfo.value?.wordsExported);
3838

3939
const args: IOnboardingGetStepFnArgs = {
4040
sleep,
@@ -53,7 +53,7 @@ export function getOnboardingTourSteps({ root }: SetupContext, screenTypes: Scre
5353
[OnboardingTourStep.WALLET_BALANCE]: getWalletBalanceStep(args),
5454
get [OnboardingTourStep.BACKUP_ALERT]() {
5555
// if the user has already backed up their account, skip this step
56-
return !accountIsSecured ? getBackupAlertStep(args) : undefined;
56+
return !accountIsSecured.value ? getBackupAlertStep(args) : undefined;
5757
},
5858
get [OnboardingTourStep.MENU_ICON]() {
5959
// show only this step if it is a new user and is not in a large screen
@@ -63,7 +63,7 @@ export function getOnboardingTourSteps({ root }: SetupContext, screenTypes: Scre
6363
[OnboardingTourStep.ACCOUNT_OPTIONS]: getAccountOptionsStep(args),
6464
get [OnboardingTourStep.BACKUP_OPTION_FROM_OPTIONS]() {
6565
// if the user has already backed up their account, remind the user that he can backup anytime
66-
if (!accountIsSecured) return undefined;
66+
if (!accountIsSecured.value) return undefined;
6767
return (screenTypes.isLargeScreen.value)
6868
? getBackupOptionLargeScreenStep()
6969
: getBackupOptionNotLargeScreenStep(args);

0 commit comments

Comments
 (0)