Skip to content

Commit

Permalink
improved some edge cases with overnice feedback
Browse files Browse the repository at this point in the history
- handle erros when faucet returns error
  • Loading branch information
onmax committed Mar 4, 2022
1 parent a072adc commit 35e8301
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 28 deletions.
43 changes: 24 additions & 19 deletions src/components/Tour.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
{{ tour.steps[tour.currentStep].button.text }}
</button>
<button
v-else-if="isLargeScreen && !disableNextStep && !isLoading"
v-else-if="isLargeScreen && !isNextStepDisabled && !isLoading"
class="right"
@click="goToNextStep()"
tabindex="0"
Expand Down Expand Up @@ -128,7 +128,7 @@
<button
class="next"
:class="{ loading: isLoading }"
:disabled="disableNextStep"
:disabled="isNextStepDisabled"
@click="!isLoading && goToNextStep()"
tabindex="0"
>
Expand Down Expand Up @@ -160,6 +160,7 @@ import { useEventListener } from '../composables/useEventListener';
import { useKeys } from '../composables/useKeys';
import { useWindowSize } from '../composables/useWindowSize';
import {
FutureStepArgs,
getTour, IContentSpecialItem, ITourBroadcast, ITourStep, IUnmountedFn, TourStepIndex,
} from '../lib/tour';
import PartyConfettiIcon from './icons/PartyConfettiIcon.vue';
Expand Down Expand Up @@ -219,7 +220,7 @@ export default defineComponent({
const isLoading = ref(true);
const currentStep: Ref<TourStepIndex> = ref(accountStore.tour?.step || 0);
const nSteps: Ref<number> = ref(steps.value.length);
const disableNextStep = ref(true);
const isNextStepDisabled = ref(true);
const showTour = ref(false);
let unmounted: IUnmountedFn | void;
Expand All @@ -228,7 +229,7 @@ export default defineComponent({
useEventListener(window, 'click', _onUserClicked);
useEventListener(window, 'resize', _onResize());
useKeys([
{ key: 'ArrowRight', handler: goToNextStep, options: { ignoreIf: disableNextStep } },
{ key: 'ArrowRight', handler: goToNextStep, options: { ignoreIf: isNextStepDisabled } },
{ key: 'ArrowLeft', handler: goToPrevStep },
{ key: 'Escape', handler: endTour },
]);
Expand All @@ -253,10 +254,10 @@ export default defineComponent({
if (!step) return;
// Update state
disableNextStep.value = currentStep.value >= nSteps.value - 1 || !!step.ui.isNextStepDisabled;
isNextStepDisabled.value = currentStep.value >= nSteps.value - 1 || !!step.ui.isNextStepDisabled;
if (step.lifecycle?.created) {
await step.lifecycle.created({ goToNextStep, goingForward: true });
await step.lifecycle.created({ goToNextStep, goToPrevStep, goingForward: true, isNextStepDisabled });
}
// If we are not currently in the correct path then we need to change the route
Expand All @@ -269,8 +270,10 @@ export default defineComponent({
_updateUI(step.ui, currentStep.value);
unmounted = await step.lifecycle?.mounted?.({
goToNextStep,
goToPrevStep,
goingForward: true,
ending: false,
isNextStepDisabled,
});
if (!context.root.$route.fullPath.endsWith(step.path)) {
Expand Down Expand Up @@ -312,34 +315,36 @@ export default defineComponent({
_toggleDisabledButtons(steps.value[currentStep.value]?.ui.disabledButtons, true);
});
function goToPrevStep() {
function goToPrevStep(args?: FutureStepArgs) {
if (currentStep.value <= 0 || disconnected.value || isLoading.value) return;
_moveToFutureStep(currentStep.value, currentStep.value - 1, false);
_moveToFutureStep(currentStep.value, currentStep.value - 1, false, args);
}
function goToNextStep() {
function goToNextStep(args?: FutureStepArgs) {
if (currentStep.value + 1 >= nSteps.value || disconnected.value || isLoading.value) return;
_moveToFutureStep(currentStep.value, currentStep.value + 1, true);
_moveToFutureStep(currentStep.value, currentStep.value + 1, true, args);
}
// for more details, read Lifecycle steps section right below imports of this file
async function _moveToFutureStep(
currentStepIndex: TourStepIndex,
newStepIndex: TourStepIndex,
goingForward: boolean,
{ withDelay }: FutureStepArgs = { withDelay: true },
) {
const { path: currentPath } = steps.value[currentStepIndex]!;
const { path: newPath, ui: newUI, lifecycle: newLifecycle } = steps.value[newStepIndex]!;
tour!.stop();
await sleep(500); // ensures animation ends
await sleep(withDelay ? 500 : 1); // 500 for making sure animation ends. 1 for avoiding concurrency
if (unmounted) {
await unmounted({ goingForward, ending: false });
await unmounted({ goingForward, ending: false, isNextStepDisabled });
}
// created
await newLifecycle?.created?.({ goToNextStep, goingForward });
await newLifecycle?.created?.({ goToNextStep, goToPrevStep, goingForward, isNextStepDisabled });
if (newPath !== currentPath && context.root.$route.fullPath !== newPath) {
context.root.$router.push(newPath);
Expand All @@ -350,9 +355,7 @@ export default defineComponent({
_updateUI(newUI, newStepIndex);
await context.root.$nextTick();
if (newPath !== currentPath) {
await sleep(500);
}
if (newPath !== currentPath && withDelay) await sleep(500);
_removeUIFromOldStep(currentStepIndex);
Expand All @@ -361,12 +364,14 @@ export default defineComponent({
_changeArrowAppearance(newStepIndex);
// some steps may not allow the user to go to the next step unless they click on a specific button
disableNextStep.value = newStepIndex >= nSteps.value - 1 || !!newUI.isNextStepDisabled;
isNextStepDisabled.value = newStepIndex >= nSteps.value - 1 || !!newUI.isNextStepDisabled;
unmounted = await newLifecycle?.mounted?.({
goToNextStep,
goToPrevStep,
goingForward,
ending: false,
isNextStepDisabled,
});
currentStep.value = newStepIndex;
Expand Down Expand Up @@ -508,7 +513,7 @@ export default defineComponent({
// destroying the tour
async function endTour(notifyManager = true, soft = false) {
if (unmounted) {
await unmounted({ ending: true, goingForward: false });
await unmounted({ ending: true, goingForward: false, isNextStepDisabled });
}
if (notifyManager) {
Expand Down Expand Up @@ -624,7 +629,7 @@ export default defineComponent({
currentStep,
nSteps,
isLoading: computed(() => disconnected.value || isLoading.value),
disableNextStep,
isNextStepDisabled,
// actions
goToPrevStep,
Expand Down
39 changes: 33 additions & 6 deletions src/lib/tour/onboarding/02_TransactionListStep.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { watch } from '@vue/composition-api';
import { ref, watch } from '@vue/composition-api';
import { defaultTooltipModifiers, ITooltipModifier, IWalletHTMLElements } from '..';
import { IOnboardingGetStepFnArgs, ITourStep, OnboardingTourStep } from '../types';
import { getOnboardingTexts } from './OnboardingTourTexts';

export function getTransactionListStep(
{ isSmallScreen, toggleHighlightButton, txsLen }: IOnboardingGetStepFnArgs): ITourStep {
const freeNimBtn = ref(() =>
document.querySelector(IWalletHTMLElements.BUTTON_ADDRESS_OVERVIEW_RECEIVE_FREE_NIM) as HTMLDivElement);

const ui: ITourStep['ui'] = {
fadedElements: [
IWalletHTMLElements.SIDEBAR_TESTNET,
Expand Down Expand Up @@ -66,8 +69,8 @@ export function getTransactionListStep(
: '.address-overview';
},
get content() {
return getOnboardingTexts(
OnboardingTourStep.TRANSACTION_LIST)[txsLen.value === 0 ? 'default' : 'alternative'] || [];
const textType = txsLen.value === 0 && freeNimBtn.value ? 'default' : 'alternative';
return getOnboardingTexts(OnboardingTourStep.TRANSACTION_LIST)[textType] || [];
},
params: {
get placement() {
Expand All @@ -90,17 +93,41 @@ export function getTransactionListStep(
},
},
lifecycle: {
mounted: ({ goToNextStep }) => {
mounted: ({ goToNextStep, isNextStepDisabled }) => {
if (txsLen.value > 0) return undefined;

function freeNimBtnClickHandler() {
setTimeout(() => {
// User in the mainnet is expected to click on the 'Get Free NIM' button
// This button opens the faucet page and the user exits the wallet. Once the user finishes, he
// is expected to return to the wallet and automatically the tour will go to the next step.
// There might be a possibility that the user gets an error in the faucet, and therefore, the
// tour will not continue. In those cases we will let the user click 'Next step' but it will
// actually skip the third step and go to the fourth step
// FIXME: In the future, if we integrate the faucet into the wallet, we could improve this
// behaviour
if (txsLen.value > 0) return;
isNextStepDisabled.value = false;
}, 2000);
}

if (freeNimBtn.value() !== null) {
freeNimBtn.value().addEventListener('click', freeNimBtnClickHandler, { once: true });
} else {
isNextStepDisabled.value = false;
}

const unwatch = watch(txsLen, (newVal) => {
if (newVal > 0) goToNextStep();
});

// Add hightlight effect to 'Get Free NIM' button
// Add hightlight effect to 'Get Free NIM' button. This will be ignored if the user have at least one tx
toggleHighlightButton(IWalletHTMLElements.BUTTON_ADDRESS_OVERVIEW_RECEIVE_FREE_NIM, true, 'green');
return () => {
unwatch();
if (freeNimBtn.value()) {
freeNimBtn.value().removeEventListener('click', freeNimBtnClickHandler);
}
return toggleHighlightButton(
IWalletHTMLElements.BUTTON_ADDRESS_OVERVIEW_RECEIVE_FREE_NIM, false, 'green');
};
Expand All @@ -110,7 +137,7 @@ export function getTransactionListStep(
return {
...ui,

// User is expected to click on the 'Get Free NIM' button
// User is expected to click on the 'Get Free NIM' button if they have not txs
isNextStepDisabled: txsLen.value === 0,
};
},
Expand Down
14 changes: 14 additions & 0 deletions src/lib/tour/onboarding/03_FirstTransactionStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ export function getFirstTransactionStep({ isSmallScreen, txsLen }: IOnboardingGe
get path() {
return isSmallScreen.value ? '/transactions' : '/';
},
lifecycle: {
created: ({ goToNextStep, goToPrevStep, goingForward }) => {
// Might be the case that user has no transactions yet and still he reaches this step nonetheless.
// This is because if NIM Faucet fails, we let the user continue to the next step. If that happens,
// we need to skip this step and go to the next/prev one
if (txsLen.value === 0) {
if (goingForward) {
goToNextStep({ withDelay: false });
} else {
goToPrevStep({ withDelay: false });
}
}
},
},
tooltip: {
get target() {
return `${IWalletHTMLElements.ADDRESS_OVERVIEW_TRANSACTIONS} ${isSmallScreen.value
Expand Down
14 changes: 11 additions & 3 deletions src/lib/tour/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,19 @@ export interface ITooltipModifier {
options: any;
}

export interface FutureStepArgs {
withDelay: boolean;
}

export interface ILifecycleArgs {
goToNextStep: () => void;
goToNextStep: (args?: FutureStepArgs) => void;
goToPrevStep: (args?: FutureStepArgs) => void;
goingForward: boolean;
ending: boolean;
isNextStepDisabled: Ref<boolean>;
}

export type IUnmountedFn = ((args: Omit<ILifecycleArgs, 'goToNextStep'>) => Promise<void> | void);
export type IUnmountedFn = ((args: Omit<ILifecycleArgs, 'goToNextStep' | 'goToPrevStep'>) => Promise<void> | void);

// This interface is the main interface for a step in the tour. It consists of:
// - path - the route that the step should be shown on
Expand Down Expand Up @@ -200,7 +206,9 @@ export enum IWalletHTMLElements {
BUTTON_SIDEBAR_BUY = '.sidebar .trade-actions button:nth-child(1)',
BUTTON_SIDEBAR_SELL = '.sidebar .trade-actions button:nth-child(2)',
BUTTON_ADDRESS_OVERVIEW_BUY = '.address-overview .transaction-list .after-first-tx button',
BUTTON_ADDRESS_OVERVIEW_RECEIVE_FREE_NIM = '.address-overview .transaction-list .empty-state button',
BUTTON_ADDRESS_OVERVIEW_RECEIVE_FREE_NIM = // The comma in document.querySelector works like an OR gate
'.address-overview .transaction-list .empty-state a,' // Mainnet uses an a element
+ '.address-overview .transaction-list .empty-state button', // Devnet uses a button element
BUTTON_ADDRESS_BACKUP_ALERT = '.account-overview .backup-warning button',

MODAL_CONTAINER = '.modal.backdrop',
Expand Down

0 comments on commit 35e8301

Please sign in to comment.