Skip to content

Commit

Permalink
feat: add new connection statuses
Browse files Browse the repository at this point in the history
  • Loading branch information
CedrikNikita committed Aug 7, 2024
1 parent 2e61f09 commit 2d1bda6
Show file tree
Hide file tree
Showing 14 changed files with 362 additions and 143 deletions.
136 changes: 136 additions & 0 deletions src/composables/connectionStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { computed, ref, watch } from 'vue';
import { TranslateResult } from 'vue-i18n';
import { useRoute } from 'vue-router';

import {
useAccounts,
useConnection,
useAeSdk,
useMultisigAccounts,
} from '@/composables';
import { StatusIconType } from '@/types';
import { ROUTE_ACCOUNT } from '@/popup/router/routeNames';
import { useAeMiddleware, useAeTippingBackend } from '@/protocols/aeternity/composables';
import { tg as t } from '@/popup/plugins/i18n';

interface StatusType {
statusMessage: TranslateResult;
title?: TranslateResult;
description?: TranslateResult;
icon?: StatusIconType;
}

const CONNECTED_DISPLAY_TIME = 2000;

const middlewareUnsynchronizedThreshold = 5;

export function useConnectionStatus() {
const { isOnline } = useConnection();
const { isAeNodeConnecting, isAeNodeReady, isAeNodeError } = useAeSdk();
const { isLoggedIn } = useAccounts();
const { middlewareStatus, isMiddlewareUnavailable } = useAeMiddleware();
const { isMultisigBackendUnavailable } = useMultisigAccounts({ pollingDisabled: true });
const { isBackendUnavailable } = useAeTippingBackend();
const route = useRoute();

const justBeenConnected = ref(false);

const showMultisigError = computed(
() => route.name === ROUTE_ACCOUNT && isMultisigBackendUnavailable.value,
);
const isError = computed(() => (
!isOnline.value
|| isAeNodeError.value
|| isMiddlewareUnavailable.value
|| (middlewareStatus.value && !middlewareStatus.value.mdwSynced)
|| showMultisigError.value
|| isBackendUnavailable.value
));

// Display "Connected" message for a while after connecting to node.
watch(isAeNodeReady, (val) => {
justBeenConnected.value = val;
if (val) {
setTimeout(() => {
justBeenConnected.value = false;
}, CONNECTED_DISPLAY_TIME);
}
});

const status = computed((): StatusType | null => {
switch (true) {
case !isOnline.value:
return {
statusMessage: t('connectionStatus.offline.statusMessage'),
title: t('connectionStatus.offline.title'),
description: t('connectionStatus.offline.description'),
icon: 'warning',
};
case !isLoggedIn.value:
return null;
case isAeNodeConnecting.value:
return {
statusMessage: t('connectionStatus.node.connecting'),
};
case justBeenConnected.value:
return {
statusMessage: t('connectionStatus.node.connected'),
};
case isAeNodeError.value:
return {
statusMessage: t('connectionStatus.node.error.statusMessage'),
title: t('connectionStatus.node.error.title'),
description: t('connectionStatus.node.error.description'),
icon: 'critical',
};
case isMiddlewareUnavailable.value:
return {
statusMessage: t('connectionStatus.middleware.error.title'),
title: t('connectionStatus.middleware.error.title'),
description: t('connectionStatus.middleware.error.description'),
icon: 'critical',
};
case isBackendUnavailable.value && isMultisigBackendUnavailable.value:
return {
statusMessage: t('connectionStatus.backend.statusMessage'),
title: t('connectionStatus.backend.title'),
description: t('connectionStatus.backend.description'),
icon: 'critical',
};
case showMultisigError.value:
return {
statusMessage: t('connectionStatus.multisig.statusMessage'),
title: t('connectionStatus.multisig.title'),
description: t('connectionStatus.multisig.description'),
icon: 'critical',
};
case isBackendUnavailable.value:
return {
statusMessage: t('connectionStatus.aeternityBackend.statusMessage'),
title: t('connectionStatus.aeternityBackend.title'),
description: t('connectionStatus.aeternityBackend.description'),
icon: 'critical',
};
case (
middlewareStatus.value
&& !middlewareStatus.value.mdwSynced
&& (
middlewareStatus.value.nodeHeight - middlewareStatus.value.mdwHeight
) > middlewareUnsynchronizedThreshold
):
return {
statusMessage: t('connectionStatus.middleware.syncing.statusMessage'),
title: t('connectionStatus.middleware.syncing.title'),
description: t('connectionStatus.middleware.syncing.description'),
icon: 'warning',
};
default:
return null;
}
});

return {
isError,
status,
};
}
2 changes: 1 addition & 1 deletion src/composables/latestTransactionList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function useLatestTransactionList() {
}

