From 238e28cca9c1942eb2e8b1785ad818137be77338 Mon Sep 17 00:00:00 2001 From: bludnic Date: Sat, 3 Aug 2024 04:41:29 +0100 Subject: [PATCH] feat: show a snackbar and redirect to Nodes page when no active nodes --- src/components/LoginForm.vue | 7 ++++- src/lib/nodes/abstract.client.ts | 7 ++++- src/lib/nodes/adm/AdmClient.ts | 44 ++++++++------------------------ src/lib/nodes/utils/errors.ts | 18 ++++++++++++- src/locales/en.json | 3 ++- src/locales/ru.json | 3 ++- src/views/Login.vue | 3 ++- 7 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 1d448ac86..480804a10 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -55,8 +55,9 @@ import { validateMnemonic } from 'bip39' import { computed, ref, defineComponent } from 'vue' import { useStore } from 'vuex' +import { useRouter } from 'vue-router' import { isAxiosError } from 'axios' -import { isAllNodesOfflineError } from '@/lib/nodes/utils/errors' +import { isAllNodesOfflineError, isAllNodesDisabledError } from '@/lib/nodes/utils/errors' export default defineComponent({ props: { @@ -67,6 +68,7 @@ export default defineComponent({ }, emits: ['login', 'error', 'update:modelValue'], setup(props, { emit }) { + const router = useRouter() const store = useStore() const showSpinner = ref(false) @@ -104,6 +106,9 @@ export default defineComponent({ emit('error', 'login.invalid_passphrase') } else if (isAllNodesOfflineError(err)) { emit('error', 'errors.all_nodes_offline') + } else if (isAllNodesDisabledError(err)) { + emit('error', 'errors.all_nodes_disabled') + router.push({ name: 'Nodes' }) } else { emit('error', 'errors.something_went_wrong') } diff --git a/src/lib/nodes/abstract.client.ts b/src/lib/nodes/abstract.client.ts index 0f7e61fc3..1edf8a3a2 100644 --- a/src/lib/nodes/abstract.client.ts +++ b/src/lib/nodes/abstract.client.ts @@ -1,5 +1,5 @@ import type { HealthcheckInterval, NodeKind, NodeType } from '@/lib/nodes/types' -import { AllNodesOfflineError } from './utils/errors' +import { AllNodesDisabledError, AllNodesOfflineError } from './utils/errors' import { filterSyncedNodes } from './utils/filterSyncedNodes' import { Node } from './abstract.node' import { nodesStorage } from './storage' @@ -137,6 +137,11 @@ export abstract class Client { } protected getNode() { + const nodes = this.nodes.filter((node) => node.active) + if (nodes.length === 0) { + throw new AllNodesDisabledError(this.type) + } + const node = this.useFastest ? this.getFastestNode() : this.getRandomNode() if (!node) { // All nodes seem to be offline: let's refresh the statuses diff --git a/src/lib/nodes/adm/AdmClient.ts b/src/lib/nodes/adm/AdmClient.ts index 54a439687..f4349b53f 100644 --- a/src/lib/nodes/adm/AdmClient.ts +++ b/src/lib/nodes/adm/AdmClient.ts @@ -3,8 +3,6 @@ import { GetHeightResponseDto } from '@/lib/schema/client' import { AdmNode, Payload, RequestConfig } from './AdmNode' import { Client } from '../abstract.client' -const CHECK_ONLINE_NODE_INTERVAL = 10000 - /** * Provides methods for calling the ADAMANT API. * @@ -44,39 +42,17 @@ export class AdmClient extends Client { * @param {RequestConfig} config request config */ async request

(config: RequestConfig

): Promise { - const node = await this.fetchAvailableNode() - - return node.request(config).catch((error) => { - if (isNodeOfflineError(error)) { - // Initiate nodes status check - this.checkHealth() - // If the selected node is not available, repeat the request with another one. - return this.request(config) - } - throw error - }) - } - - async fetchAvailableNode() { - const node = this.useFastest ? this.getFastestNode() : this.getRandomNode() - if (node) { - return node - } - - return await new Promise((resolve) => { - const ticker = setInterval(() => { - let node - try { - node = this.useFastest ? this.getFastestNode() : this.getRandomNode() - if (node) { - clearInterval(ticker) - resolve(node) - } - } catch (e) { - console.error(e) + return this.getNode() + .request(config) + .catch((error) => { + if (isNodeOfflineError(error)) { + // Initiate nodes status check + this.checkHealth() + // If the selected node is not available, repeat the request with another one. + return this.request(config) } - }, CHECK_ONLINE_NODE_INTERVAL) - }) + throw error + }) } async getHeight() { diff --git a/src/lib/nodes/utils/errors.ts b/src/lib/nodes/utils/errors.ts index 3190f1758..ebf106c64 100644 --- a/src/lib/nodes/utils/errors.ts +++ b/src/lib/nodes/utils/errors.ts @@ -2,7 +2,8 @@ import { NodeType } from '@/lib/nodes/types' const CODES = { NODE_OFFLINE: 'NODE_OFFLINE', - ALL_NODES_OFFLINE: 'ALL_NODES_OFFLINE' + ALL_NODES_OFFLINE: 'ALL_NODES_OFFLINE', + ALL_NODES_DISABLED: 'ALL_NODES_DISABLED' } as const /** @@ -47,3 +48,18 @@ export class AllNodesOfflineError extends Error { export function isAllNodesOfflineError(error: Error): error is AllNodesOfflineError { return (error as AllNodesOfflineError).code === CODES.ALL_NODES_OFFLINE } + +export class AllNodesDisabledError extends Error { + code = CODES.ALL_NODES_DISABLED + nodeLabel: NodeType + + constructor(label: NodeType) { + super(`${label}: All nodes are disabled`) + + this.nodeLabel = label + } +} + +export function isAllNodesDisabledError(error: Error): error is AllNodesDisabledError { + return (error as AllNodesDisabledError).code === CODES.ALL_NODES_DISABLED +} diff --git a/src/locales/en.json b/src/locales/en.json index 43359e31a..21ee2ebfd 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -99,7 +99,8 @@ "error": "Error", "errors": { "something_went_wrong": "Something went wrong. Check the console for details", - "all_nodes_offline": "All {crypto} nodes are offline. Try again later" + "all_nodes_offline": "All {crypto} nodes are offline. Try again later", + "all_nodes_disabled": "All {crypto} nodes are disabled. Please enable at least one node to continue using the Messenger" }, "home": { "balance": "Balance", diff --git a/src/locales/ru.json b/src/locales/ru.json index 9a95fca9e..7a44f5a4d 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -99,7 +99,8 @@ "error": "Ошибка", "errors": { "something_went_wrong": "Что-то пошло не так. Детали ошибки в консоли", - "all_nodes_offline": "Все {crypto} ноды недоступны. Попробуйте позже" + "all_nodes_offline": "Все {crypto} ноды недоступны. Попробуйте позже", + "all_nodes_disabled": "Все {crypto} ноды отключены. Пожалуйста, включите хотя бы один узел, чтобы продолжить пользоваться мессенджером" }, "home": { "balance": "Баланс", diff --git a/src/views/Login.vue b/src/views/Login.vue index 1d0cbf44c..5f8193d3a 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -154,7 +154,8 @@ export default defineComponent({ } const onLoginError = (key) => { store.dispatch('snackbar/show', { - message: t(key) + message: t(key), + timeout: 3000 }) } const onCopyPassphrase = () => {