Browse Source

worked the single relays page a bit, various bugfixes

develop
dskvr 2 years ago
parent
commit
3cfb45bade
  1. 64
      src/components/AuthComponent.vue
  2. 24
      src/components/layout/HeaderComponent.vue
  3. 1
      src/components/relays/RefreshComponent.vue
  4. 296
      src/components/relays/RelayStatistics.vue
  5. 12
      src/components/relays/RelaysFind.vue
  6. 199
      src/components/relays/pages/RelaysSingle.vue
  7. 2
      src/components/relays/partials/NostrSyncPopoverNag.vue
  8. 6
      src/main.js
  9. 13
      src/store/stats.js
  10. 5
      src/store/user.js

64
src/components/AuthComponent.vue

@ -34,9 +34,14 @@ export default defineComponent({
this.showAuth()
if(this.isLoggedIn())
this.getData()
// this.store.user.$subscribe( mutation => {
// if(mutation.key != 'pubKey')
// return
// console.log('there was amutation!!!!')
// this.getData()
// })
},
updated(){
this.showAuth()
},
computed: {},
methods: Object.assign(UserLib, {
@ -49,36 +54,41 @@ export default defineComponent({
resolve()
}, 1001)
})
console.log('signer enabled', this.signer)
},
auth: async function(){
this.store.user.setPublicKey(await window.nostr.getPublicKey())
this.getData()
const pubkey = await window.nostr.getPublicKey()
this.store.user.setPublicKey(pubkey)
await this.getData()
},
getData: function(){
const subid = crypto.randomBytes(40).toString('hex')
const filterProfile = { limit: 1, kinds:[0], authors: [this.store.user.getPublicKey ] }
const filterEvent = { limit: 1, kinds:[1], authors: [this.store.user.getPublicKey ] }
let foundProfile = false,
foundEvent = false
this.$pool
.on('open', (relay) => {
relay.subscribe(`${subid}_profile`, filterProfile)
relay.subscribe(`${subid}_event`, filterEvent)
})
.on('event', (relay, sub_id, event) => {
if(`${subid}_profile` == sub_id && !foundProfile) {
this.store.user.setProfile(event.content)
this.$pool.unsubscribe(subid)
foundProfile = true
}
if(`${subid}_event` == sub_id && !foundEvent) {
this.store.user.setTestEvent(event)
console.log('user event', this.store.user.getTestEvent)
this.$pool.unsubscribe(subid)
foundEvent = true
}
})
return new Promise( resolve => {
const subid = crypto.randomBytes(40).toString('hex')
const filterProfile = { limit: 1, kinds:[0], authors: [this.store.user.getPublicKey ] }
const filterEvent = { limit: 1, kinds:[1], authors: [this.store.user.getPublicKey ] }
let foundProfile = false,
foundEvent = false
this.$pool
.subscribe(`${subid}_profile`, filterProfile)
this.$pool
.subscribe(`${subid}_event`, filterEvent)
this.$pool
.on('event', (relay, sub_id, event) => {
if(`${subid}_profile` == sub_id && !foundProfile) {
this.store.user.setProfile(event.content)
this.$pool.unsubscribe(subid)
foundProfile = true
if(foundProfile && foundEvent)
resolve()
}
if(`${subid}_event` == sub_id && !foundEvent) {
this.store.user.setTestEvent(event)
this.$pool.unsubscribe(subid)
foundEvent = true
if(foundProfile && foundEvent)
resolve()
}
})
})
},
generateCodeChallenge: async function(codeVerifier) {

24
src/components/layout/HeaderComponent.vue

@ -41,24 +41,25 @@
<Menu as="div" class="relative ml-3">
<!-- <Menu as="div" class="relative ml-3"> -->
<AuthComponent />
<div v-if="isLoggedIn()">
<div v-if="store.user.getPublicKey">
<MenuButton class="flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span class="sr-only">Open user menu</span>
<span class="text-white mt-1.5 mr-3">{{ store.user.profile.nip05 || store.user.profile.name }}</span>
<img v-if="store.user.isProfile" class="h-8 w-8 rounded-full" :src="store.user.profile?.picture" alt="store.user.profile?.name" />
<span class="text-white mt-1.5 mr-3">{{ store.user.getNip05 || store.user.getName }}</span>
<span class="text-white mt-1.5 mr-3">{{ }}</span>
<img v-if="store.user.getPicture" class="h-8 w-8 rounded-full" :src="store.user.getPicture" alt="store.user.profile?.name" />
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<MenuItems
style="z-index:9000 !important;"
class="absolute right-0 z-9000 mt-2 w-64 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<span v-if="store.user.profile.name" class="block text-ellipsis text-sm w-full font-extrabold">
{{ store.user.getName }}
<span v-if="store.user.getName" class="block text-ellipsis text-sm w-full font-extrabold mt-2">
{{ store.user.getName }}
</span>
<span v-if="store.user.getNip05" class="block text-ellipsis text-sm w-full">
{{ store.user.getNip05 }}
<span v-if="store.user.getNip05" class="block text-ellipsis text-sm w-full mt-2">
{{ store.user.getNip05 }}
</span>
<span class="block text-ellipsis text-xs">
<span class="block text-ellipsis text-xs mt-3 mb-2">
<code>{{ store.user.getPublicKey.slice(0, 16) }}...</code>
</span>
<MenuItem v-slot="{ active }">
@ -77,7 +78,6 @@
</div>
</DisclosurePanel>
</Disclosure>
</template>
<style scoped>
nav.menu {
@ -143,7 +143,11 @@ export default defineComponent({
}
},
mounted(){
console.log('user store ???', this.store)
this.store.user.$subscribe((mutation) => {
mutation
})
},
computed: {
},
methods: UserLib
});

