diff --git a/src/components/NetworkMap.vue b/src/components/NetworkMap.vue index 0f92b1601..f2a954100 100644 --- a/src/components/NetworkMap.vue +++ b/src/components/NetworkMap.vue @@ -60,6 +60,7 @@ import NetworkMap, { } from '../lib/NetworkMap'; export default defineComponent({ + name: 'network-map', setup(props, context) { const $container = ref(null); const $network = ref(null); diff --git a/src/components/Tour.vue b/src/components/Tour.vue index 2dacf2b06..b061a252b 100644 --- a/src/components/Tour.vue +++ b/src/components/Tour.vue @@ -24,7 +24,14 @@
-

{{ $t(content) }}

+
+

+
    +
  • + - + +
  • +
@@ -100,6 +107,7 @@ import { computed, defineComponent, onMounted, + onUnmounted, Ref, ref, watch, @@ -142,16 +150,10 @@ export default defineComponent({ buttonNext: false, buttonStop: false, }, - labels: { - buttonSkip: 'Skip tour', - buttonPrevious: 'Previous', - buttonNext: 'Next', - buttonStop: 'Finish', - }, + // TODO Add padding to arrow useKeyboardNavigation: false, // handled by us }; - // TODO Go back to index - const steps = Object.values(getTour(tourStore.tour?.name, context)); + let steps = Object.values(getTour(tourStore.tour?.name, context)); // Initial state const isLoading = ref(true); @@ -168,10 +170,12 @@ export default defineComponent({ // REMOVE ME const { removeTransactions, addTransactions } = useTransactionsStore(); - // removeTransactions([getFakeTx()]); - addTransactions([getFakeTx()]); + removeTransactions([getFakeTx()]); + // addTransactions([getFakeTx()]); }); + onUnmounted(() => endTour()); + async function tourSetup() { await context.root.$nextTick(); // to ensure the DOM is ready @@ -186,6 +190,11 @@ export default defineComponent({ await step.lifecycle.created({ goToNextStep, goingForward: true }); } + if (context.root.$route.fullPath !== step.path) { + context.root.$router.push(step.path); + await context.root.$nextTick(); + } + _toggleDisabledButtons(step.ui.disabledButtons, true); _addAttributes(step.ui, currentStep.value); @@ -209,6 +218,10 @@ export default defineComponent({ window.addEventListener('keyup', _onKeyDown); window.addEventListener('click', _userClicked()); + // window.addEventListener('resize', _OnResize(_OnResizeEnd)); TODO + + const app = document.querySelector('#app'); + app!.setAttribute('data-tour-active', ''); _receiveEvents(); _broadcast({ @@ -236,7 +249,9 @@ export default defineComponent({ // it takes to render the button. I don't know how to fix it. // Ensure that we disabled 'Receive Free NIM' button - await sleep(500); + await sleep(500); // TODO + // TODO Remove this code for the network, find other way + // steps = Object.values(getTour(tourStore.tour?.name, context)); _toggleDisabledButtons(steps[currentStep.value]?.ui.disabledButtons, true); }); @@ -326,7 +341,10 @@ export default defineComponent({ } function _userClicked() { - const userCanClick = ['.tour', '.tour-manager'].map((s) => document.querySelector(s) as HTMLElement); + const userCanClick = ['.tour', '.tour-manager'] + .map((s) => document.querySelector(s) as HTMLElement) + .filter((e) => !!e); + return ({ target }: MouseEvent) => { if (!target) return; if (!userCanClick.some((el) => el.contains(target as Node))) { @@ -399,26 +417,47 @@ export default defineComponent({ }); } - async function endTour() { - _removeAttributes(currentStep.value); - _toggleDisabledButtons(steps[currentStep.value]?.ui.disabledButtons, false); - + async function endTour(soft = false) { window.removeEventListener('keyup', _onKeyDown); - window.addEventListener('click', _userClicked()); - - context.root.$off('nimiq-tour-event'); + window.removeEventListener('click', () => _userClicked()); if (unmounted) { await unmounted({ ending: true, goingForward: false }); } + if (soft) { + return; + } + + window.removeEventListener('resize', () => _OnResize(_OnResizeEnd)); + + _removeAttributes(currentStep.value); + _toggleDisabledButtons(steps[currentStep.value]?.ui.disabledButtons, false); + + context.root.$off('nimiq-tour-event'); // If user finalizes tour while it is loading, allow interaction again - const app = document.querySelector('#app main') as HTMLDivElement; - app.removeAttribute('data-non-interactable'); + const app = document.querySelector('#app') as HTMLDivElement; + app.removeAttribute('data-tour-active'); + app.querySelector('main')!.removeAttribute('data-non-interactable'); setTour(null); } + function _OnResize(func: () => void) { + endTour(true); + tour!.stop(); + let timer: ReturnType | null = null; + return () => { + if (timer) clearTimeout(timer); + timer = setTimeout(func, 100); + }; + } + + function _OnResizeEnd() { + steps = Object.values(getTour(tourStore.tour?.name, context)); + tourSetup(); + } + function _onKeyDown(event: KeyboardEvent) { switch (event.key) { case 'ArrowRight': @@ -483,21 +522,21 @@ export default defineComponent({ // updated with opacity and non-interactivity properties as data attributes allow to use a value like // [data-opaified="1"] although the CSS selector that we can use is [data-opacified]. @see _removeAttributes -[data-opacified] { +[data-tour-active] [data-opacified] { filter: opacity(0.3); } -[data-non-interactable], -[data-non-interactable] * { +[data-tour-active] [data-non-interactable], +[data-tour-active] [data-non-interactable] * { user-select: none !important; pointer-events: none !important; } -#app > *:not(.tour):not(.tour-manager) { +[data-tour-active]#app > *:not(.tour):not(.tour-manager) { cursor: not-allowed; } -button.highlighted { +[data-tour-active] button.highlighted { background: linear-gradient( 274.28deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.2) 27.6%, rgba(255, 255, 255, 0) 53.12%, rgba(255, 255, 255, 0.2) 81.25%, rgba(255, 255, 255, 0) 100% @@ -542,7 +581,23 @@ button.highlighted { display: flex; align-items: center; - p { + ul { + list-style-type: none; + margin: 0; + padding-left: 0; + + li { + display: flex; + gap: 1rem; + margin-top: 1rem; + + .dash { + user-select: none; + } + } + } + + p, li { margin: 0; font-size: 15px; line-height: 21px; @@ -553,11 +608,17 @@ button.highlighted { } } - p::selection { + p::selection, li::selection { color: var(--nimiq-light-blue); background: var(--nimiq-light-gray); } + hr { + width: 100%; + opacity: 0.2; + height: 1.5px; + } + ::v-deep svg { float: left; margin-right: 2rem; @@ -569,6 +630,7 @@ button.highlighted { .actions { margin-top: 2rem; display: flex; + gap: 1rem; button { font-weight: 700; diff --git a/src/components/TourLargeScreenManager.vue b/src/components/TourLargeScreenManager.vue index 123037d2e..28d3667eb 100644 --- a/src/components/TourLargeScreenManager.vue +++ b/src/components/TourLargeScreenManager.vue @@ -1,5 +1,5 @@ @@ -134,19 +142,30 @@ export default defineComponent({ max-height: 38rem; } } -} -.nq-h1 { - margin-top: 0; - margin-bottom: 2rem; -} + .content { + .nq-h1 { + margin-top: 0; + margin-bottom: 2rem; + margin-right: 5rem; + } -p, a { - font-size: var(--body-size); -} + p, button { + font-size: var(--body-size); + } -a { - display: inline-flex; + button { + margin-top: 1.75rem; + display: inline-flex; + align-items: center; + gap: 1.5rem; + transition: margin 0.2s ease-in-out; + } + + button:hover svg { + left: 10px; + } + } } @media (min-width: 700px) { // Full mobile breakpoint @@ -166,6 +185,10 @@ a { .page-body { --padding: 3rem; + + button { + margin-top: 1.5rem; + } } } diff --git a/src/composables/useWindowSize.ts b/src/composables/useWindowSize.ts index 572a4b1ef..8e04205eb 100644 --- a/src/composables/useWindowSize.ts +++ b/src/composables/useWindowSize.ts @@ -23,9 +23,9 @@ export function useWindowSize() { width = ref(0); height = ref(0); listener(); - isSmallScreen = computed(() => width!.value <= 700); // Small screen breakpoint - isMediumScreen = computed(() => width!.value > 700 && width!.value <= 1160); // Small screen breakpoint - isLargeScreen = computed(() => width!.value > 1160); // Small screen breakpoint + isSmallScreen = computed(() => width!.value <= 700); + isMediumScreen = computed(() => width!.value > 700 && width!.value <= 1160); + isLargeScreen = computed(() => width!.value > 1160); } onMounted(() => { diff --git a/src/lib/tour/index.ts b/src/lib/tour/index.ts index 02c39d1fe..0fc8f77e6 100644 --- a/src/lib/tour/index.ts +++ b/src/lib/tour/index.ts @@ -1,6 +1,6 @@ import { useAddressStore } from '@/stores/Address'; -import { SetupContext } from '@vue/composition-api'; import { Transaction } from '@/stores/Transactions'; +import { SetupContext } from '@vue/composition-api'; import { getNetworkTourSteps } from './network'; import { getOnboardingTourSteps } from './onboarding'; import { NetworkTourStep, OnboardingTourStep, TourName, TourSteps } from './types'; @@ -11,7 +11,7 @@ export function getTour(tour: TourName | undefined, context: SetupContext) case 'onboarding': return getOnboardingTourSteps(context); case 'network': - return getNetworkTourSteps(); + return getNetworkTourSteps(context); default: return {}; } diff --git a/src/lib/tour/network/01_YourLocationStep.ts b/src/lib/tour/network/01_YourLocationStep.ts new file mode 100644 index 000000000..0caf15b10 --- /dev/null +++ b/src/lib/tour/network/01_YourLocationStep.ts @@ -0,0 +1,41 @@ +import { SCALING_FACTOR } from '@/lib/NetworkMap'; +import { NetworkGetStepFnArgs, NetworkTourStep, TourStep, WalletHTMLElements } from '..'; +import { getNetworkTexts } from './NetworkTourTexts'; + +export function getYourLocationStep({ nodes, scrollIntoView, sleep, selfNodeIndex }: NetworkGetStepFnArgs): TourStep { + return { + path: '/network', + tooltip: { + target: `${WalletHTMLElements.NETWORK_NODES} span:nth-child(${selfNodeIndex + 1})`, + content: getNetworkTexts(NetworkTourStep.YOUR_LOCATION), + params: { + // TODO On mobile phones if the node is in the south, the tooltip might break the web + placement: 'bottom', + }, + }, + ui: { + fadedElements: [ + WalletHTMLElements.SIDEBAR_TESTNET, + WalletHTMLElements.SIDEBAR_LOGO, + WalletHTMLElements.SIDEBAR_PRICE_CHARTS, + WalletHTMLElements.SIDEBAR_ACCOUNT_MENU, + WalletHTMLElements.SIDEBAR_NETWORK, + WalletHTMLElements.SIDEBAR_SETTINGS, + WalletHTMLElements.NETWORK_STATS, + ], + disabledElements: [ + WalletHTMLElements.NETWORK_TABLET_MENU_BAR, + WalletHTMLElements.NETWORK_MAP, + ], + disabledButtons: [WalletHTMLElements.BUTTON_SIDEBAR_BUY, WalletHTMLElements.BUTTON_SIDEBAR_SELL], + }, + lifecycle: { + created: (async ({ goingForward }) => { + scrollIntoView((nodes()[selfNodeIndex].x / 2) * SCALING_FACTOR); + if (!goingForward) { + await sleep(500); + } + }), + }, + } as TourStep; +} diff --git a/src/lib/tour/network/02_BackboneNodeStep.ts b/src/lib/tour/network/02_BackboneNodeStep.ts new file mode 100644 index 000000000..93c8ad0f0 --- /dev/null +++ b/src/lib/tour/network/02_BackboneNodeStep.ts @@ -0,0 +1,56 @@ +import { SCALING_FACTOR } from '@/lib/NetworkMap'; +import { ref } from '@vue/composition-api'; +import { NetworkGetStepFnArgs, NetworkTourStep, TourStep, WalletHTMLElements } from '..'; +import { getNetworkTexts } from './NetworkTourTexts'; + +export function getBackboneNodeStep( + { nodes, selfNodeIndex, isLargeScreen, scrollIntoView, sleep }: NetworkGetStepFnArgs): TourStep { + const selectedNode = ref(-1); + return { + path: '/network', + tooltip: { + get target() { + return `${WalletHTMLElements.NETWORK_NODES} span:nth-child(${selectedNode.value + 1})`; + }, + content: getNetworkTexts(NetworkTourStep.BACKBONE_NODE), + params: { + // TODO On mobile phones if the node is in the south, the tooltip might break the web + placement: isLargeScreen.value ? 'right' : 'bottom', + }, + }, + ui: { + fadedElements: [ + WalletHTMLElements.SIDEBAR_TESTNET, + WalletHTMLElements.SIDEBAR_LOGO, + WalletHTMLElements.SIDEBAR_PRICE_CHARTS, + WalletHTMLElements.SIDEBAR_ACCOUNT_MENU, + WalletHTMLElements.SIDEBAR_NETWORK, + WalletHTMLElements.SIDEBAR_SETTINGS, + WalletHTMLElements.NETWORK_STATS, + ], + disabledElements: [ + WalletHTMLElements.NETWORK_TABLET_MENU_BAR, + WalletHTMLElements.NETWORK_MAP, + ], + disabledButtons: [WalletHTMLElements.BUTTON_SIDEBAR_BUY, WalletHTMLElements.BUTTON_SIDEBAR_SELL], + }, + lifecycle: { + created: (async () => { + const distance = ([x1, y1]: number[], [x2, y2]: number[]) => (((x1 - x2) ** 2) + (y1 - y2) ** 2) ** 0.5; + const _nodes = nodes(); + const { position: pSelf } = _nodes[selfNodeIndex]; + + // get closest west node minimum distance of 5 + const node = _nodes + .map((n, i) => ({ ...n, i, x: n.x })) // add index + .filter((_, i) => i !== selfNodeIndex) + .map((n) => ({ ...n, d: distance([n.position.x, n.position.y], [pSelf.x, pSelf.y]) })) + .filter(({ d }) => d > 5) + .sort((a, b) => a.d - b.d)[0]; + selectedNode.value = node.i; + scrollIntoView((node.x / 2) * SCALING_FACTOR); + await sleep(500); + }), + }, + } as TourStep; +} diff --git a/src/lib/tour/network/03_NetworkMetricsStep.ts b/src/lib/tour/network/03_NetworkMetricsStep.ts new file mode 100644 index 000000000..4a87fab29 --- /dev/null +++ b/src/lib/tour/network/03_NetworkMetricsStep.ts @@ -0,0 +1,31 @@ +import { NetworkTourStep, TourStep, WalletHTMLElements } from '..'; +import { getNetworkTexts } from './NetworkTourTexts'; + +export function getNetworkMetricsStep(): TourStep { + return { + path: '/network', + tooltip: { + target: WalletHTMLElements.NETWORK_STATS, + content: getNetworkTexts(NetworkTourStep.METRICS), + params: { + placement: 'top', + }, + }, + ui: { + fadedElements: [ + WalletHTMLElements.SIDEBAR_TESTNET, + WalletHTMLElements.SIDEBAR_LOGO, + WalletHTMLElements.SIDEBAR_PRICE_CHARTS, + WalletHTMLElements.SIDEBAR_ACCOUNT_MENU, + WalletHTMLElements.SIDEBAR_NETWORK, + WalletHTMLElements.SIDEBAR_SETTINGS, + ], + disabledElements: [ + WalletHTMLElements.NETWORK_TABLET_MENU_BAR, + WalletHTMLElements.NETWORK_MAP, + WalletHTMLElements.NETWORK_STATS, + ], + disabledButtons: [WalletHTMLElements.BUTTON_SIDEBAR_BUY, WalletHTMLElements.BUTTON_SIDEBAR_SELL], + }, + } as TourStep; +} diff --git a/src/lib/tour/network/04_NetworkCompletedStep.ts b/src/lib/tour/network/04_NetworkCompletedStep.ts new file mode 100644 index 000000000..f7a95ecff --- /dev/null +++ b/src/lib/tour/network/04_NetworkCompletedStep.ts @@ -0,0 +1,45 @@ +import { NetworkGetStepFnArgs, NetworkTourStep, TourStep, WalletHTMLElements } from '..'; +import { getNetworkTexts } from './NetworkTourTexts'; + +export function getNetworkCompletedStep({ isLargeScreen }: NetworkGetStepFnArgs): TourStep { + return { + path: '/network', + tooltip: { + get target() { + return isLargeScreen.value + ? WalletHTMLElements.SIDEBAR_ACCOUNT_MENU + : `${WalletHTMLElements.NETWORK_TABLET_MENU_BAR} .account-button`; + }, + content: getNetworkTexts(NetworkTourStep.NETWORK_COMPLETED, isLargeScreen.value), + params: { + get placement() { + return isLargeScreen.value ? 'right' : 'top'; + }, + }, + button: { + text: 'End Tour', + fn: async (endTour) => { + if (endTour) { + await endTour(); + } + }, + }, + }, + ui: { + fadedElements: [ + WalletHTMLElements.SIDEBAR_TESTNET, + WalletHTMLElements.SIDEBAR_LOGO, + WalletHTMLElements.SIDEBAR_PRICE_CHARTS, + WalletHTMLElements.SIDEBAR_NETWORK, + WalletHTMLElements.SIDEBAR_SETTINGS, + ], + disabledElements: [ + WalletHTMLElements.SIDEBAR_ACCOUNT_MENU, + WalletHTMLElements.NETWORK_TABLET_MENU_BAR, + WalletHTMLElements.NETWORK_MAP, + WalletHTMLElements.NETWORK_STATS, + ], + disabledButtons: [WalletHTMLElements.BUTTON_SIDEBAR_BUY, WalletHTMLElements.BUTTON_SIDEBAR_SELL], + }, + } as TourStep; +} diff --git a/src/lib/tour/network/NetworkTourTexts.ts b/src/lib/tour/network/NetworkTourTexts.ts new file mode 100644 index 000000000..5624a2280 --- /dev/null +++ b/src/lib/tour/network/NetworkTourTexts.ts @@ -0,0 +1,34 @@ +import { NetworkTourStep } from '../types'; + +type TourStepTexts = { + [x in T]: string[] +} + +export function getNetworkTexts(i: NetworkTourStep, isLargeScreen?: boolean) { + return ({ + [NetworkTourStep.YOUR_LOCATION]: [ + 'This is you. Your location is determined by your IP address.', + 'Nimiq doesn’t collect or store such data.', + ], + [NetworkTourStep.BACKBONE_NODE]: [ + 'This is a peer or a backbone node that you are connected to.', + 'These connections enable you to establish consensus with a sub set of participants directly.', + [ + '‘Available browsers’ are other user’s browsers, just like yours.', + '‘Backbone nodes’ provide a fallback to connect to.', + ], + ], + [NetworkTourStep.METRICS]: [ + 'Find the network’s key performance metrics below.', + 'The {{NETWORK}}-icon indicates that you are connected to the network.', + ], + [NetworkTourStep.NETWORK_COMPLETED]: [ + 'You made it!', + 'HR', + 'Enjoy the decentralized future, and don’t forget to invite your friends and family.', + isLargeScreen + ? 'Click {{ ACCOUNT }} to get back to your wallet.' + : 'Click ‘Back to Addresses’ to get back to your wallet.', + ], + } as TourStepTexts)[i]; +} diff --git a/src/lib/tour/network/index.ts b/src/lib/tour/network/index.ts index f9e463887..545b70e97 100644 --- a/src/lib/tour/network/index.ts +++ b/src/lib/tour/network/index.ts @@ -1,22 +1,43 @@ -import { NetworkTourStep, TourSteps } from '../types'; +import { useWindowSize } from '@/composables/useWindowSize'; +import { NodeHexagon, NodeType, WIDTH } from '@/lib/NetworkMap'; +import { SetupContext } from '@vue/composition-api'; +import { searchComponentByName } from '..'; +import { NetworkGetStepFnArgs, NetworkTourStep, TourSteps, WalletHTMLElements } from '../types'; +import { getYourLocationStep } from './01_YourLocationStep'; +import { getBackboneNodeStep } from './02_BackboneNodeStep'; +import { getNetworkMetricsStep } from './03_NetworkMetricsStep'; +import { getNetworkCompletedStep } from './04_NetworkCompletedStep'; + +export function getNetworkTourSteps({ root }: SetupContext): TourSteps { + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + const { isSmallScreen, isMediumScreen, isLargeScreen } = useWindowSize(); + + const networkMapInstance = searchComponentByName(root, 'network-map') as any; + const nodes = () => networkMapInstance?.nodes as NodeHexagon[] || []; + const selfNodeIndex = nodes().findIndex((node) => [...node.peers].find((p) => p.type === NodeType.SELF)); + + const scrollIntoView = async (x: number) => { + const map = document.querySelector(WalletHTMLElements.NETWORK_SCROLLER) as HTMLElement; + const mapWidth = map.children[0]!.clientWidth; + const adjustedX = x * (mapWidth / WIDTH); + const scrollTarget = adjustedX - (window.innerWidth / 2); + map.scrollTo(scrollTarget, 0); + }; + + const args: NetworkGetStepFnArgs = { + nodes, + selfNodeIndex, + isSmallScreen, + isMediumScreen, + isLargeScreen, + scrollIntoView, + sleep, + }; -export function getNetworkTourSteps(): TourSteps { return { - [NetworkTourStep.TODO]: { - path: '/network', - tooltip: { - target: '.network-overview .network-name', - content: [ - 'Welcome to the {WORLD} Network!', - 'This is the main network where all Nimiq transactions take place.', - 'You can switch between networks by clicking on the {WORLD} Network icon in the top right corner.', - ], - params: { - placement: 'bottom', - }, - }, - ui: {}, - lifecycle: {}, - }, + [NetworkTourStep.YOUR_LOCATION]: getYourLocationStep(args), + [NetworkTourStep.BACKBONE_NODE]: getBackboneNodeStep(args), + [NetworkTourStep.METRICS]: getNetworkMetricsStep(), + [NetworkTourStep.NETWORK_COMPLETED]: getNetworkCompletedStep(args), }; } diff --git a/src/lib/tour/onboarding/OnboardingTourTexts.ts b/src/lib/tour/onboarding/OnboardingTourTexts.ts index 7cd5d404d..831a797cd 100644 --- a/src/lib/tour/onboarding/OnboardingTourTexts.ts +++ b/src/lib/tour/onboarding/OnboardingTourTexts.ts @@ -14,7 +14,7 @@ export function getOnboardingTexts(i: OnboardingTourStep, isANewUser: boolean) { 'You can click on the address to copy and share it.', ], }, - [OnboardingTourStep.TRANSACTIONS_LIST]: { + [OnboardingTourStep.TRANSACTION_LIST]: { default: [ 'This is where all your transactions will appear.', 'Click the green button to receive a free NIM from Team Nimiq.', @@ -23,7 +23,7 @@ export function getOnboardingTexts(i: OnboardingTourStep, isANewUser: boolean) { }, [OnboardingTourStep.FIRST_TRANSACTION]: { default: [ - "Here's your first transaction with your first NIM.", + 'Here’s your first transaction with your first NIM.', 'Every NIM address comes with an avatar. They help to make sure you got the right one.', ], }, @@ -40,9 +40,9 @@ export function getOnboardingTexts(i: OnboardingTourStep, isANewUser: boolean) { }, [OnboardingTourStep.BACKUP_ALERT]: { default: isANewUser ? [ - 'There is no \'forgot password\'. Create a backup to make sure you stay in control.', + 'There is no ‘forgot password’. Create a backup to make sure you stay in control.', ] : [ - 'Seriously! There is no \'forgot password\'! Create a backup to make sure you stay in control.', + 'Seriously! There is no ‘forgot password’! Create a backup to make sure you stay in control.', ], }, [OnboardingTourStep.MENU_ICON]: { @@ -52,7 +52,7 @@ export function getOnboardingTexts(i: OnboardingTourStep, isANewUser: boolean) { }, [OnboardingTourStep.BACKUP_OPTION_LARGE_SCREENS]: { default: [ - 'You can always create a new backup. Simply click your account and select \'Create backup\'.', + 'You can always create a new backup. Simply click your account and select ‘Create backup’.', ], }, [OnboardingTourStep.ACCOUNT_OPTIONS]: { @@ -64,7 +64,7 @@ export function getOnboardingTexts(i: OnboardingTourStep, isANewUser: boolean) { [OnboardingTourStep.BACKUP_OPTION_NOT_LARGE_SCREENS]: { default: [ 'You can always create a new backup.', - 'Simply click your account and select \'Create backup\'.', + 'Simply click your account and select ‘Create backup’.', ], }, [OnboardingTourStep.ONBOARDING_COMPLETED]: { diff --git a/src/lib/tour/onboarding/index.ts b/src/lib/tour/onboarding/index.ts index 96d672234..0ae2a219f 100644 --- a/src/lib/tour/onboarding/index.ts +++ b/src/lib/tour/onboarding/index.ts @@ -1,7 +1,7 @@ import { useWindowSize } from '@/composables/useWindowSize'; import { AccountType, useAccountStore } from '@/stores/Account'; import { SetupContext } from '@vue/composition-api'; -import { searchComponentByName } from '..'; +import { searchComponentByName, TourName } from '..'; import { GetStepFnArgs, OnboardingTourStep, TourSteps } from '../types'; import { getFirstAddressStep } from './01_FirstAddressStep'; import { getTransactionListStep } from './02_TransactionListStep'; @@ -27,14 +27,12 @@ export function getOnboardingTourSteps({ root }: SetupContext): TourSteps = {}; - const openAccountOptions = async () => { const accountMenu = searchComponentByName(root, 'account-menu') as any; if (!accountMenu || !('closeMenu' in accountMenu) @@ -55,7 +53,6 @@ export function getOnboardingTourSteps({ root }: SetupContext): TourSteps = { sleep, - steps, toggleDisabledAttribute, root, isSmallScreen, @@ -66,11 +63,16 @@ export function getOnboardingTourSteps({ root }: SetupContext): TourSteps = { + [OnboardingTourStep.FIRST_ADDRESS]: getFirstAddressStep(args), + [OnboardingTourStep.TRANSACTION_LIST]: getTransactionListStep(args), + [OnboardingTourStep.FIRST_TRANSACTION]: getFirstTransactionStep(args), + [OnboardingTourStep.BITCOIN_ADDRESS]: getBitcoinAddressStep(args), + [OnboardingTourStep.WALLET_BALANCE]: getWalletBalanceStep(args), + [OnboardingTourStep.ACCOUNT_OPTIONS]: getAccountOptionsStep( + { ...args, keepMenuOpenOnForward: fileExported && !isLargeScreen.value }), + [OnboardingTourStep.ONBOARDING_COMPLETED]: getOnboardingCompletedStep(args), + }; if (!fileExported) { steps[OnboardingTourStep.BACKUP_ALERT] = getBackupAlertStep(args); } @@ -80,12 +82,8 @@ export function getOnboardingTourSteps({ root }: SetupContext): TourSteps = { [x in T]?: TourStep; }; +// TODO Rename or mix with NetworkGetStepFnArgs export type GetStepFnArgs = Pick, 'isSmallScreen' | 'isMediumScreen' | 'isLargeScreen'> & { root: SetupContext['root'], - steps: TourSteps, toggleDisabledAttribute: (selector: string, disabled: boolean) => Promise, sleep: (ms: number) => Promise, isANewUser: boolean, @@ -89,6 +93,14 @@ export type GetStepFnArgs = closeAccountOptions: () => Promise, }; +export type NetworkGetStepFnArgs = + Pick, 'isSmallScreen' | 'isMediumScreen' | 'isLargeScreen'> & { + nodes: () => NodeHexagon[], + selfNodeIndex: number, + scrollIntoView: (x: number) => void, + sleep: (ms: number) => Promise, + } + export type TourBroadcast = TourBroadcastEnd | TourBroadcastStepChanged | TourBroadcastClickedOutsideTour interface TourBroadcastEnd { @@ -139,7 +151,11 @@ export enum WalletHTMLElements { MODAL_CONTAINER = '.modal.backdrop', MODAL_WRAPPER = '.modal .wrapper', MODAL_PAGE = '.modal .small-page', - MODAL_CLOSE_BUTTON = '.modal .close-button' + MODAL_CLOSE_BUTTON = '.modal .close-button', - // TODO: NETWORK + NETWORK_STATS = '.network .network-stats', + NETWORK_MAP = '.network .network-map', + NETWORK_NODES = '.network .network-map .nodes', + NETWORK_SCROLLER = '.network .scroller', + NETWORK_TABLET_MENU_BAR = '.network .menu-bar', } diff --git a/src/stores/Account.ts b/src/stores/Account.ts index 494448cbf..20d68c284 100644 --- a/src/stores/Account.ts +++ b/src/stores/Account.ts @@ -8,7 +8,7 @@ export type AccountState = { accountInfos: {[id: string]: AccountInfo}, activeAccountId: string | null, activeCurrency: CryptoCurrency, - tour: { name: TourName, isANewUser: boolean } | null, + tour: { name: TourName.NETWORK } | { name: TourName.ONBOARDING, isANewUser: boolean } | null, } // Mirror of Hub WalletType, which is not exported