Skip to content

Commit d0ee499

Browse files
committed
added store state for tour
1 parent b670447 commit d0ee499

File tree

5 files changed

+142
-59
lines changed

5 files changed

+142
-59
lines changed

src/App.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<div id="app" :class="{'value-masked': amountsHidden}">
33
<!-- (?) This could be moved to Groundfloor.vue -->
4-
<transition v-if="!!$route.params.tourName" name="delay">
5-
<Tour :tourName="$route.params.tourName" />
4+
<transition v-if="showTour" name="delay">
5+
<Tour/>
66
</transition>
77

88
<main :class="routeClass" ref="$main">
@@ -77,7 +77,13 @@ export default defineComponent({
7777
}
7878
});
7979
80-
const { accountInfos } = useAccountStore();
80+
const { accountInfos, state: accountState, removeTour } = useAccountStore();
81+
if (!['root', 'transactions'].includes(context.root.$route.name as string)
82+
&& accountState.tour?.name === 'onboarding') {
83+
removeTour();
84+
}
85+
const showTour = computed(() => !!accountState.tour);
86+
8187
// Convert result of computation to boolean, to not trigger rerender when number of accounts changes above 0.
8288
const hasAccounts = computed(() => Boolean(Object.keys(accountInfos.value).length));
8389
@@ -170,6 +176,7 @@ export default defineComponent({
170176
});
171177
172178
return {
179+
showTour,
173180
routeClass,
174181
hasAccounts,
175182
amountsHidden,

src/components/Tour.vue

Lines changed: 93 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<template>
2-
<div>
2+
<div class="tour">
33
<v-tour
4-
class="tour"
54
name="nimiq-tour"
65
:steps="Object.values(steps).map((s) => s.tooltip)"
76
>
@@ -20,34 +19,37 @@
2019
:is-last="tour.isLast"
2120
:labels="tour.labels"
2221
>
23-
<div class="content" slot="content">
22+
<div slot="content" class="content">
2423
<p
2524
v-for="(content, i) in tour.steps[tour.currentStep].content"
2625
:key="i"
27-
v-html="content"
26+
v-html="$t(content)"
2827
></p>
2928
<!-- TODO REMOVE ME -->
3029
<div class="remove_me" v-if="currentStep === 1" @click="simulate()">
3130
Simulate Receive NIM
3231
</div>
3332
</div>
34-
<div slot="actions">
35-
<template v-if="!isMobile">
36-
<button @click="tour.previousStep">
37-
{{ $t("Previous step") }}
38-
</button>
39-
<button @click="tour.goToNextStep">
40-
{{ $t("Next step") }}
41-
</button>
42-
</template>
33+
<div slot="actions" class="actions">
34+
<button @click="tour.previousStep" v-if="!isMobile">
35+
{{ $t("Previous step") }}
36+
</button>
37+
<button class="right" @click="alert"
38+
v-if="tour.steps[tour.currentStep].button">
39+
{{ $t(tour.steps[tour.currentStep].button.text) }}
40+
</button>
41+
<button class="right" @click="tour.goToNextStep"
42+
v-else-if="!isMobile">
43+
{{ $t("Next step") }}
44+
</button>
4345
</div>
4446
</v-step>
4547
</transition>
4648
</template>
4749
</v-tour>
4850
<transition name="fade">
4951
<div class="tour-control-bar">
50-
<button disabled>
52+
<button @click="endTour()">
5153
{{ $t("End Tour") }}
5254
</button>
5355
<span class="progress">
@@ -82,6 +84,7 @@
8284
</template>
8385

8486
<script lang="ts">
87+
import { useAccountStore } from '@/stores/Account';
8588
import { useNetworkStore } from '@/stores/Network';
8689
import { useTransactionsStore } from '@/stores/Transactions';
8790
import { CircleSpinner } from '@nimiq/vue-components';
@@ -91,10 +94,11 @@ import {
9194
onMounted,
9295
Ref,
9396
ref,
97+
watch,
9498
} from '@vue/composition-api';
9599
import Vue from 'vue';
96100
import VueTour from 'vue-tour';
97-
import { TourName, TourStep, TourStepIndex, TourSteps, useFakeTx, useTour } from '../composables/useTour';
101+
import { TourStep, TourStepIndex, TourSteps, useFakeTx, useTour } from '../composables/useTour';
98102
import { useWindowSize } from '../composables/useWindowSize';
99103
import CaretRightIcon from './icons/CaretRightIcon.vue';
100104
@@ -104,13 +108,6 @@ require('vue-tour/dist/vue-tour.css');
104108
105109
export default defineComponent({
106110
name: 'tour',
107-
props: {
108-
tourName: {
109-
type: String,
110-
required: true,
111-
validator: (tour: TourName) => (['onboarding', 'network'] as TourName[]).indexOf(tour) !== -1,
112-
},
113-
},
114111
setup(props, context) {
115112
// TODO Use isMobile
116113
const { width } = useWindowSize();
@@ -120,12 +117,14 @@ export default defineComponent({
120117
() => $network.consensus !== 'established',
121118
);
122119
120+
const { state: tourStore, removeTour } = useAccountStore();
121+
123122
let tour: VueTour.Tour | null = null;
124-
const steps: TourSteps<any> = useTour(props.tourName as TourName, context) || {};
123+
const steps: TourSteps<any> = useTour(tourStore.tour, context) || {};
125124
126125
// Initial state
127126
const loading = ref(true);
128-
const currentStep: Ref<TourStepIndex> = ref(0);
127+
const currentStep: Ref<TourStepIndex> = ref(8);
129128
const nSteps: Ref<number> = ref(0);
130129
const disableNextStep = ref(true);
131130
@@ -142,17 +141,36 @@ export default defineComponent({
142141
nSteps.value = Object.keys(steps).length;
143142
disableNextStep.value = currentStep.value >= nSteps.value - 1
144143
|| !!steps[currentStep.value].ui.disabledNextStep;
144+
145145
_addAttributes(steps[currentStep.value].ui, currentStep.value);
146146
// eslint-disable-next-line no-unused-expressions
147147
steps[currentStep.value].lifecycle?.onMountedStep?.(goToNextStep);
148148
149+
if (context.root.$route.path !== steps[currentStep.value].path) {
150+
context.root.$router.push(steps[currentStep.value].path);
151+
}
152+
149153
await sleep(500);
150154
151155
tour = context.root.$tours['nimiq-tour'];
152156
tour!.start(`${currentStep.value}`);
153157
loading.value = false;
154158
}
155159
160+
// Dont allow user to interact with the page while it is loading
161+
// But allow to end it
162+
watch([loading, disconnected], () => {
163+
const app = document.querySelector('#app main') as HTMLDivElement;
164+
165+
if (loading.value || disconnected.value) {
166+
// eslint-disable-next-line no-unused-expressions
167+
app?.setAttribute('data-non-interactable', '');
168+
} else {
169+
// eslint-disable-next-line no-unused-expressions
170+
app?.removeAttribute('data-non-interactable');
171+
}
172+
});
173+
156174
function goToPrevStep() {
157175
if (currentStep.value <= 0) return;
158176
_moveToFutureStep(currentStep.value, currentStep.value - 1);
@@ -171,8 +189,8 @@ export default defineComponent({
171189
) {
172190
const goingForward = futureStepIndex > currentStepIndex;
173191
174-
const { page: currentPage, lifecycle: currentLifecycle } = steps[currentStepIndex];
175-
const { page: futurePage, ui: futureUI, lifecycle: futureLifecycle } = steps[futureStepIndex];
192+
const { path: currentPage, lifecycle: currentLifecycle } = steps[currentStepIndex];
193+
const { path: futurePage, ui: futureUI, lifecycle: futureLifecycle } = steps[futureStepIndex];
176194
177195
loading.value = true;
178196
tour!.stop();
@@ -183,10 +201,14 @@ export default defineComponent({
183201
await currentLifecycle.prepareDOMPrevPage();
184202
} else if (goingForward && currentLifecycle && currentLifecycle.prepareDOMNextPage) {
185203
await currentLifecycle.prepareDOMNextPage();
186-
} else if (futurePage !== currentPage && currentPage.startsWith(context.root.$route.path)) {
187-
// Default prepare DOM
188-
context.root.$router.push(futurePage);
189-
await context.root.$nextTick();
204+
} else if (futurePage !== currentPage) {
205+
try {
206+
// Default prepare DOM
207+
context.root.$router.push(futurePage);
208+
await context.root.$nextTick();
209+
} catch {
210+
// Ignore error
211+
}
190212
}
191213
192214
_addAttributes(futureUI, futureStepIndex);
@@ -243,6 +265,16 @@ export default defineComponent({
243265
});
244266
}
245267
268+
function endTour() {
269+
_removeAttributes(currentStep.value);
270+
271+
// If user finalizes tour while it is loading, allow then interaction
272+
const app = document.querySelector('#app main') as HTMLDivElement;
273+
app.removeAttribute('data-non-interactable');
274+
275+
removeTour();
276+
}
277+
246278
// TODO REMOVE ME - Simulate tx
247279
function simulate() {
248280
const { addTransactions } = useTransactionsStore();
@@ -263,6 +295,7 @@ export default defineComponent({
263295
// actions
264296
goToPrevStep,
265297
goToNextStep,
298+
endTour,
266299
267300
// TODO REMOVE ME
268301
simulate,
@@ -294,6 +327,20 @@ export default defineComponent({
294327
.tour {
295328
position: relative;
296329
330+
button {
331+
width: min-content;
332+
white-space: nowrap;
333+
font-size: 16px;
334+
padding: 0.8rem 1.6rem;
335+
336+
text-align: center;
337+
background: #ffffff33; // TODO Maybe move this to a CSS variable (?)
338+
color: var(--nimiq-white);
339+
border: none;
340+
outline: var(--nimiq-);
341+
border-radius: 9999px;
342+
}
343+
297344
.tooltip-step {
298345
background: radial-gradient(
299346
100% 100% at 100% 100%,
@@ -323,6 +370,21 @@ export default defineComponent({
323370
}
324371
}
325372
}
373+
374+
.actions {
375+
margin-top: 2rem;
376+
display: flex;
377+
378+
button {
379+
font-weight: 700;
380+
font-size: 14px;
381+
382+
&.right {
383+
margin-left: auto;
384+
}
385+
}
386+
387+
}
326388
}
327389
}
328390
@@ -348,17 +410,8 @@ export default defineComponent({
348410
);
349411
350412
button {
351-
width: min-content;
352-
white-space: nowrap;
353413
padding: 1.4rem 1.6rem 1rem 1.6rem;
354-
font-size: 16px;
355-
356-
text-align: center;
357-
background: #ffffff33; // TODO Maybe move this to a CSS variable (?)
358-
color: var(--nimiq-white);
359-
border: none;
360-
outline: var(--nimiq-);
361-
border-radius: 9999px;
414+
font-weight: 700;
362415
363416
&:disabled {
364417
opacity: 0.5;

src/components/layouts/Settings.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@
9595
</p>
9696
</div>
9797

98-
<router-link :to="{ name: 'root', params: { tourName: 'onboarding' } }">
99-
<button class="nq-button-pill light-blue">{{ $t('Start Tour') }}</button>
100-
</router-link>
98+
<button class="nq-button-pill light-blue" @click="goToOnboardingTour()">
99+
{{ $t('Start Tour') }}
100+
</button>
101101
</div>
102102

103103
<!-- <div class="setting">
@@ -275,6 +275,7 @@ import { CircleSpinner } from '@nimiq/vue-components';
275275
// @ts-expect-error missing types for this package
276276
import { Portal } from '@linusborg/vue-simple-portal';
277277
278+
import { useAccountStore } from '@/stores/Account';
278279
import MenuIcon from '../icons/MenuIcon.vue';
279280
import CrossCloseButton from '../CrossCloseButton.vue';
280281
import CountryFlag from '../CountryFlag.vue';
@@ -372,6 +373,12 @@ export default defineComponent({
372373
reader.readAsText(file);
373374
}
374375
376+
function goToOnboardingTour() {
377+
const { setTour } = useAccountStore();
378+
setTour('onboarding');
379+
context.root.$router.push('/');
380+
}
381+
375382
async function onTrialPassword(el: HTMLInputElement) {
376383
let hash: string;
377384
try {
@@ -434,6 +441,7 @@ export default defineComponent({
434441
...settings,
435442
$fileInput,
436443
loadFile,
444+
goToOnboardingTour,
437445
showVestingSetting,
438446
onTrialPassword,
439447
applyWalletUpdate,

0 commit comments

Comments
 (0)