if (
regularTransactions.length
regularTransactions?.length
|| pendingTransactions?.length
|| tipWithdrawnTransactions?.length
) {
Expand Down
19 changes: 10 additions & 9 deletions src/composables/multisigAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface MultisigAccountsOptions {

let multisigContractInstances: Dictionary<Contract<SimpleGAMultiSigContractApi>> = {};
let composableInitialized = false;
const isMultisigBackendUnavailable = ref(false);

const POLLING_INTERVAL = 12000;

Expand Down Expand Up @@ -257,10 +258,9 @@ export function useMultisigAccounts({
await Promise.all(aeAccounts.value.map(async ({ address }) => rawMultisigData.push(
...(await fetchJson(`${aeActiveNetworkPredefinedSettings.value.multisigBackendUrl}/${address}`)),
)));
isMultisigBackendUnavailable.value = false;
} catch {
// TODO: handle failure in multisig loading
// eslint-disable-next-line no-console
console.log('failed to fetch multisigAccounts');
isMultisigBackendUnavailable.value = true;
}

rawMultisigData = uniqBy(rawMultisigData, 'contractId');
Expand Down Expand Up @@ -339,19 +339,20 @@ export function useMultisigAccounts({
}

return {
multisigAccounts: allMultisigAccounts,
pendingMultisigAccounts,
isAdditionalInfoNeeded,
isActiveMultisigAccountPending,
activeMultisigAccountId,
activeMultisigAccount,
activeMultisigAccountExplorerUrl,
isAdditionalInfoNeeded,
isActiveMultisigAccountPending,
isMultisigBackendUnavailable,
multisigAccounts: allMultisigAccounts,
pendingMultisigAccounts,
addPendingMultisigAccount,
addTransactionToPendingMultisigAccount,
fetchAdditionalInfo,
getMultisigAccountByContractId,
setActiveMultisigAccountId,
stopFetchingAdditionalInfo,
updateMultisigAccounts,
getMultisigAccountByContractId,
addPendingMultisigAccount,
};
}
6 changes: 3 additions & 3 deletions src/popup/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
class="main"
/>

<NodeConnectionStatus
<ConnectionStatus
v-if="!modalsOpen.length"
class="connection-status"
/>
Expand Down Expand Up @@ -93,15 +93,15 @@ import {
import { useTransferSendHandler } from '@/composables/transferSendHandler';
import Header from '@/popup/components/Header.vue';
import NodeConnectionStatus from '@/popup/components/NodeConnectionStatus.vue';
import ConnectionStatus from '@/popup/components/ConnectionStatus.vue';
import Loader from '@/popup/components/Loader.vue';
import QrCodeReaderMobileOverlay from '@/popup/components/QrCodeReaderMobileOverlay.vue';
export default defineComponent({
name: 'App',
components: {
Header,
NodeConnectionStatus,
ConnectionStatus,
QrCodeReaderMobileOverlay,
IonApp,
IonRouterOutlet,
Expand Down
79 changes: 79 additions & 0 deletions src/popup/components/ConnectionStatus.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<transition name="fade-transition">
<div
v-if="status"
data-cy="connection-status"
class="connection-status"
:class="{
'is-error': isError,
}"
>
{{ status?.statusMessage }}
<BtnHelp
v-if="status.title"
:title="status.title"
:msg="status.description"
:icon="status.icon"
warning
class="btn-help"
:class="{
'is-error': isError,
}"
/>
</div>
</transition>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { useConnectionStatus } from '@/composables/connectionStatus';

import BtnHelp from './buttons/BtnHelp.vue';

export default defineComponent({
components: { BtnHelp },
setup() {
const { isError, status } = useConnectionStatus();

return {
isError,
status,
};
},
});
</script>

<style lang="scss" scoped>
@use '@/styles/variables' as *;
@use '@/styles/typography';

.connection-status {
@extend %face-sans-15-medium;

display: flex;
align-items: center;
justify-content: center;
min-height: 40px;
padding: 4px 10px;
color: $color-white;
backdrop-filter: blur($bg-blur-radius);
text-align: center;

&::before {
content: '';
position: absolute;
z-index: -1;
inset: 0;
background: $color-bg-3;
opacity: 0.8;
}

&.is-error {
color: $color-warning;
}

.btn-help {
margin-left: 4px;
}
}
</style>
48 changes: 27 additions & 21 deletions src/popup/components/Modals/Help.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,35 @@ export default {
<style lang="scss">
@use '@/styles/variables' as *;
.help-modal .help-template-renderer ol {
text-align: left;
list-style-type: none;
counter-reset: list-number;
.help-modal .help-template-renderer {
p {
margin-top: 8px;
}
ol {
text-align: left;
list-style-type: none;
counter-reset: list-number;
li {
margin-bottom: 18px;
li {
margin-bottom: 18px;
&::before {
counter-increment: list-number;
content: counter(list-number);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 2px;
width: 24px;
height: 24px;
position: absolute;
left: 24px;
background: rgba($color-white, 0.03);
border: 2px solid rgba($color-white, 0.15);
border-radius: 28px;
&::before {
counter-increment: list-number;
content: counter(list-number);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 2px;
width: 24px;
height: 24px;
position: absolute;
left: 24px;
background: rgba($color-white, 0.03);
border: 2px solid rgba($color-white, 0.15);
border-radius: 28px;
}
}
}
}
Expand Down
Loading

0 comments on commit 2d1bda6

Please sign in to comment.