1
src/components/relays/RefreshComponent.vue

@ -128,6 +128,7 @@ const localMethods = {
.on('complete', (instance) => {
instance.result.aggregate = this.getAggregate(instance.result)
instance.relay.close()
instance.result.log = instance.log
resolve(instance.result)
})
.on('close', (relay) => {

296
src/components/relays/RelayStatistics.vue

@ -1,20 +1,80 @@
<template>
<div>
<pre>
{{ }}
</pre>
<!-- NIP-15
<pre>
{{this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == 'public').filter( relay => this.results?.[relay]?.info?.supported_nips.includes(15)).length }}
</pre>
<pre>
{{ this.store.relays.getAll.length }}
</pre> -->
<pre>
{{ this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == 'public').length }}
</pre>
<pre>
{{ this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == 'restricted').length }}
</pre>
<pre>
{{ this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == 'offline').length }}
</pre>
<pre>
{{ store.stats.get('history') }}
</pre>
<pre>
{{ store.stats.get('countries') }}
</pre>
<pre>
{{ store.stats.get('nips') }}
</pre>
<pre>
{{ store.stats.get('continents') }}
</pre>
<pre>
{{ }}
</pre>
<!-- <pre>
{{ this.collateSupportedNips }}
</pre>
<pre>
{{ this.collateContinents }}
</pre>
<pre>
{{ collateCountries }}
</pre> -->
history
growth chart
Basic:
total
<!-- total
online
restricted
restricted -->
tor
offline
<!-- offline -->
Software:
software/versiono
nips:
OK support by nips
<!-- OK support by nips -->
geo
by country
continent
<!-- by country -->
<!-- continent -->
aggregate stats
oldest relay still online
newest relay
@ -23,6 +83,8 @@
<script>
import { defineComponent, toRefs } from 'vue'
import { setupStore } from '@/store'
import { RelayPool } from 'nostr'
import crypto from 'crypto'
export default defineComponent({
name: 'RelayStatistics',
@ -34,13 +96,23 @@ export default defineComponent({
results: results
}
},
mounted(){
beforeMount(){
},
async mounted(){
this.store.stats.set('nips', this.collateSupportedNips)
this.store.stats.set('continents', this.collateContinents)
this.store.stats.set('countries', this.collateCountries)
this.store.stats.setHistory(await this.historicalData())
},
data: function(){
return {
relays: this.store.relays.getAll,
geo: this.store.relays.getGeo
geo: this.store.relays.geo,
bySupportedNips: null,
byCountry: null,
byContinent: null,
history: null
}
},
props: {
@ -51,33 +123,29 @@ export default defineComponent({
}
},
},
methods: {
computed: {
collateSupportedNips(){
const nips = new Object()
Object.entries(this.results).forEach( (result) => {
result = result[1]
if(result?.info?.supported_nips)
result?.info?.supported_nips.forEach( nip => {
if( !(nips[nip] instanceof Array ))
nips[nip] = new Set
if( !(nips[nip] instanceof Set ))
nips[nip] = new Set()
nips[nip].add(result.uri)
})
})
console.log('supported nips', nips)
return nips
},
collateSoftware(){
// const software = new Object()
},
collateSoftwareVersion(){
},
collateContinents(){
const byCont = new Object()
this.relays.forEach( relay => {
if( !(this.geo[relay] instanceof Object) ) {
if( !(this.geo[relay] instanceof Object) || typeof this.geo[relay].continentName === 'undefined' ) {
if( !(byCont.unknown instanceof Set) )
byCont.unknown = new Set()
byCont.unknown.add(relay)
return
}
const cont = this.geo[relay].continentName
if( !(byCont[cont] instanceof Set) )
@ -87,14 +155,190 @@ export default defineComponent({
console.log('continents', byCont)
return byCont;
},
async getRelayHistory(){
// const uniqueRelays = new Set()
collateCountries(){
const byCountry = new Object()
this.relays.forEach( relay => {
if( !(this.geo[relay] instanceof Object) || typeof this.geo[relay].country === 'undefined' ) {
if( !(byCountry.unknown instanceof Set) )
byCountry.unknown = new Set()
byCountry.unknown.add(relay)
return
}
const cont = this.geo[relay].country
if( !(byCountry[cont] instanceof Set) )
byCountry[cont] = new Set()
byCountry[cont].add(relay)
})
console.log('countries', byCountry)
return byCountry;
},
},
methods: {
collateSoftware(){
// const software = new Object()
},
collateSoftwareVersion(){
//subscribe kind 3
this.$pool.subscribe()
//array of objects
//sort the array of objects by timestamps
}
},
async historicalData(){
let relays = [],
relaysKnown = [],
relaysRemote = {},
// remove = [],
uniques = null,
relayTimeCodes = {}
const run = async function(){
//discover relays [kind:3], "remoteRelays"
await discover().catch( err => console.warn(err) )
//Sanitize knownRelays to prevent dupes in uniques
sanitizeKnownRelays()
//sanitize remoteRelays
sanitizeRemoteRelays()
//check remoteRelays
// await checkRemoteRelays()
//Remove offline remoteRelays
// removeOfflineRelays()
//Combine knownRelays and remoteRelays
concatRelays()
//set uniques
uniques = new Set(relays)
console.log(uniques, uniques.size)
const final = []
uniques.forEach( relay => {
if( !(relayTimeCodes[relay] instanceof Array) )
return
relayTimeCodes[relay].sort( (a, b) => a - b )
final.push( [relay, relayTimeCodes[relay][0] ] )
})
console.log('before sort', final[0])
final.sort( (a, b) => a[1]-b[1] )
console.log('afdter sort', final[0])
return final
}
const concatRelays = function(){
relays = relaysKnown.concat(relaysRemote)
}
const discover = async () => {
relaysKnown = this.store.relays.getAll
return new Promise(resolve => {
let total = 0
const subid = crypto.randomBytes(40).toString('hex')
const pool = RelayPool(this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == 'public').filter( relay => this.results?.[relay]?.info?.supported_nips.includes(15)))
pool
.on('open', relay => {
// console.log('open')
relay.subscribe(subid, {since: 1609829, limit: 10000, kinds:[3]})
})
.on('eose', (relay) => {
console.log('closing', relay.url)
relay.close()
resolve(true)
})
.on('event', (relay, _subid, event) => {
if(subid == _subid) {
console.log(total++)
try {
// console.log(event)
const parsed = JSON.parse(event.content)
relaysRemote = Object.assign(relaysRemote, parsed)
Object.keys(parsed).forEach( key => {
if( !(relayTimeCodes[key] instanceof Array) )
relayTimeCodes[key] = new Array()
relayTimeCodes[key].push(event.created_at)
})
relay.close()
} catch(e) {
console.error(e)
}
}
})
setTimeout( () => {
pool.close()
resolve(true)
}, 10*1000 )
})
}
const sanitizeRemoteRelays = function(){
const remote1 = Object.entries(relaysRemote)
.filter( relay => Array.isArray(relay) )
.map( relay => sanitizeRelay(relay[0]) )
.filter( relay => relay.startsWith('wss://') )
.filter( relay => !relay.includes('localhost') )
const remote2 = Object.entries(relaysRemote)
.filter( relay => relay instanceof String )
.map( relay => sanitizeRelay(relay) )
.filter( relay => relay.startsWith('wss://') )
.filter( relay => !relay.includes('localhost') )
relaysRemote = remote1.concat(remote2)
}
const sanitizeKnownRelays = function(){
relaysKnown = relaysKnown.map( relay => sanitizeRelay(relay) ) //Known relays may have trailing slash
}
const sanitizeRelay = function(relay) {
return relay
.toLowerCase()
.trim()
.replace(/\s\t/g, '')
.replace(/\/+$/, '')
.replace(/^[^a-z\d]*|[^a-z\d]*$/gi, '');
}
// const checkRemoteRelays = async function(){
// for(let i=0;i<relaysRemote.length;i++) {
// // console.log('check for connect', remoteMerged[i])
// await checkRelay(relaysRemote[i])
// .catch( () => {
// remove.push(relaysRemote[i])
// console.log('removals:', remove.length, relaysRemote[i])
// })
// }
// }
// const checkRelay = async function(relay){
// return new Promise( (resolve, reject) => {
// let socket = new Relay(relay)
// socket
// .on('open', relay => {
// relay
// socket.close()
// resolve()
// })
// .on('error', reject )
// setTimeout( reject, 500 )
// })
// }
// const removeOfflineRelays = function(){
// relaysRemote = relaysRemote.filter( relay => !remove.includes(relay) )
// }
return await run()
},
}
})
</script>

12
src/components/relays/RelaysFind.vue

@ -4,7 +4,7 @@
<div class="sm:flex-auto text-left">
<h1 class="text-4xl capitalize font-semibold text-gray-900">
<span class="inline-flex rounded bg-green-800 text-sm px-2 py-1 text-white relative -top-2">
{{ relaysCount[activeSubsection] }}
{{ getRelaysCount(activeSubsection) }}
</span>
{{ activeSubsection }} Relays
</h1>
@ -105,6 +105,16 @@
activeSection: function(){ return this.store.layout.getActiveItem('relays')?.slug },
activeSubsection: function(){ return this.store.layout.getActiveItem(`relays/${this.activeSection}`)?.slug },
navSubsection: function() { return this.store.layout.getNavGroup(`relays/${this.activeSection}`) || [] },
getRelaysCount: function() {
return (subsection) => {
if(subsection === 'all')
return this.store.relays.getAll.length
if(subsection === 'favorite')
return this.store.relays.getFavorites.length
return this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == subsection).length
}
},
parseHash
},

199
src/components/relays/pages/RelaysSingle.vue

@ -10,89 +10,133 @@
v-if="(geo instanceof Object)"
/>
<div id="wrapper" class="mt-8 flex-container m-auto">
<div id="wrapper" class="mt-8 mx-auto max-w-7xl grid grid-cols-3 gap-8">
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h1>{{geo?.countryCode ? getFlag : ''}}<span @click="copy(relayFromUrl)">{{ relayFromUrl }}</span></h1>
<p class="mt-1 max-w-2xl text-sm text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
<div class="overflow-hidden bg-white shadow sm:rounded-lg col-span-3">
<div class="px-4 py-5 sm:px-6">
<h1>{{geo?.countryCode ? getFlag : ''}}<span @click="copy(relayFromUrl)">{{ relayFromUrl }}</span></h1>
<p class="mt-1 max-w-2xl text-sm text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.name">
<dt class="text-sm font-medium text-gray-500">Relay Name</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ result.info.name }}</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.pubkey">
<dt class="text-sm font-medium text-gray-500">Public Key</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0"><code class="text-xs">{{ result.info.pubkey }}</code></dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.email">
<dt class="text-sm font-medium text-gray-500">Contact</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0 "><SafeMail :email="result.info.email" /></dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.software">
<dt class="text-sm font-medium text-gray-500">Software</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ result.info.software }}</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-sm font-medium text-gray-500">Software Version</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0"><code class="text-xs">{{ result.info.version }}</code></dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-sm font-medium text-gray-500">Connection Status</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span><img :src="badgeCheck('connect')" class="inline mr-3" /></span>
<span><img :src="badgeCheck('read')" class="inline mr-3"/></span>
<span><img :src="badgeCheck('write')" class="inline" /></span>
</dd>
<div class="py-5 col-span-2">
<div class="overflow-hidden bg-white shadow sm:rounded-lg col-span-2 relative">
<div class="px-4 py-5 sm:px-6">
<h3>Relay Info <code class="text-gray-300 text-xs absolute top-3 right-3">NIP-11</code></h3>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result.info?.supported_nips">
<dt class="text-sm font-medium text-gray-500">Supported Nips</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span v-for="(nip) in result.info.supported_nips" :key="`${relay}_${nip}`" class="inline-block mr-3 mt-1">
<a :href="nipLink(nip)" target="_blank" ><img :src="badgeLink(nip)" /></a>
</span>
</dd>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-sm font-medium text-gray-500">Connection Status</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span><img :src="badgeCheck('connect')" class="inline mr-3" /></span>
<span><img :src="badgeCheck('read')" class="inline mr-3"/></span>
<span><img :src="badgeCheck('write')" class="inline" /></span>
</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result.info?.supported_nips">
<dt class="text-sm font-medium text-gray-500">Supported Nips</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span v-for="(nip) in result.info.supported_nips" :key="`${relay}_${nip}`" class="inline-block mr-3 mt-1">
<a :href="nipLink(nip)" target="_blank" ><img :src="badgeLink(nip)" /></a>
</span>
</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.name">
<dt class="text-sm font-medium text-gray-500">Relay Name</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ result.info.name }}</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.pubkey">
<dt class="text-sm font-medium text-gray-500">Public Key</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0"><code class="text-xs">{{ result.info.pubkey }}</code></dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.email">
<dt class="text-sm font-medium text-gray-500">Contact</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0 "><SafeMail :email="result.info.email" /></dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.software">
<dt class="text-sm font-medium text-gray-500">Software</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ result.info.software }}</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-sm font-medium text-gray-500">Software Version</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0"><code class="text-xs">{{ result.info.version }}</code></dd>
</div>
</dl>
</div>
</dl>
</div>
</div>
</div>
<div class="overflow-hidden bg-white shadow sm:rounded-lg mt-8" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3>DNS</h3>
<p class="mt-1 max-w-2xl text-sm text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
<div class="col-span-1">
<div class="overflow-hidden bg-white shadow sm:rounded-lg mt-8" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3>DNS</h3>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-for="(value, key) in Object.entries(geo.dns)" :key="`${value}_${key}`">
<dt class="text-sm font-medium text-gray-500">{{ value[0] }}</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-for="(value, key) in Object.entries(geo.dns)" :key="`${value}_${key}`">
<dt class="text-sm font-medium text-gray-500">{{ value[0] }}</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
<div class="overflow-hidden bg-white shadow sm:rounded-lg mt-8 col-span-1" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3>Geo Data {{geo?.countryCode ? getFlag : ''}}</h3>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-for="(value, key) in Object.entries(geo).filter(value => value[0] != 'dns')" :key="`${value}_${key}`">
<dt class="text-sm font-medium text-gray-500">{{ value[0] }}</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
</div>
</div>
</div>
<div class="overflow-hidden bg-white shadow sm:rounded-lg mt-8" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3>Geo Data {{geo?.countryCode ? getFlag : ''}}</h3>
<p class="mt-1 max-w-2xl text-sm text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl class="sm:divide-y sm:divide-gray-200">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-for="(value, key) in Object.entries(geo).filter(value => value[0] != 'dns')" :key="`${value}_${key}`">
<dt class="text-sm font-medium text-gray-500">{{ value[0] }}</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
<!-- component -->
<div class="col-span-1">
<div class="overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="py-2 inline-block min-w-full sm:px-6 lg:px-8">
<div class="overflow-hidden">
<table class="min-w-full text-center">
<thead class="border-b">
<tr>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4">
Log
</th>
</tr>
</thead>
<tbody>
<tr class="border-b" v-for="(log, index) in result.log" :key="`${log[0]}-${index}`">
<td class="text-sm text-gray-900 font-medium px-6 py-4 overflow-ellipsis" :class="getLogClass(log[0])">
{{ log[0] }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="max-w-sm p-6 w-auto bg-white border border-gray-200 rounded-lg shadow-md dark:bg-gray-800 dark:border-gray-700" v-if="!result?.check?.connect">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">This Relay Appears to be offline</h5>
</div>
</div>
</template>
@ -113,11 +157,7 @@ import emoji from 'node-emoji';
// import { relays } from '../../relays.yaml'
// import { geo } from '../../cache/geo.yaml'
import { setupStore } from '@/store'
import { useHead } from '@vueuse/head'
@ -211,6 +251,17 @@ export default defineComponent({
getFlag () {
return this.geo?.countryCode ? countryCodeEmoji(this.geo?.countryCode) : emoji.get('shrug');
},
getLogClass(){
return (slug) => {
return {
['bg-indigo-100 border-indigo-200']: slug == 'eose',
['bg-red-100 border-red-200']: slug == 'wserror',
['bg-yellow-100 border-yellow-200']: slug == 'error',
['bg-green-100 border-green-200']: slug == 'ok',
['bg-gray-50 border-gray-200']: slug == 'event',
}
}
}
}),
// updated() {
@ -233,9 +284,9 @@ tr td:last-child { text-align:left }
body, .grid-Column { padding:0; margin:0; }
.badges { display:block; margin: 10px 0 11px}
.badges > span {margin-right:5px} */
#wrapper {max-width:800px}
/* #wrapper {max-width:800px} */
#relay-wrapper { margin: 50px 0 20px; padding: 20px 0}
/* #relay-wrapper { margin: 50px 0 20px; padding: 20px 0} */
h1 {cursor:pointer;font-size:40pt; margin: 0px 0 15px; padding:0 0 10px; border-bottom:3px solid #e9e9e9}
</style>

2
src/components/relays/partials/NostrSyncPopoverNag.vue

@ -18,7 +18,7 @@
</div>
<div class="text-white p-3">
This feature depends on the finalization of
<a href="https://github.com/nostr-protocol/nips/pull/32" target="_blank">NIP-32 [link]</a>,
<a href="https://github.com/nostr-protocol/nips/pull/32" target="_blank">NIP-23 [link]</a>,
if you actively develop a nostr client, please provide input on the NIP to help make the
implementation of user relay lists easier for everyone.
</div>

6
src/main.js

@ -8,7 +8,7 @@ import directives from "./directives/"
import { plugin as storePlugin } from './store'
import { RelayPool } from 'nostr'
import VueInputAutowidth from 'vue-input-autowidth'
// import { relays } from '../relays.yaml'
import { relays } from '../relays.yaml'
const app = createApp(App)
.use(router)
@ -19,8 +19,8 @@ const app = createApp(App)
directives(app);
// app.config.globalProperties.$pool = new RelayPool([relays])
app.config.globalProperties.$pool = new RelayPool(['wss://relay.nostr.ch'])
app.config.globalProperties.$pool = new RelayPool(relays, {reconnect: false})
// app.config.globalProperties.$pool = new RelayPool(['wss://relay.nostr.ch'])
await router.isReady()

13
src/store/stats.js

@ -11,7 +11,20 @@ export const useStatStore = defineStore('stats', {
newestRelayOnline: null,
}),
getters: {
getHistory: (state) => state.history,
getBySoftware: (state) => state.software,
getByNip: (state) => state.nips,
getByCountry: (state) => state.countries,
getByContinent: (state) => state.continents,
get: (state) => (which) => state[which]
},
actions: {
setHistory(payload){
this.history = payload
},
set(type, payload){
Object.keys(payload).forEach( key => this[type][key] = Array.from(payload[key] ) )
},
},
})

5
src/store/user.js

@ -10,8 +10,9 @@ export const useUserStore = defineStore('user', {
getters: {
getPublicKey: (state) => state.pubKey,
getProfile: (state) => state.profile,
getName: (state) => state.profile.name ? state.profile.name : false,
getNip05: (state) => state.profile.nip05 ? state.profile.nip05 : false,
getName: (state) => state.profile.name,
getPicture: (state) => state.profile.picture,
getNip05: (state) => state.profile.nip05,
isProfile: (state) => Object.keys(state.profile).length ? true : false,
getTestEvent: (state) => state.testEvent
},

Loading…
Cancel
Save