Skip to content

Commit

Permalink
feat: Network indicator use get node info (#893)
Browse files Browse the repository at this point in the history
* feat: Add getNodeInfo support bridge to wallet lib

* fix: Wallet api getNodeInfo definition

* feat: Use getNodeInfo for network status

* feat: Add show network status indicator setting

* fix: Update cargo.lock

* fix: Remove redundant code

* fix: Node info type check

* fix: Match cargo.lock with develop

* fix: Change settings to only hide the statistics
  • Loading branch information
obany authored Apr 29, 2021
1 parent 700c752 commit 6de0ef9
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 73 deletions.
2 changes: 0 additions & 2 deletions packages/desktop/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import { goto } from 'shared/lib/helpers'
import { dir, isLocaleLoaded, setupI18n, _ } from 'shared/lib/i18n'
import { pollMarketData } from 'shared/lib/marketData'
import { pollNetworkStatus } from 'shared/lib/networkStatus'
import { openPopup, popupState } from 'shared/lib/popup'
import { cleanupEmptyProfiles, cleanupInProgressProfiles } from 'shared/lib/profile'
import { dashboardRoute, initRouter, routerNext, routerPrevious, walletRoute } from 'shared/lib/router'
Expand Down Expand Up @@ -61,7 +60,6 @@
}, 3000)
await pollMarketData()
await pollNetworkStatus()
// @ts-ignore: This value is replaced by Webpack DefinePlugin
if (!devMode) {
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/components/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

<aside
class="flex flex-col justify-center items-center bg-white dark:bg-gray-800 h-screen relative w-20 px-5 pb-9 pt-9 border-solid border-r border-gray-100 dark:border-gray-800">
<Logo classes="logo mb-9 {hasTitleBar ? "mt-3": ""}" width="48px" logo="logo-firefly" />
<Logo classes="logo mb-9 {hasTitleBar ? 'mt-3' : ''}" width="48px" logo="logo-firefly" />
<nav class="flex flex-grow flex-col items-center justify-between">
<button class={$dashboardRoute === Tabs.Wallet ? 'text-blue-500' : 'text-gray-500'} on:click={() => openWallet()}>
<Icon icon="wallet" />
Expand Down
32 changes: 18 additions & 14 deletions packages/shared/components/modals/NetworkIndicator.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script lang="typescript">
import { onDestroy } from 'svelte'
import { Text, Modal, HR } from 'shared/components'
import { HR, Modal, Text } from 'shared/components'
import { networkStatus } from 'shared/lib/networkStatus'
import { activeProfile } from 'shared/lib/profile'
import { onDestroy } from 'svelte'
export let isActive
export let locale
let healthStatus = 2
let healthStatusText = 'networkOperational'
let messagesPerSecond = 0
let confirmationRate = 0
let referencedRate = 0
const NETWORK_HEALTH_COLORS = {
0: 'red',
Expand All @@ -18,8 +20,8 @@
const unsubscribe = networkStatus.subscribe((data) => {
healthStatus = data.health ?? 0
healthStatusText = healthStatus === 0 ? 'networkDown' : healthStatus === 1 ? 'networkDegraded' : 'networkOperational'
messagesPerSecond = data.itemsPerSecond ?? 0
confirmationRate = data.confirmationRate ?? 0
messagesPerSecond = data.messagesPerSecond ?? 0
referencedRate = data.referencedRate ?? 0
})
onDestroy(() => {
Expand All @@ -33,14 +35,16 @@
<div class="px-7 pb-5 text-13 text-{NETWORK_HEALTH_COLORS[healthStatus]}-500">
{locale(`views.dashboard.network.${healthStatusText}`)}
</div>
<HR />
<div class="flex flex-row justify-between px-7 pt-5 pb-2">
<span class="text-12 text-gray-800 dark:text-white">{locale('views.dashboard.network.messagesPerSecond')}</span>
<span class="text-12 text-gray-500">{`${Math.round(messagesPerSecond)}`}</span>
</div>
<div class="flex flex-row justify-between px-7 pb-5">
<span class="text-12 text-gray-800 dark:text-white">{locale('views.dashboard.network.confirmationRate')}</span>
<span class="text-12 text-gray-500">{`${Math.round(confirmationRate)}%`}</span>
</div>
{#if !$activeProfile?.settings.hideNetworkStatistics}
<HR />
<div class="flex flex-row justify-between px-7 pt-5 pb-2">
<span class="text-12 text-gray-800 dark:text-white">{locale('views.dashboard.network.messagesPerSecond')}</span>
<span class="text-12 text-gray-500">{`${Math.round(messagesPerSecond)}`}</span>
</div>
<div class="flex flex-row justify-between px-7 pb-5">
<span class="text-12 text-gray-800 dark:text-white">{locale('views.dashboard.network.referencedRate')}</span>
<span class="text-12 text-gray-500">{`${Math.round(referencedRate)}%`}</span>
</div>
{/if}
</network-indicator-content>
</Modal>
98 changes: 43 additions & 55 deletions packages/shared/lib/networkStatus.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,40 @@
import { writable } from 'svelte/store'

/**
* Network status endpoints list
*/
export const NETWORK_STATUS_ENDPOINTS = [
'https://explorer-api.iota.org/stats/chrysalis',
'https://explorer-api.iota.works/stats/chrysalis',
]

/**
* Default timeout for a request made to an endpoint
*/
const DEFAULT_NETWORK_STATUS_ENDPOINT_TIMEOUT = 5000
import { get, writable } from 'svelte/store'
import { asyncGetNodeInfo, wallet } from "shared/lib/wallet"
import { getOfficialNodes } from 'shared/lib/network'

/**
* Default interval for polling the network status
*/
const DEFAULT_NETWORK_STATUS_POLL_INTERVAL = 10000

type StatusData = {
itemsPerSecond?: number
confirmedItemsPerSecond?: number
confirmationRate?: number
latestMilestoneIndex?: number
latestMilestoneIndexTime?: number
messagesPerSecond?: number
referencedRate?: number
health?: number
healthReason?: string
}

export const networkStatus = writable<StatusData>({})

let pollInterval

/**
* Poll the network status at an interval.
*/
export async function pollNetworkStatus(): Promise<void> {
await fetchNetworkStatus()
setInterval(async () => fetchNetworkStatus(), DEFAULT_NETWORK_STATUS_POLL_INTERVAL)
pollInterval = setInterval(async () => fetchNetworkStatus(), DEFAULT_NETWORK_STATUS_POLL_INTERVAL)
}

const { accounts, accountsLoaded } = get(wallet)

accountsLoaded.subscribe((val) => {
if (val) {
pollNetworkStatus()
} else {
clearInterval(pollInterval)
}
})

/**
* Fetches network status data
*
Expand All @@ -46,52 +43,43 @@ export async function pollNetworkStatus(): Promise<void> {
* @returns {Promise<void>}
*/
export async function fetchNetworkStatus(): Promise<void> {
const requestOptions: RequestInit = {
headers: {
Accept: 'application/json',
},
}

let updated = false

for (let index = 0; index < NETWORK_STATUS_ENDPOINTS.length; index++) {
const endpoint = NETWORK_STATUS_ENDPOINTS[index]

try {
const abortController = new AbortController()
const timerId = setTimeout(
() => {
if (abortController) {
abortController.abort();
}
},
DEFAULT_NETWORK_STATUS_ENDPOINT_TIMEOUT);

requestOptions.signal = abortController.signal;
const accs = get(accounts)

const response = await fetch(endpoint, requestOptions);
if (accs.length > 0) {
const account0 = accs[0]
const clientOptions = account0.clientOptions
const node = clientOptions.node ?? getOfficialNodes()[0]

clearTimeout(timerId)

const statusData: StatusData = await response.json()

networkStatus.set(statusData)
try {
// TODO add user/pass support when implemented in wallet.rs
const response = await asyncGetNodeInfo(account0.id, node.url)

const timeSinceLastMsInMinutes = (Date.now() - (response.nodeinfo.latestMilestoneTimestamp * 1000)) / 60000;
let health = 0; //bad
if (timeSinceLastMsInMinutes < 2) {
health = 2; // good
} else if (timeSinceLastMsInMinutes < 5) {
health = 1; // degraded
}

networkStatus.set({
messagesPerSecond: response.nodeinfo.messagesPerSecond,
referencedRate: response.nodeinfo.referencedRate,
health
})

updated = true

break
} catch (err) {
console.error(err.name === "AbortError" ? new Error(`Could not fetch from ${endpoint}.`) : err)
console.error(err.name === "AbortError" ? new Error(`Could not fetch from ${node.url}.`) : err)
}
}

if (!updated) {
networkStatus.set({
itemsPerSecond: 0,
confirmedItemsPerSecond: 0,
confirmationRate: 0,
latestMilestoneIndex: 0,
latestMilestoneIndexTime: 0,
messagesPerSecond: 0,
referencedRate: 0,
health: 0
})
}
Expand Down
1 change: 1 addition & 0 deletions packages/shared/lib/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface UserSettings {
lockScreenTimeout: number
showHiddenAccounts?: boolean
chartSelectors: ChartSelectors
hideNetworkStatistics?: boolean
}

export const activeProfileId = writable<string | null>(null)
Expand Down
1 change: 1 addition & 0 deletions packages/shared/lib/typings/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum SettingsIcons {
language = 'language',
currency = 'currency',
notifications = 'bell',
networkStatus = 'view',
exportStronghold = 'export',
appLock = 'lock3',
changePassword = 'lock2',
Expand Down
1 change: 1 addition & 0 deletions packages/shared/lib/typings/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export enum GeneralSettings {
Language = 'language',
Currency = 'currency',
Notifications = 'notifications',
NetworkStatus = 'networkStatus',
}

export enum GeneralSettingsNoProfile {
Expand Down
13 changes: 13 additions & 0 deletions packages/shared/lib/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,19 @@ export const asyncSyncAccounts = (addressIndex?, gapLimit?, accountDiscoveryThre
})
}

export const asyncGetNodeInfo = (accountId, url) => {
return new Promise<NodeInfo>((resolve, reject) => {
api.getNodeInfo(accountId, url, {
onSuccess(response) {
resolve(response.payload)
},
onError(err) {
reject(err)
},
})
})
}

/**
* Initialises event listeners from wallet library
*
Expand Down
7 changes: 6 additions & 1 deletion packages/shared/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@
"title": "Notifications",
"description": "System notifications for transaction events"
},
"networkStatus": {
"title": "Network status",
"description": "Displays the status of the node you are currently connected to."
},
"exportStronghold": {
"title": "Export Backup",
"description": "Export to a Stronghold file - a complete encrypted backup of your wallets and latest transaction history"
Expand Down Expand Up @@ -371,7 +375,7 @@
"networkDown": "Network Disconnected",
"status": "Status",
"messagesPerSecond": "Messages per second",
"confirmationRate": "Confirmation rate"
"referencedRate": "Referenced rate"
},
"profileModal": {
"allSettings": "All settings",
Expand Down Expand Up @@ -583,6 +587,7 @@
"viewStatus": "View status",
"showHiddenAccounts": "Show hidden wallets",
"confirm": "Confirm",
"hideNetworkStatistics": "Hide network statistics",
"findBalances": "Find balances",
"searchBalances": "Search for balances",
"searchAgain": "Search again",
Expand Down
10 changes: 10 additions & 0 deletions packages/shared/routes/dashboard/settings/views/General.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
let darkModeEnabled = $appSettings.darkMode
let notificationsChecked = $appSettings.notifications
let hideNetworkStatistics = $activeProfile?.settings.hideNetworkStatistics
$: $appSettings.darkMode = darkModeEnabled
$: $appSettings.notifications = notificationsChecked
$: updateProfile('settings.hideNetworkStatistics', hideNetworkStatistics)
const handleCurrencySelect = (item) => {
updateProfile('settings.currency', item.value)
Expand Down Expand Up @@ -72,4 +74,12 @@
<Text type="p" secondary classes="mb-5">{locale('views.settings.notifications.description')}</Text>
<Checkbox label={locale('actions.enableSystemNotifications')} bind:checked={notificationsChecked} />
</section>
{#if $loggedIn}
<HR classes="pb-5 mt-5 justify-center" />
<section id="networkStatus" class="w-3/4">
<Text type="h4" classes="mb-3">{locale('views.settings.networkStatus.title')}</Text>
<Text type="p" secondary classes="mb-5">{locale('views.settings.networkStatus.description')}</Text>
<Checkbox label={locale('actions.hideNetworkStatistics')} bind:checked={hideNetworkStatistics} />
</section>
{/if}
</div>

0 comments on commit 6de0ef9

Please sign in to comment.