-
-
- {{ pageTitle }}
-
-
-
-
+
+
+
+ {{ pageTitle }}
+
+
+
+
+
+
+
+ autoupdate
+
@@ -48,14 +57,26 @@ export default {
computed: {
...mapState({
blocks: state => state.backend.blocks,
- lastBlocks: state => state.backend.lastBlocks
+ lastBlocks: state => state.backend.lastBlocks,
+ autoUpdate: state => state.config.autoUpdateBlocks
}),
...mapGetters({
pending: 'pendingBlocks'
})
},
methods: {
- ...mapActions(['updateBlocks'])
+ ...mapActions([
+ 'updateBlocks',
+ 'setAutoUpdate'
+ ]),
+ handleAutoUpdate (event) {
+ const value = event.target.checked
+ if (value) this.updateBlocks()
+ this.setAutoUpdate(value)
+ }
+ },
+ created () {
+ this.updateBlocks()
}
}
diff --git a/src/config/entities/address.js b/src/config/entities/address.js
index d61a689c..0a951a5f 100644
--- a/src/config/entities/address.js
+++ b/src/config/entities/address.js
@@ -28,6 +28,9 @@ export const Addresses = () => {
field: 'blockNumber',
type: 'block',
title: 'Updated at block'
+ },
+ rns: {
+ hideIfEmpty: true
}
}
}
diff --git a/src/lib/js/rns.js b/src/lib/js/rns.js
new file mode 100644
index 00000000..283c6130
--- /dev/null
+++ b/src/lib/js/rns.js
@@ -0,0 +1,11 @@
+/* eslint-disable new-cap */
+import Resolver from '@rsksmart/rns-resolver.js'
+
+export const getAddr = async (domain) => {
+ let resolver
+
+ if (process.env.WS_URL.includes('testnet')) resolver = new Resolver.forRskTestnet()
+ else resolver = new Resolver.forRskMainnet()
+
+ return resolver.addr(domain)
+}
diff --git a/src/router/index.js b/src/router/index.js
index 965d92b3..898e8bb0 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -5,6 +5,7 @@ import routes from './routes'
import { normalizeSearch } from '../lib/js/utils'
import { isValidAddress } from '@rsksmart/rsk-utils/dist/addresses'
import { ROUTES as r } from '../config/types'
+import { getAddr } from '../lib/js/rns'
Vue.use(Router)
const router = new Router({
@@ -58,12 +59,26 @@ router.afterEach((to, from) => {
/**
* Navigation guard for all routes
*/
-function checkBeforeEnter (to, from, next) {
+async function checkBeforeEnter (to, from, next) {
const chainId = store.getters.chainId
const { params } = Object.assign({}, to)
const { address, hash } = params
if (hash) params.hash = normalizeSearch(hash)
if (isAddressPath(to, address)) {
+ // Allow `/address/`
+ if (address.match(/.rsk/)) {
+ const domain = address
+ try {
+ const resolved = await getAddr(domain)
+ if (resolved) {
+ store.commit('SET_DOMAIN', { domain, address: resolved })
+ next(`${r.address}/${resolved}`)
+ }
+ } catch (error) {
+ // console.error(error.message)
+ next()
+ }
+ }
// checksum error
let error
if (!isValidAddress(address, chainId)) {
diff --git a/src/store/modules/backend/actions.js b/src/store/modules/backend/actions.js
index 1f555db6..7efb4b43 100644
--- a/src/store/modules/backend/actions.js
+++ b/src/store/modules/backend/actions.js
@@ -30,10 +30,15 @@ export const socketNewTransactions = ({ state, commit, getters }, result) => {
}
}
// handle newBlocks from blocks channel
-export const socketNewBlocks = ({ state, commit, getters }, result) => {
+export const socketNewBlocks = ({ state, commit, getters, dispatch, rootState }, result) => {
const blocks = result.data || []
const autoUpdate = getters.autoUpdate
commit('LAST_BLOCKS', blocks)
+ if (rootState.autoUpdateBlocks) dispatch('updateBlocks')
+ if (rootState.route.path === '/' && autoUpdate) {
+ commit('LAST_BLOCKS_TIME', blocks[0].timestamp)
+ dispatch('fetchRouteData', { action: 'getTransactions', params: undefined, module: 'transactions', key: 'data' })
+ }
if (!state.lastBlocksTime) commit('LAST_BLOCKS_TIME')
if (!state.blocks.length || autoUpdate) {
commit('SET_BLOCKS', blocks.slice())
@@ -55,6 +60,12 @@ export const socketData = ({ state, commit, getters, dispatch }, res) => {
const isExport = getters.isExportKey(key)
if (isExport) return dispatch('exportPages', res)
+
+ if (res.action === 'getAddress') {
+ const domain = getters.getDomain(req.params.address)
+ res.data.rns = domain
+ }
+
const response = Object.assign({}, state.responses[key])
const updating = Object.assign(delayedObject(), response.delayed)
const isUpdating = Boolean(!updating.registry && updating.fields.length)
diff --git a/src/store/modules/backend/getters.js b/src/store/modules/backend/getters.js
index f172f325..5c61430c 100644
--- a/src/store/modules/backend/getters.js
+++ b/src/store/modules/backend/getters.js
@@ -10,6 +10,10 @@ export const transactions = state => {
return state.transactions
}
+export const lastTransactions = state => {
+ return state.responses?.data?.data
+}
+
export const pendingBlocks = state => {
return Object.keys(state.pendingBlocks).length
}
@@ -95,3 +99,9 @@ export const getExportMetadata = state => key => {
if (!key) return
return state.exports[key]
}
+
+export const getDomain = (state) => (address) => {
+ if (typeof address !== 'string') return // console.error('Address should be provided as string')
+
+ return state.rns[address.toLowerCase()]
+}
diff --git a/src/store/modules/backend/mutations.js b/src/store/modules/backend/mutations.js
index 17e59dad..3929b6d9 100644
--- a/src/store/modules/backend/mutations.js
+++ b/src/store/modules/backend/mutations.js
@@ -19,8 +19,7 @@ export const LAST_BLOCKS = (state, blocks) => {
}
export const LAST_BLOCKS_TIME = (state, time) => {
- if (undefined === time) time = Date.now()
- state.lastBlocksTime = time
+ state.lastBlocksTime = time ? new Date(time * 1000) : Date.now()
}
export const SET_BLOCKS = (state, blocks) => {
state.blocks = blocks
@@ -108,3 +107,10 @@ export const SET_EXPORT_METADATA = (state, [key, metadata]) => {
Vue.set(state.exports[key], prop, metadata[prop])
}
}
+
+export const SET_DOMAIN = (state, { domain, address }) => {
+ if (!domain || !address) return
+
+ address = address.toLowerCase()
+ state.rns[address] = domain
+}
diff --git a/src/store/modules/backend/state.js b/src/store/modules/backend/state.js
index 96026e29..1cbb3961 100644
--- a/src/store/modules/backend/state.js
+++ b/src/store/modules/backend/state.js
@@ -26,7 +26,8 @@ export default function () {
timestamp: 0
},
txPoolChart: [],
- stats: {}
+ stats: {},
+ rns: {}
}
}
diff --git a/src/store/modules/config/state.js b/src/store/modules/config/state.js
index a0302867..7fd4fbeb 100644
--- a/src/store/modules/config/state.js
+++ b/src/store/modules/config/state.js
@@ -2,7 +2,7 @@ import { EXPORT_FORMATS, EXPORT_ITEMS } from '../../../config/types'
export default function () {
return {
menuToggle: false,
- autoUpdateBlocks: false,
+ autoUpdateBlocks: true,
sort: {},
tables: {},
exportFormat: EXPORT_FORMATS.JSON,
diff --git a/src/store/modules/search/actions.js b/src/store/modules/search/actions.js
index 0e7d529c..50a9478e 100644
--- a/src/store/modules/search/actions.js
+++ b/src/store/modules/search/actions.js
@@ -1,6 +1,9 @@
import { testSearchedValue } from '../../../lib/js/validate'
import { isHexString, isTxOrBlockHash } from '@rsksmart/rsk-utils/dist/strings'
+import { getAddr } from '../../../lib/js/rns'
+import { store } from '../../../store/index'
+
const DEFAULT_TYPE = 'addressByName'
const createSearchKey = (value, type) => {
@@ -15,7 +18,16 @@ export const clearSearchedResults = async ({ commit, dispatch, getters }) => {
}
export const updateSearchedValue = async ({ commit, dispatch, state }, value) => {
- value = String(value).replace(/[\W_]+/g, '')
+ if (value.match(/.rsk/)) {
+ try {
+ const address = await getAddr(value)
+ store.commit('SET_DOMAIN', { domain: value, address })
+
+ value = address
+ } catch (error) {
+ // console.error(error.message, value)
+ }
+ }
const lcValue = value.toLowerCase()
value = (isHexString(value) && isTxOrBlockHash(lcValue)) ? lcValue : value
if (state.value !== value) {
diff --git a/src/store/modules/search/getters.js b/src/store/modules/search/getters.js
index 4e363668..414bc36a 100644
--- a/src/store/modules/search/getters.js
+++ b/src/store/modules/search/getters.js
@@ -8,14 +8,20 @@ export const getSearchPayloadByType = state => type => {
export const searchKeysRequested = state => Object.keys(state.requested)
-export const getSearchLink = (state, getters) => ({ type, value }) => {
+export const getSearchLink = (state, getters, commit) => ({ type, value }) => {
const payload = getters.getSearchPayloadByType(type)
const path = r[payload.type]
if (!path || !value) return
const link = `/${path}/${value}`
+ state.searchLink = link
+ state.type = type
return link
}
+export const linkToSearch = (state) => {
+ return state.searchLink
+}
+
export const getSearchedResults = (state, getters) => {
const { requested } = state
const results = getters.searchKeysRequested.map(key => {
@@ -50,6 +56,10 @@ export const searchedTypes = state => {
return Object.keys(types).filter(k => types[k])
}
+export const searchedType = state => {
+ return state.type
+}
+
export const isSearchPage = (state, getters) => {
const re = new RegExp(`^/${r.search}`)
return re.test(getters.getRouterPath)
diff --git a/src/store/modules/search/mutations.js b/src/store/modules/search/mutations.js
index 6e7ce59a..f74044fa 100644
--- a/src/store/modules/search/mutations.js
+++ b/src/store/modules/search/mutations.js
@@ -19,3 +19,7 @@ export const SET_SEARCH_TYPES = (state, types) => {
export const SET_SEARCH_EXPAND = (state, value) => {
Vue.set(state, 'expanded', value)
}
+
+export const SET_SEARCH_LINK = (state, value) => {
+ Vue.set(state, 'searchLink', value)
+}
diff --git a/src/store/modules/search/state.js b/src/store/modules/search/state.js
index 8a47b28f..c815f9d6 100644
--- a/src/store/modules/search/state.js
+++ b/src/store/modules/search/state.js
@@ -6,6 +6,8 @@ export default function () {
payloads,
value: undefined,
types: {},
- requested: {}
+ requested: {},
+ searchLink: null,
+ type: null
}
}
diff --git a/src/styles/_controls.scss b/src/styles/_controls.scss
index a5326da7..d69fb693 100644
--- a/src/styles/_controls.scss
+++ b/src/styles/_controls.scss
@@ -171,56 +171,60 @@ li {
.item {
margin: 0 24px;
}
-
- label {
- display: flex;
- justify-items: center;
- input {
- margin-right: 10px;
- }
- }
- .checkbox-container {
- display: flex;
- align-items: center;
- }
-
- .custom-checkbox {
- opacity: 0;
- position: absolute;
- }
-
- .checkmark {
- height: 20px;
- width: 20px;
- cursor: pointer;
- background-color: transparent;
- border-radius: 5px;
- margin-right: 10px;
- display: inline-block;
- position: relative;
- border: 1px solid #575757;
- }
-
- .custom-checkbox:checked + .checkmark {
- background-color: orange;
- }
-
- .custom-checkbox:checked + .checkmark:after {
- content: "";
- position: absolute;
- display: block;
- left: 5px;
- top: 3px;
- width: 4px;
- height: 8px;
- border: solid black;
- border-width: 0 3px 3px 0;
- transform: rotate(45deg);
- }
}
+}
+.checkbox-container {
+ display: flex;
+ align-items: center;
+ justify-items: center;
+ input {
+ margin-right: 10px;
+ }
+}
+.custom-checkbox {
+ opacity: 0;
+ position: absolute;
}
+.checkmark {
+ height: 20px;
+ width: 20px;
+ cursor: pointer;
+ background-color: transparent;
+ border-radius: 5px;
+ margin-right: 10px;
+ display: inline-block;
+ position: relative;
+ border: 1px solid #575757;
+}
+
+.custom-checkbox:checked + .checkmark {
+ background-color: orange;
+}
+
+.custom-checkbox:checked + .checkmark:after {
+ content: "";
+ position: absolute;
+ display: block;
+ left: 5px;
+ top: 3px;
+ width: 4px;
+ height: 8px;
+ border: solid black;
+ border-width: 0 3px 3px 0;
+ transform: rotate(45deg);
+}
+.auto-update {
+ .checkmark {
+ height: 18px;
+ width: 18px;
+ }
+ .custom-checkbox:checked + .checkmark:after {
+ width: 3px;
+ height: 6px;
+ }
+}
.waiting-result {
display: flex;
flex-direction: column;
diff --git a/src/styles/_home.scss b/src/styles/_home.scss
index 4a6aeaaf..22b10ef6 100644
--- a/src/styles/_home.scss
+++ b/src/styles/_home.scss
@@ -257,9 +257,20 @@
.box-head {
display: flex;
align-items: center;
- padding: 0 24px;
+ justify-content: space-between;
+ padding-left: 24px;
margin-bottom: 10px;
}
+ .box-title {
+ display: flex;
+ align-items: center;
+ }
+ .auto-update {
+ color: $white_400;
+ display: flex;
+ gap: 5px;
+ width: max-content;
+ }
.title {
&::first-letter {
text-transform: uppercase;
diff --git a/src/styles/_navigation.scss b/src/styles/_navigation.scss
index 231b74b1..6a3e3f9e 100644
--- a/src/styles/_navigation.scss
+++ b/src/styles/_navigation.scss
@@ -82,15 +82,34 @@
}
}
}
+.search {
+ position: relative;
+ .search-background {
+ z-index: 9;
+ width: 100%;
+ height: 100vh;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ position: fixed;
+ background-color: rgba(0, 0, 0, .6);
+ }
+}
.search-content {
+ z-index: 10;
position: relative;
- width: 420px;
+ width: 470px;
height: 40px;
border-radius: 90px;
padding: 0 16px;
display: flex;
align-items: center;
border: 1px solid $newbw_800;
+ background-color: $newbw_900;
+ &.search-content-focus {
+ border: 1px solid $white_400 !important;
+ }
img {
width: 16px;
height: 16px;
@@ -104,6 +123,7 @@
border: none;
color: white;
font-size: 14px;
+ background-color: $newbw_900;
&:focus {
outline: none !important;
background: unset;
@@ -131,15 +151,15 @@
left: 0;
right: 0;
border-radius: 20px;
- width: 420px;
+ width: 470px;
padding: 16px;
min-height: 80px;
height: max-content;
max-height: 400px;
- overflow-y: scroll;
+ overflow-y: auto;
overflow-x: hidden;
&::-webkit-scrollbar {
- width: 8px;
+ width: 5px;
}
&::-webkit-scrollbar-track {
@@ -152,6 +172,11 @@
border-radius: 20px;
border: 1px solid rgb(49, 49, 49);
}
+ .title-address {
+ color: $white_100;
+ font-weight: 700;
+ margin-bottom: 10px;
+ }
a {
color: #c0c0c0;
@@ -161,6 +186,15 @@
color: white;
text-decoration: underline;
}
+ &.search-address {
+ background-color: $newbw_700;
+ font-weight: 500;
+ color: white;
+ }
+ }
+ &.no-results {
+ display: flex;
+ align-items: center;
}
}
}
@@ -479,7 +513,7 @@
}
.search-results {
margin-top: 5px;
- box-shadow: 3px 5px 10px #272727;
+ width: 350px;
}
.btn-close {
cursor: pointer;
diff --git a/src/styles/_page.scss b/src/styles/_page.scss
index 320d0922..d8156294 100644
--- a/src/styles/_page.scss
+++ b/src/styles/_page.scss
@@ -161,7 +161,10 @@
}
}
}
-
+.Account.Balance .data-item {
+ padding: 12px 24px 28px;
+ border-radius: 12px;
+}
.data-item {
background-color: $newbw_800;
.items {