Skip to content

Commit

Permalink
added store state for tour
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed Jan 20, 2022
1 parent b670447 commit df7272d
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 36 deletions.
13 changes: 10 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<div id="app" :class="{'value-masked': amountsHidden}">
<!-- (?) This could be moved to Groundfloor.vue -->
<transition v-if="!!$route.params.tourName" name="delay">
<Tour :tourName="$route.params.tourName" />
<transition v-if="showTour" name="delay">
<Tour/>
</transition>

<main :class="routeClass" ref="$main">
Expand Down Expand Up @@ -77,7 +77,13 @@ export default defineComponent({
}
});
const { accountInfos } = useAccountStore();
const { accountInfos, state: accountState, removeTour } = useAccountStore();
if (!['root', 'transactions'].includes(context.root.$route.name as string)
&& accountState.tour?.name === 'onboarding') {
removeTour();
}
const showTour = computed(() => !!accountState.tour);
// Convert result of computation to boolean, to not trigger rerender when number of accounts changes above 0.
const hasAccounts = computed(() => Boolean(Object.keys(accountInfos.value).length));
Expand Down Expand Up @@ -170,6 +176,7 @@ export default defineComponent({
});
return {
showTour,
routeClass,
hasAccounts,
amountsHidden,
Expand Down
66 changes: 49 additions & 17 deletions src/components/Tour.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<p
v-for="(content, i) in tour.steps[tour.currentStep].content"
:key="i"
v-html="content"
v-html="$t(content)"
></p>
<!-- TODO REMOVE ME -->
<div class="remove_me" v-if="currentStep === 1" @click="simulate()">
Expand All @@ -47,7 +47,7 @@
</v-tour>
<transition name="fade">
<div class="tour-control-bar">
<button disabled>
<button @click="endTour()">
{{ $t("End Tour") }}
</button>
<span class="progress">
Expand Down Expand Up @@ -82,6 +82,7 @@
</template>

<script lang="ts">
import { useAccountStore } from '@/stores/Account';
import { useNetworkStore } from '@/stores/Network';
import { useTransactionsStore } from '@/stores/Transactions';
import { CircleSpinner } from '@nimiq/vue-components';
Expand All @@ -91,10 +92,11 @@ import {
onMounted,
Ref,
ref,
watch,
} from '@vue/composition-api';
import Vue from 'vue';
import VueTour from 'vue-tour';
import { TourName, TourStep, TourStepIndex, TourSteps, useFakeTx, useTour } from '../composables/useTour';
import { TourStep, TourStepIndex, TourSteps, useFakeTx, useTour } from '../composables/useTour';
import { useWindowSize } from '../composables/useWindowSize';
import CaretRightIcon from './icons/CaretRightIcon.vue';
Expand All @@ -104,13 +106,6 @@ require('vue-tour/dist/vue-tour.css');
export default defineComponent({
name: 'tour',
props: {
tourName: {
type: String,
required: true,
validator: (tour: TourName) => (['onboarding', 'network'] as TourName[]).indexOf(tour) !== -1,
},
},
setup(props, context) {
// TODO Use isMobile
const { width } = useWindowSize();
Expand All @@ -120,8 +115,10 @@ export default defineComponent({
() => $network.consensus !== 'established',
);
const { state: tourStore, removeTour } = useAccountStore();
let tour: VueTour.Tour | null = null;
const steps: TourSteps<any> = useTour(props.tourName as TourName, context) || {};
const steps: TourSteps<any> = useTour(tourStore.tour, context) || {};
// Initial state
const loading = ref(true);
Expand All @@ -142,17 +139,36 @@ export default defineComponent({
nSteps.value = Object.keys(steps).length;
disableNextStep.value = currentStep.value >= nSteps.value - 1
|| !!steps[currentStep.value].ui.disabledNextStep;
_addAttributes(steps[currentStep.value].ui, currentStep.value);
// eslint-disable-next-line no-unused-expressions
steps[currentStep.value].lifecycle?.onMountedStep?.(goToNextStep);
if (context.root.$route.path !== steps[currentStep.value].path) {
context.root.$router.push(steps[currentStep.value].path);
}
await sleep(500);
tour = context.root.$tours['nimiq-tour'];
tour!.start(`${currentStep.value}`);
loading.value = false;
}
// Dont allow user to interact with the page while it is loading
// But allow to end it
watch([loading, disconnected], () => {
const app = document.querySelector('#app main') as HTMLDivElement;
if (loading.value || disconnected.value) {
// eslint-disable-next-line no-unused-expressions
app?.setAttribute('data-non-interactable', '');
} else {
// eslint-disable-next-line no-unused-expressions
app?.removeAttribute('data-non-interactable');
}
});
function goToPrevStep() {
if (currentStep.value <= 0) return;
_moveToFutureStep(currentStep.value, currentStep.value - 1);
Expand All @@ -171,8 +187,8 @@ export default defineComponent({
) {
const goingForward = futureStepIndex > currentStepIndex;
const { page: currentPage, lifecycle: currentLifecycle } = steps[currentStepIndex];
const { page: futurePage, ui: futureUI, lifecycle: futureLifecycle } = steps[futureStepIndex];
const { path: currentPage, lifecycle: currentLifecycle } = steps[currentStepIndex];
const { path: futurePage, ui: futureUI, lifecycle: futureLifecycle } = steps[futureStepIndex];
loading.value = true;
tour!.stop();
Expand All @@ -183,10 +199,14 @@ export default defineComponent({
await currentLifecycle.prepareDOMPrevPage();
} else if (goingForward && currentLifecycle && currentLifecycle.prepareDOMNextPage) {
await currentLifecycle.prepareDOMNextPage();
} else if (futurePage !== currentPage && currentPage.startsWith(context.root.$route.path)) {
// Default prepare DOM
context.root.$router.push(futurePage);
await context.root.$nextTick();
} else if (futurePage !== currentPage) {
try {
// Default prepare DOM
context.root.$router.push(futurePage);
await context.root.$nextTick();
} catch {
// Ignore error
}
}
_addAttributes(futureUI, futureStepIndex);
Expand Down Expand Up @@ -243,6 +263,16 @@ export default defineComponent({
});
}
function endTour() {
_removeAttributes(currentStep.value);
// If user finalizes tour while it is loading, allow then interaction
const app = document.querySelector('#app main') as HTMLDivElement;
app.removeAttribute('data-non-interactable');
removeTour();
}
// TODO REMOVE ME - Simulate tx
function simulate() {
const { addTransactions } = useTransactionsStore();
Expand All @@ -263,6 +293,7 @@ export default defineComponent({
// actions
goToPrevStep,
goToNextStep,
endTour,
// TODO REMOVE ME
simulate,
Expand Down Expand Up @@ -359,6 +390,7 @@ export default defineComponent({
border: none;
outline: var(--nimiq-);
border-radius: 9999px;
font-weight: 700;
&:disabled {
opacity: 0.5;
Expand Down
14 changes: 11 additions & 3 deletions src/components/layouts/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@
</p>
</div>

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

<!-- <div class="setting">
Expand Down Expand Up @@ -275,6 +275,7 @@ import { CircleSpinner } from '@nimiq/vue-components';
// @ts-expect-error missing types for this package
import { Portal } from '@linusborg/vue-simple-portal';
import { useAccountStore } from '@/stores/Account';
import MenuIcon from '../icons/MenuIcon.vue';
import CrossCloseButton from '../CrossCloseButton.vue';
import CountryFlag from '../CountryFlag.vue';
Expand Down Expand Up @@ -372,6 +373,12 @@ export default defineComponent({
reader.readAsText(file);
}
function goToOnboardingTour() {
const { setTour } = useAccountStore();
setTour('onboarding');
context.root.$router.push('/');
}
async function onTrialPassword(el: HTMLInputElement) {
let hash: string;
try {
Expand Down Expand Up @@ -434,6 +441,7 @@ export default defineComponent({
...settings,
$fileInput,
loadFile,
goToOnboardingTour,
showVestingSetting,
onTrialPassword,
applyWalletUpdate,
Expand Down
32 changes: 19 additions & 13 deletions src/composables/useTour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { SetupContext } from '@vue/composition-api';
import { useAddressStore } from '../stores/Address';

export type TourName = 'onboarding' | 'network'
type OnboardingTourPages = '/' | '/transactions' | '/?sidebar=true'

enum MobileOnboardingTourStep {
FIRST_ADDRESS,
Expand All @@ -17,7 +16,6 @@ enum MobileOnboardingTourStep {
ONBOARDING_COMPLETED
}

type NetworkTourPages = '/network'
enum NetworkTourStep {
TODO,
}
Expand All @@ -32,7 +30,7 @@ type AlignedPlacement = `${BasePlacement}-${Alignment}`;
export type Placement = BasePlacement | AlignedPlacement;

export interface TourStep {
page: OnboardingTourPages | NetworkTourPages;
path: '/' | '/transactions' | '/?sidebar=true' | '/network';

// data for the steps of v-tour
tooltip: {
Expand Down Expand Up @@ -115,7 +113,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard

const steps: TourSteps<MobileOnboardingTourStep> = {
[MobileOnboardingTourStep.FIRST_ADDRESS]: {
page: '/',
path: '/',
tooltip: {
target: '.address-list > .address-button .identicon img',
content: [
Expand All @@ -127,6 +125,13 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
lifecycle: {
prepareDOMNextPage: async () => {
if (root.$route.path === '/') {
const addressButton = document
.querySelector('.address-list > .address-button') as HTMLButtonElement;
addressButton.click();
}
},
onMountedStep: (cbu: () => void) => {
const addressButton = document.querySelector('.address-list > .address-button');

Expand All @@ -146,7 +151,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.TRANSACTIONS_LIST]: {
page: '/transactions',
path: '/transactions',
tooltip: {
target: '.transaction-list > .empty-state h2',
content: [
Expand All @@ -169,7 +174,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.FIRST_TRANSACTION]: {
page: '/transactions',
path: '/transactions',
tooltip: {
target: '.transaction-list .list-element > .transaction > .identicon',
content: [
Expand Down Expand Up @@ -198,7 +203,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.BITCOIN_ADDRESS]: {
page: '/',
path: '/',
tooltip: {
target: '.account-overview .bitcoin-account > .bitcoin-account-item > svg',
content: [
Expand All @@ -222,7 +227,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.WALLET_BALANCE]: {
page: '/',
path: '/',
tooltip: {
target: '.account-overview .account-balance-container .amount',
content: [
Expand All @@ -247,7 +252,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.RECOVERY_WORDS_ALERT]: {
page: '/',
path: '/',
tooltip: {
target: '.account-overview .backup-warning button',
content: [
Expand All @@ -270,7 +275,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.MENU_ICON]: {
page: '/',
path: '/',
tooltip: {
target: '.account-overview .mobile-menu-bar > button.reset',
content: [
Expand Down Expand Up @@ -305,7 +310,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.ACCOUNT_OPTIONS]: {
page: '/?sidebar=true',
path: '/?sidebar=true',
tooltip: {
target: '.modal .small-page',
content: [
Expand Down Expand Up @@ -334,7 +339,7 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
},
},
[MobileOnboardingTourStep.ONBOARDING_COMPLETED]: {
page: '/?sidebar=true',
path: '/?sidebar=true',
tooltip: {
target: '.column-sidebar .network .consensus-icon',
content: [
Expand Down Expand Up @@ -378,10 +383,11 @@ function getOnboardingTourSteps({ root }: SetupContext): TourSteps<MobileOnboard
};
return steps;
}
export function useTour(tour: TourName, context: SetupContext)
export function useTour(tour: TourName | null, context: SetupContext)
: TourSteps<MobileOnboardingTourStep> | TourSteps<NetworkTourStep> | undefined {
if (tour === 'onboarding') {
return getOnboardingTourSteps(context);
}

return undefined;
}
Loading

0 comments on commit df7272d

Please sign in to comment.