Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/service tab #630

Merged
merged 25 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
89a11b2
feat: info-service tab
RealGoodProgrammer May 29, 2024
833e831
fix: toggle active
RealGoodProgrammer May 29, 2024
167a3c3
Merge branch 'refs/heads/dev' into feat/service-tab
RealGoodProgrammer Jul 12, 2024
d532716
fix(RateInfoService.ts): incorrect label
RealGoodProgrammer Jul 12, 2024
c6eaf6e
fix: useFastest checkbox, refactoring (node kind)
RealGoodProgrammer Jul 12, 2024
b86f8a4
fix(NodesTableHead): label as a property
RealGoodProgrammer Jul 12, 2024
34f1ca6
fix(rate-actions): removed Promise wrapper
RealGoodProgrammer Jul 12, 2024
7bb6218
fix(rate-mutations): rename loadRates -> setLoaded
RealGoodProgrammer Jul 12, 2024
b1522d9
fix: rename 'rates-info' -> ratesInfo
RealGoodProgrammer Jul 13, 2024
70503a2
fix(nodes/storage): access of undefined value when switching from an …
bludnic Jul 13, 2024
8c9bf26
refactor(nodes/storage): reuse code
bludnic Jul 13, 2024
f7f3e61
fix(nodes/storage): modify only `useFastest` option
bludnic Jul 13, 2024
21e6312
refactor(NodesTable): move Service Nodes tab to the end
bludnic Jul 13, 2024
a6d15f2
refactor(NodesTable): shorter import
bludnic Jul 13, 2024
ec9c962
refactor(NodesTable): move npm packages imports at the top
bludnic Jul 13, 2024
3016de6
refactor(NodesTableHead.vue): remove unused `hideLabel` prop
bludnic Jul 13, 2024
c630080
fix: use relative import and extract RateInfoService types
PaulDremanovich Jul 14, 2024
6930f95
fix: refactor requests
PaulDremanovich Jul 14, 2024
0a61070
fix: active checkbox
PaulDremanovich Jul 14, 2024
f9f39ce
fix: relative imports
PaulDremanovich Jul 14, 2024
0731a50
feat: healthcheck for BtcNode. DogeNode doesn`t supported yet
PaulDremanovich Jul 15, 2024
1990e2d
fix: healthcheck for DogeNode. Refactoring
PaulDremanovich Jul 16, 2024
da7faba
feat: added btcIndexer, dogeIndexer, ethIndexer, klyIndexer in settin…
PaulDremanovich Jul 16, 2024
8a13693
Merge branch 'refs/heads/dev' into feat/service-tab
bludnic Aug 1, 2024
f9614c0
chore(nodes): move methods to indexers and add TS types
bludnic Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/components/nodes/NodesTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
<div :class="classes.root">
<v-tabs v-model="tab" bg-color="transparent">
<v-tab value="adm">{{ $t('nodes.tabs.adm_nodes') }}</v-tab>
<v-tab value="services">{{ $t('nodes.tabs.service_nodes') }}</v-tab>
<v-tab value="coins">{{ $t('nodes.tabs.coin_nodes') }}</v-tab>
</v-tabs>

<v-window v-model="tab">
<v-window-item value="adm">
<AdmNodesTable />
</v-window-item>

<v-window-item value="services">
<ServiceNodesTable />
</v-window-item>
<v-window-item value="coins">
<CoinNodesTable />
</v-window-item>
Expand All @@ -29,6 +32,20 @@
</div>
<div>&nbsp;<br />&nbsp;</div>
</div>
<div v-else-if="tab === 'services'">
<v-checkbox
v-model="preferFastestCoinNodeOption"
RealGoodProgrammer marked this conversation as resolved.
Show resolved Hide resolved
:label="$t('nodes.fastest_title')"
:class="classes.checkbox"
class="mt-4"
color="grey darken-1"
hide-details
/>
<div class="a-text-explanation-enlarged">
{{ $t('nodes.fastest_tooltip') }}
</div>
<div>&nbsp;<br />&nbsp;</div>
</div>
<div v-else-if="tab === 'adm'">
<v-checkbox
v-model="preferFastestAdmNodeOption"
Expand Down Expand Up @@ -72,6 +89,7 @@ import { defineComponent, ref, computed } from 'vue'
import { AdmNodesTable } from './adm'
import { CoinNodesTable } from './coins'
import { useStore } from 'vuex'
import ServiceNodesTable from '@/components/nodes/services/ServiceNodesTable.vue'
RealGoodProgrammer marked this conversation as resolved.
Show resolved Hide resolved

const className = 'nodes-table'
const classes = {
Expand All @@ -80,10 +98,11 @@ const classes = {
checkbox: `${className}__checkbox`
}

type Tab = 'adm' | 'coins'
type Tab = 'adm' | 'coins' | 'services'

export default defineComponent({
components: {
ServiceNodesTable,
AdmNodesTable,
CoinNodesTable
},
Expand Down
7 changes: 6 additions & 1 deletion src/components/nodes/components/NodesTableHead.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<thead :class="classes.root">
<tr>
<th :class="classes.checkbox" v-if="!hideCheckbox" />
<th :class="classes.label" class="pl-0 pr-2" v-if="!hideLabel">{{ t('nodes.coin') }}</th>
<th :class="classes.label" class="pl-0 pr-2" v-if="!hideLabel">
{{ customLabelCode ? t(customLabelCode) : t('nodes.coin') }}
</th>
RealGoodProgrammer marked this conversation as resolved.
Show resolved Hide resolved
<th :class="classes.th" class="pl-0 pr-2" v-if="!hideHost">
{{ t('nodes.host') }}
</th>
Expand Down Expand Up @@ -35,6 +37,9 @@ export default {
},
hideSocket: {
type: Boolean
},
customLabelCode: {
type: String
}
},
setup() {
Expand Down
53 changes: 53 additions & 0 deletions src/components/nodes/services/ServiceNodesTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<NodesTableContainer>
<NodesTableHead hide-socket custom-label-code="nodes.service" />

<tbody>
<ServiceNodesTableItem
v-for="node in nodes"
:key="node.url"
:label="node.label"
:node="node"
/>
</tbody>
</NodesTableContainer>
</template>

<script lang="ts">
import { computed, defineComponent } from 'vue'
import { useStore } from 'vuex'
import NodesTableContainer from '@/components/nodes/components/NodesTableContainer.vue'
import NodesTableHead from '@/components/nodes/components/NodesTableHead.vue'
import ServiceNodesTableItem from './ServiceNodesTableItem.vue'
import { type NodeStatusResult } from '@/lib/nodes/abstract.node.ts'
import { sortNodesFn } from '@/components/nodes/utils/sortNodesFn.ts'

const className = 'nodes-table'
const classes = {
root: className
}

export default defineComponent({
components: {
NodesTableContainer,
NodesTableHead,
ServiceNodesTableItem
},
setup() {
const store = useStore()

const nodes = computed<NodeStatusResult[]>(() => {
const arr = store.getters['servicesModule/services']

return [...arr].sort(sortNodesFn)
})

return {
nodes,
classes
}
}
})
</script>

<style lang="scss" scoped></style>
92 changes: 92 additions & 0 deletions src/components/nodes/services/ServiceNodesTableItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<template>
<tr :class="classes.root">
<NodeColumn checkbox>
<NodeStatusCheckbox :value="active" @change="toggleActiveStatus" />
</NodeColumn>

<NodeColumn align="left">
<NodeLabel :label="label" />
</NodeColumn>

<NodeColumn>
<NodeUrl :node="node" />
</NodeColumn>

<NodeColumn ping :colspan="isUnsupported ? 2 : 1">
<NodeStatus :node="node" />
</NodeColumn>
</tr>
</template>

<script lang="ts">
import { computed, PropType } from 'vue'
import { useStore } from 'vuex'
import type { NodeStatusResult } from '@/lib/nodes/abstract.node.ts'
import type { TNodeLabel } from '@/lib/nodes/constants.ts'
PaulDremanovich marked this conversation as resolved.
Show resolved Hide resolved
import NodeUrl from '@/components/nodes/components/NodeUrl.vue'
import NodeColumn from '@/components/nodes/components/NodeColumn.vue'
import NodeLabel from '@/components/nodes/components/NodeLabel.vue'
import NodeStatus from '@/components/nodes/components/NodeStatus.vue'
import NodeStatusCheckbox from '@/components/nodes/components/NodeStatusCheckbox.vue'

const className = 'nodes-table-item'
const classes = {
root: className,
column: `${className}__column`,
columnCheckbox: `${className}__column--checkbox`,
checkbox: `${className}__checkbox`
}

export default {
components: {
NodeColumn,
NodeStatus,
NodeLabel,
NodeUrl,
NodeStatusCheckbox
},
props: {
node: {
type: Object as PropType<NodeStatusResult>,
required: true
},
label: {
type: String as PropType<TNodeLabel>,
required: true
}
},
setup(props) {
const store = useStore()

const url = computed(() => props.node.url)
const active = computed(() => props.node.active)
const isUnsupported = computed(() => props.node.status === 'unsupported_version')
const type = computed(() => props.node.label)

const toggleActiveStatus = () => {
store.dispatch('servicesModule/toggle', {
type: type.value,
url: url.value,
active: !active.value
})
store.dispatch('servicesModule/updateStatus')
}

return {
classes,
url,
active,
isUnsupported,
toggleActiveStatus
}
}
}
</script>

<style lang="scss">
.nodes-table-item {
}

.nodeName {
}
</style>
7 changes: 1 addition & 6 deletions src/lib/nodes/abstract.node.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { getHealthCheckInterval } from './utils/getHealthcheckConfig'
import { TNodeLabel } from './constants'
import { HealthcheckInterval, NodeKind, NodeStatus, NodeType } from './types'
import { HealthcheckInterval, HealthcheckResult, NodeKind, NodeStatus, NodeType } from './types'
import { nodesStorage } from './storage'

type HealthcheckResult = {
height: number
ping: number
}

type HttpProtocol = 'http:' | 'https:'
type WsProtocol = 'ws:' | 'wss:'

Expand Down
4 changes: 2 additions & 2 deletions src/lib/nodes/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type TNodeLabel =
| 'dash-node'
| 'lsk-node'
| 'lsk-indexer'
| 'rates-info'
| 'rate'

type KebabToCamelCase<S extends string> = S extends `${infer T}-${infer U}`
? `${T}${Capitalize<KebabToCamelCase<U>>}`
Expand Down Expand Up @@ -38,5 +38,5 @@ export const NODE_LABELS: NodeLabels = {
DashNode: 'dash-node',
LskNode: 'lsk-node',
LskIndexer: 'lsk-indexer',
RatesInfo: 'rates-info'
Rate: 'rate'
}
33 changes: 33 additions & 0 deletions src/lib/nodes/rate-info-service/RateInfoClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Client } from '@/lib/nodes/abstract.client.ts'
import { RateInfoResponse, RateInfoService } from '@/lib/nodes/rate-info-service/RateInfoService.ts'

export class RateInfoClient extends Client<RateInfoService> {
constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') {
super('adm')
this.nodes = endpoints.map((endpoint) => new RateInfoService(endpoint))
this.minNodeVersion = minNodeVersion

void this.watchNodeStatusChange()
}

async getAllRates(): Promise<RateInfoResponse> {
const node = await this.fetchNode()
PaulDremanovich marked this conversation as resolved.
Show resolved Hide resolved
return await node.getAllRates()
}

async getHistory(timestamp: number) {
const node = await this.fetchNode()
PaulDremanovich marked this conversation as resolved.
Show resolved Hide resolved
return await node.getHistory(timestamp)
}

async fetchNode() {
const node = this.useFastest ? this.getFastestNode() : this.getRandomNode()
if (!node) {
// All nodes seem to be offline: let's refresh the statuses
this.checkHealth()
// But there's nothing we can do right now
return Promise.reject(new Error('No online nodes at the moment'))
}
return node
}
}
52 changes: 52 additions & 0 deletions src/lib/nodes/rate-info-service/RateInfoService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Node } from '@/lib/nodes/abstract.node'
import axios, { AxiosInstance } from 'axios'
import { HealthcheckResult } from '@/lib/nodes/types.ts'
import { NODE_LABELS } from '@/lib/nodes/constants.ts'

export type RateInfoResponse = {
success: boolean
date: number
result: Record<string, number>
}

export type RateHistoryInfoResponse = {
success: boolean
date: number
result: {
_id: string
date: number
tickers: Record<string, number>
}[]
}
PaulDremanovich marked this conversation as resolved.
Show resolved Hide resolved

export class RateInfoService extends Node<AxiosInstance> {
constructor(url: string) {
super(url, 'adm', 'service', NODE_LABELS.Rate)
}
protected buildClient(): AxiosInstance {
return axios.create({
baseURL: this.url
})
}

async getAllRates(): Promise<RateInfoResponse> {
const response = await this.client.get<RateInfoResponse>('/get')
return response.data
}

async getHistory(timestamp: number) {
const response = await this.client.get<RateHistoryInfoResponse>(
`/getHistory?timestamp=${timestamp}`
PaulDremanovich marked this conversation as resolved.
Show resolved Hide resolved
)
return response.data
}

protected async checkHealth(): Promise<HealthcheckResult> {
const start = Date.now()
const response = await this.getAllRates()
return {
ping: Date.now() - start,
height: response.date
}
}
}
10 changes: 10 additions & 0 deletions src/lib/nodes/rate-info-service/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from '@/config'
import { NodeInfo } from '@/types/wallets'
import { RateInfoClient } from '@/lib/nodes/rate-info-service/RateInfoClient.ts'

const endpoints = (config.adm.services.list.infoService as NodeInfo[]).map(
(endpoint) => endpoint.url
)
export const rateInfoClient = new RateInfoClient(endpoints)

export default rateInfoClient
4 changes: 3 additions & 1 deletion src/lib/nodes/services.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { lskIndexer } from './lsk-indexer'
import { ethIndexer } from './eth-indexer'
import { rateInfoClient } from './rate-info-service'

export const services = {
lskIndexer,
ethIndexer
ethIndexer,
rate: rateInfoClient
}
5 changes: 5 additions & 0 deletions src/lib/nodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ export type NodeType = 'adm' | 'eth' | 'btc' | 'doge' | 'dash' | 'lsk'
export type NodeKind = 'node' | 'service'

export type HealthcheckInterval = 'normal' | 'crucial' | 'onScreen'

export type HealthcheckResult = {
height: number
ping: number
}
9 changes: 8 additions & 1 deletion src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,14 @@
"ms": "ms",
"nodeLabelDescription": "Support decentralization and enhance privacy level—run your own ADAMANT node and web application for messaging. Node can be run <a target=\"_blank\" href=\"https://medium.com/adamant-im/how-to-run-your-adamant-node-on-ubuntu-990e391e8fcc\">following instructions</a>. To add your nodes in the list for connection, you need <a target=\"_blank\" href=\"https://github.com/Adamant-im/adamant-im/\">to deploy web application on separate domain</a>. This limitation refers to Content Security Policy (CSP). In iOS and Android applications there are no such limitations.",
"offline": "Offline",
"ping": "Ping"
"ping": "Ping",
"service": "Service",
"socket": "Socket",
"tabs": {
"adm_nodes": "ADM Knooppunten",
"coin_nodes": "Munt Knooppunten",
"service_nodes": "Service Knooppunten"
}
},
"notifications": {
"message": {
Expand Down
Loading
Loading