Browse Source

optimization, cleanup, hashrouter + vue router play nicely

develop
dskvr 2 years ago
parent
commit
2d00866af8
  1. 172
      src/components/layout/SidebarComponent.vue
  2. 57
      src/components/relays/FindRelaysSubnav.vue
  3. 65
      src/components/relays/ListClearnet.vue
  4. 227
      src/components/relays/MapInteractive.vue
  5. 3
      src/components/relays/RefreshComponent.vue
  6. 14
      src/components/relays/RelayStatistics.vue
  7. 49
      src/components/relays/SubnavComponent.vue
  8. 140
      src/components/relays/pages/RelaysHome.vue
  9. 17
      src/shared/dynamic-router.js
  10. 68
      src/shared/hash-router.js
  11. 44
      src/shared/relays-lib.js
  12. 2
      src/store/layout.js
  13. 3
      src/store/prefs.js

172
src/components/layout/SidebarComponent.vue

@ -1,172 +0,0 @@
<template>
<TransitionRoot :show="sidebarOpened">
<Dialog as="div" @close="sidebarOpened = false" class="md:hidden">
<TransitionChild
enter="transition ease-in-out duration-200 transform"
enter-from="-translate-x-full"
enter-to="translate-x-0"
leave="transition ease-in-out duration-200 transform"
leave-from="translate-x-0"
leave-to="-translate-x-full"
as="template">
<div class="flex relative z-10 flex-col w-72 h-full bg-gray-50 border-r border-gray-200 md:hidden">
<button
@click="sidebarOpened = false"
class="hover:ring-2 hover:ring-gray-300 flex absolute top-2 right-2 justify-center items-center w-10 h-10 rounded-full focus:outline-none focus:ring-2 focus:ring-gray-600"
type="button" value="Close sidebar">
<XIcon class="w-5 h-5"/>
</button>
<div class="px-6 pt-8 pb-4">
<a href="/">
<ApplicationLogo class="w-48 h-9"/>
</a>
</div>
<div class="overflow-y-auto flex-1">
<div class="mb-10">
<h3 class="mx-6 mb-2 text-xs tracking-widest text-gray-400 uppercase">
Find Relays
</h3>
<a v-for="(item, index) in store.layout.getSidebarGroup('relays')"
:href="item.href" :key="index" class="flex items-center px-6 py-2.5 text-gray-500 hover:text-orange-600 group">
<component
:is="item.icon"
class="mr-2 w-5 h-5 text-gray-400 group-hover:text-orange-500"/>
{{ item.label }}
</a>
</div>
<div class="mb-10">
<h3 class="mx-6 mb-2 text-xs tracking-widest text-gray-400 uppercase">
Relay Tools
</h3>
<a v-for="(item, index) in store.layout.getSidebarGroup('tools')"
:href="item.href" :key="index" class="flex items-center px-6 py-2.5 text-gray-500 hover:text-orange-600 group">
<component
:is="item.icon"
class="mr-2 w-5 h-5 text-gray-400 group-hover:text-orange-500"/>
{{ item.label }}
</a>
</div>
</div>
</div>
</TransitionChild>
<TransitionChild
enter="transition-opacity ease-linear duration-200"
enter-from="opacity-0"
enter-to="opacity-100"
leave="transition-opacity ease-linear duration-200"
leave-from="opacity-100"
leave-to="opacity-0"
as="template">
<DialogOverlay class="fixed inset-0 bg-gray-600 bg-opacity-50"></DialogOverlay>
</TransitionChild>
</Dialog>
</TransitionRoot>
<div class="hidden w-64 bg-gray-50 border-slate-200 md:block items-start drop-shadow-lg">
<h3 class="py-3 uppercase text-xs text-gray-100 bg-gray-800 bg-opacity-80 text-left pl-3">Browse Relays</h3>
<nav class="space-y-1" aria-label="Sidebar">
<a v-for="item in store.layout.getSidebarGroup('relays')"
:key="item.name"
href="#"
@click="setActive('relays', item.slug)"
:class="[isActive(item) ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900', 'group flex items-center px-3 py-2 text-sm font-medium rounded-md']"
:aria-current="isActive(item) ? 'page' : undefined">
<span class="truncate">{{ item.name }}</span>
<span v-if="item.count" :class="[isActive(item) ? 'bg-white' : 'bg-gray-100 text-gray-600 group-hover:bg-gray-200', 'ml-auto inline-block py-0.5 px-3 text-xs rounded-full']">{{ item.count }}</span>
</a>
</nav>
<h3 class="py-3 uppercase text-xs text-gray-100 bg-gray-800 bg-opacity-80 text-left pl-3">Tools</h3>
<nav class="space-y-1" aria-label="Sidebar">
<a v-for="item in store.layout.getSidebarGroup('tools')"
:key="item.name"
href="#"
@click="setActive('tools', item.slug)"
:class="[isActive(item) ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900', 'group flex items-center px-3 py-2 text-sm font-medium rounded-md']"
:aria-current="isActive(item) ? 'page' : undefined">
<span class="truncate">{{ item.name }}</span>
<span v-if="item.count" :class="[isActive(item) ? 'bg-white' : 'bg-gray-100 text-gray-600 group-hover:bg-gray-200', 'ml-auto inline-block py-0.5 px-3 text-xs rounded-full']">{{ item.count }}</span>
</a>
</nav>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import { items } from '@/data/nav-sidebar.yaml'
import { setupStore } from '@/store'
export default defineComponent({
title: "nostr.watch registry & network status",
name: 'NavComponent',
components: {
// PreferencesComponent,
// AuthComponent
},
props: {},
data(){
return {
active: null,
groups: new Set(items.map( item => item.slug )),
sidebar: []
}
},
setup(){
return {
store : setupStore()
}
},
updated(){
},
mounted(){
this.active = this.store.layout.getActive('relays')
this.store.layout.setSidebarItems(items)
this.sidebar = this.store.layout.getSidebar
this.sidebar['relays'].map(item => {
item.count = this.store.relays.getCount(item.slug)
console.log('mapping', item.slug, this.store.relays.getCount(item.slug))
return item
})
// this.store.relays.$subscribe( (mutation) => {
// // console.log('relays mutation', mutation)
// if(this.groups.has(mutation.events.key)) {
// this.sidebar = this.sidebar.map( item => {
// if(item.slug == mutation.events.key) {
// item.count = mutation.events.newValue
// console.log('ok', item.count)
// }
// return item
// })
// }
// })
},
methods: {
setActive(section, slug){
this.active = slug
this.store.layout.setActive(section, slug)
},
findActive(){
const active = this.store.layout.getActive('relays')
return Object.entries(this.sidebar).filter( item => item[1].slug == active)
},
},
computed: {
isActive(){
return (item) => item.slug==this.active
}
},
// watch: {
// active(n){
// console.log('ok', n)
// }
// }
});
</script>

57
src/components/relays/FindRelaysSubnav.vue

@ -7,7 +7,7 @@
<a v-for="item in store.layout.getNavGroup(this.navSlug)"
:key="`subnav-${item.slug}`"
:href="item.href"
@click="setActive(this.navSlug, item.slug)"
@click="setActiveContent(item.slug)"
:class="[isActive(item) ? 'bg-slate-500 text-white' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900', 'group flex items-center px-3 py-2 text-sm font-medium']"
class="inline-flex items-center pt-1.5 text-sm font-medium">
{{ item.name }}
@ -41,7 +41,7 @@
<DisclosureButton
v-for="item in store.layout.getNavGroup(this.navSlug)"
:key="`subnav-${item.slug}`"
@click="setActive(this.navSlug, item.slug)"
@click="setActiveContent(item.slug)"
:class="[isActive(item) ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900', 'group flex items-center px-3 py-2 text-sm font-medium']"
as="a"
:href="item.href"
@ -55,15 +55,16 @@
<script>
import { defineComponent } from 'vue'
import { items } from './config/find-pagenav.yaml'
import { setupStore } from '@/store'
import { items } from './config/find-pagenav.yaml'
import RelaysLib from '@/shared/relays-lib.js'
import { setupNavData, mountNav, setActiveContent, loadContent, routeValid, parseHash, contentIsActive } from '@/shared/hash-router.js'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
import { MagnifyingGlassIcon } from '@heroicons/vue/20/solid'
import { Bars3Icon, XMarkIcon } from '@heroicons/vue/24/outline'
import RelaysLib from '@/shared/relays-lib.js'
export default defineComponent({
title: "nostr.watch registry & network status",
name: 'NavComponent',
@ -76,12 +77,7 @@ export default defineComponent({
},
props: {},
data(){
return {
active: null,
// groups: new Set(items.map( item => item.slug )),
sidebar: [],
navSlug: 'relays-find-pagenav'
}
return setupNavData('relays/find')
},
setup(){
return {
@ -90,42 +86,17 @@ export default defineComponent({
},
updated(){
},
created(){
this.mountNav('subsection', items)
},
mounted(){
this.active = this.store.layout.getActive(this.navSlug)
this.store.layout.setNavItems(this.navSlug, items)
this.sidebar = this.store.layout.getNavGroup(this.navSlug)
this.loadPageContent('subsection')
// this.sidebar['relays'].map(item => {
// item.count = this.store.relays.getCount(item.slug)
// // console.log('mapping', item.slug, this.store.relays.getCount(item.slug))
// return item
// })
// this.store.relays.$subscribe( (mutation) => {
// // console.log('relays mutation', mutation)
// if(this.groups.has(mutation.events.key)) {
// this.sidebar = this.sidebar.map( item => {
// if(item.slug == mutation.events.key) {
// item.count = mutation.events.newValue
// console.log('ok', item.count)
// }
// return item
// })
// }
// })
},
methods: Object.assign(RelaysLib, {
setActive(section, slug){
this.active = slug
this.store.layout.setActive(section, slug)
return true
},
}),
methods: Object.assign(RelaysLib, { mountNav, setActiveContent, loadContent, routeValid, parseHash, contentIsActive }),
computed: {
isActive(){
return (item) => item.slug==this.active
return (item) => item.slug==this.navActiveContent
}
},
// watch: {

65
src/components/relays/ListClearnet.vue

@ -1,8 +1,8 @@
<template>
<div class="pt-5 px-1 sm:px-6 lg:px-8">
<div class="-mt-10 pt-0 px-1 sm:px-6 lg:px-8">
<div class="mt-8 flex flex-col">
<div class="overflow-x-auto">
<div class="inline-block min-w-full align-middle" v-if="subsectionRelays.length">
<div class="inline-block min-w-full align-middle" v-if="subsectionRelays().length">
<div class="relative overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full table-auto divide-y divide-gray-300">
<thead>
@ -12,7 +12,10 @@
</th>
<th scope="col" class="relay text-left">
<NostrSyncPopoverNag v-if="activePageItem == 'favorite'" />
<NostrSyncPopoverNag v-if="subsection == 'favorite'" />
<span v-if="subsection != 'favorite' && store.relays.getFavorites.length">
<input type="checkbox" id="relays-pin-favorites" v-model="store.prefs.pinFavorites" /> <label class="font-thin text-sm" for="relays-pin-favorites">Pin Favorites</label>
</span>
</th>
<th scope="col" class="verified">
<span class="verified-shape-wrapper">
@ -43,7 +46,7 @@
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<tr v-for="(relay, index) in subsectionRelays" :key="relay" class="bg-gray-50" :class="getResultClass(relay, index)">
<tr v-for="(relay, index) in subsectionRelays()" :key="relay" class="bg-gray-50" :class="getResultClass(relay, index)">
<SingleClearnet
:relay="relay"
v-bind:selectedRelays="selectedRelays"
@ -53,7 +56,7 @@
</table>
</div>
</div>
<div class="inline-block min-w-full align-middle" v-if="!relays.length && activePageItem == 'favorite'">
<div class="inline-block min-w-full align-middle" v-if="!relays.length && subsection == 'favorite'">
You have not selected any favorites. To select a favorite, click the heart emoji next to any relay in a relays list.
</div>
</div>
@ -79,34 +82,14 @@
even: index % 2,
}
},
queryJson(){
const result = { relays: this.relays }
return JSON.stringify(result,null,'\t')
},
relaysTotal () {
return this.relays.length //TODO: Figure out WHY?
},
relaysConnected () {
return Object.entries(this.result).length
},
relaysComplete () {
return this.relays.filter(relay => this.results?.[relay]?.state == 'complete').length
},
sha1 (message) {
const hash = crypto.createHash('sha1').update(JSON.stringify(message)).digest('hex')
return hash
},
isDone(){
return this.relaysTotal()-this.relaysComplete() <= 0
},
loadingComplete(){
return this.isDone() ? 'loaded' : ''
},
subsectionRelays(){
const relays = this.sortRelays( this.store.relays.getRelays(this.subsection, this.results ) )
this.relaysCount[this.subsection] = relays.length
return relays
}
}
export default defineComponent({
@ -116,25 +99,26 @@
NostrSyncPopoverNag
},
setup(props){
const {activePageItemProp: activePageItem} = toRefs(props)
const {subsectionProp: subsection} = toRefs(props)
const {relaysCountProp: relaysCount} = toRefs(props)
const {resultsProp: results} = toRefs(props)
return {
store : setupStore(),
results: results,
activePageItem: activePageItem,
subsection: subsection,
relaysCount: relaysCount
}
},
mounted(){
this.activePageData = this.navData.filter( item => item.slug == this.activePageItem )[0]
this.activePageData = this.navData.filter( item => item.slug == this.subsection )[0]
},
updated(){
console.log('state, updated')
// console.log('state, updated')
},
props: {
activePageItemProp: {
subsectionProp: {
type: String,
default(){
return ""
@ -158,20 +142,17 @@
selectedRelays: [],
relays: [],
timeout: null,
navData: this.store.layout.getNavGroup('relays-find-pagenav'),
navData: this.store.layout.getNavGroup('relays/find'),
activePageData: {}
}
},
computed: {
subsectionRelays(){
return this.sortRelays( this.store.relays.getRelays(this.activePageItem, this.results ) )
}
},
methods: Object.assign(RelaysLib, localMethods),
// watch: {
// activePageItem: function(){
// // this.activePageData = this.navData.filter( item => item.slug == this.activePageItem )[0]
// subsection: function(){
// // this.activePageData = this.navData.filter( item => item.slug == this.subsection )[0]
// // this.relaysUpdate()
// }
// }

227
src/components/relays/MapInteractive.vue

@ -0,0 +1,227 @@
<template>
<div :class="mapToggleClass()" style="margin-bottom:-30px">
<l-map
ref="map"
v-model:zoom="zoom"
:center="[34.41322, -1.219482]"
:minZoom="zoom"
:maxZoom="zoom"
:zoomControl="false"
:dragging="false"
:touchZoom="false"
:scrollWheelZoom="false"
:doubleClickZoom="false"
>
<l-tile-layer
url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
layer-type="base"
name="OpenStreetMap"
attribution='<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
/>
<!-- <l-marker v-for="([relay, result]) in Object.entries(geo)" :lat-lng="getLatLng(result)" :key="relay">
<l-popup>
{{ relay }}
</l-popup>
</l-marker> -->
<l-circle-marker
v-for="relay in getRelaysWithGeo"
:lat-lng="getLatLng(relay)"
:key="relay"
:radius="2"
:weight="4"
:color="getCircleColor(relay)"
:fillOpacity="1" >
<!-- <l-popup>
{{ relay }}
</l-popup> -->
</l-circle-marker>
</l-map>
<button @click="toggleMap">
<span class="expand">expand</span>
<span class="collapse">collapse</span>
map
</button>
</div>
</template>
<script>
import { defineComponent, toRefs } from 'vue'
import "leaflet/dist/leaflet.css"
import { LMap, LTileLayer, LCircleMarker } from "@vue-leaflet/vue-leaflet"
import { setupStore } from '@/store'
import RelaysLib from '@/shared/relays-lib.js'
export default defineComponent({
name: "MapInteractive",
components: {
LMap,
LTileLayer,
LCircleMarker,
},
data() {
return {
zoom: 2,
center: [40.41322, -1.219482],
expanded: false,
relays: []
};
},
setup(props){
const {activePageItemProp: activePageItem} = toRefs(props)
const {resultsProp: results} = toRefs(props)
return {
store : setupStore(),
results: results,
activePageItem: activePageItem
}
},
mounted() {
console.log('results', this.results)
this.geo = this.store.relays.geo
},
updated(){},
props: {
resultsProp: {
type: Array,
default(){
return []
}
},
activePageItemProp: {
type: String,
default(){
return ""
}
},
},
computed: {
getCircleClass(relay){
console.log('the relay', relay)
return (relay) => {
return {
visible: this.isRelayInActiveSubsection(relay),
hidden: !this.isRelayInActiveSubsection(relay),
[relay]: true
}
}
},
getRelaysWithGeo(){
return this.store.relays.getAll.filter( relay => this.geo?.[relay] instanceof Object )
},
},
methods: Object.assign(RelaysLib, {
mapHeight(){
return this.expanded ? "500px" : "250px"
},
getLatLng(relay){
// console.log('geo', relay, [this.geo.lat, this.geo.lon])
return [this.geo[relay].lat, this.geo[relay].lon]
},
getCircleColor(relay){
if(!this.isRelayInActiveSubsection(relay))
return 'transparent'
if(this.results[relay]?.aggregate == 'public')
return '#00AA00'
if(this.results[relay]?.aggregate == 'restricted')
return '#FFA500'
if(this.results[relay]?.aggregate == 'offline')
return '#FF0000'
},
isRelayInActiveSubsection(relay){
// console.log(this.store.relays.getRelays(this.activePageItem).length, this.activePageItem, relay, this.store.relays.getRelays(this.activePageItem).includes(relay))
return this.store.relays.getRelays(this.activePageItem, this.results).includes(relay)
},
toggleMap(){
this.expanded = !this.expanded
setTimeout(() => { this.resetMapSize() }, 1)
},
mapToggleClass(){
return {
expanded: this.expanded
}
},
resetMapSize(){
if (this.$refs.map.ready)
this.$refs.map.leafletObject.invalidateSize()
}
}),
watch: {
// activePageItem: function(){
// this.relays = this.subsectionRelays()
// }
}
});
</script>
<style>
/* :root {
--map-tiles-filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);
}
@media (prefers-color-scheme: dark) {
.leaflet-tile {
filter:var(--map-tiles-filter, none);
}
} */
</style>
<style scoped>
.leaflet-container {
position:relative;
z-index:900;
margin:0;
padding:0;
height:250px !important;
width:100%;
/* -webkit-transition:height 300ms ease-in-out;
-moz-transition:height 300ms ease-in-out;
-o-transition:height 300ms ease-in-out;
transition:height 300ms ease-in-out;*/
}
.expanded .leaflet-container {
height:555px !important;
}
.leaflet-control-zoom {
display: none !important;
}
button {
position: relative;
z-index:901;
top: -30px;
background:rgba(255,255,255,0.5);
border:0;
padding:3px 6px;
color:#777;
cursor:pointer;
}
button:hover {
color:#222;
}
.expanded button .expand,
button .collapse {
display:none;
}
button .expand,
.expanded button .collapse {
display:inline;
}
</style>

3
src/components/relays/RefreshComponent.vue

@ -166,6 +166,9 @@ export default defineComponent({
created(){
document.addEventListener('visibilitychange', this.handleVisibility, false)
},
unmounted(){
clearInterval(this.interval)
},
mounted(){
if(!this.windowActive)
return

14
src/components/relays/RelayStatistics.vue

@ -26,7 +26,7 @@ import { setupStore } from '@/store'
export default defineComponent({
name: 'RelayControlComponent',
components: {RefreshComponent},
components: {},
setup(props){
const {resultsProp: results} = toRefs(props)
return {
@ -35,13 +35,14 @@ export default defineComponent({
}
},
mounted(){
}
},
data: function(){
return {
relays: this.store.relays.getAll,
geo: this.store.relays.getGeo
}
}
},
props: {
resultsProp: {
type: Object,
@ -55,7 +56,7 @@ export default defineComponent({
const nips = new Object()
Object.entries(this.results).forEach( (result) => {
if(result?.info?.supported_nips)
supported_nips.forEach( nip => {
result?.info?.supported_nips.forEach( nip => {
if( !(nips[nip] instanceof Array ))
nips[nip] = new Set
nips[nip].add(result.uri)
@ -65,7 +66,7 @@ export default defineComponent({
return nips
},
collateSoftware(){
const software = new Object()
// const software = new Object()
},
collateSoftwareVersion(){
@ -87,8 +88,7 @@ export default defineComponent({
return byCont;
},
async getRelayHistory(){
const uniqueRelays = new Set(),
relay
// const uniqueRelays = new Set()
//subscribe kind 3
this.$pool.subscribe()

49
src/components/relays/SubnavComponent.vue

@ -7,7 +7,7 @@
<a v-for="item in store.layout.getNavGroup(this.navSlug)"
:key="`subnav-${item.slug}`"
:href="item.href"
@click="setActive(this.navSlug, item.slug)"
@click="setActiveContent(item.slug)"
:class="[isActive(item) ? 'bg-gray-100 text-gray-900' : ' hover:bg-gray-50 hover:text-gray-900', 'group flex items-center px-3 text-sm font-medium']"
class="inline-flex items-center mx-1 text-sm font-medium text-white">
{{ item.name }}
@ -28,6 +28,7 @@ import { items } from './config/subnav.yaml'
import { setupStore } from '@/store'
import RelaysLib from '@/shared/relays-lib.js'
import { setupNavData, mountNav, setActiveContent, loadContent, routeValid, parseHash, contentIsActive } from '@/shared/hash-router.js'
import RefreshComponent from '@/components/relays/RefreshComponent.vue'
export default defineComponent({
@ -47,12 +48,7 @@ export default defineComponent({
},
},
data(){
return {
active: null,
// groups: new Set(items.map( item => item.slug )),
sidebar: [],
navSlug: 'relays-subnav'
}
return setupNavData('relays')
},
setup(props){
const {resultsProp: results} = toRefs(props)
@ -64,43 +60,18 @@ export default defineComponent({
updated(){
},
beforeMount(){
this.mountNav('section', items)
},
mounted(){
this.store.layout.setNavItems(this.navSlug, items)
this.active = this.store.layout.getActive(this.navSlug)
console.log('active item', this.active)
console.log('state from subnav', this.active, this.store.layout.getActive(this.navSlug))
this.loadPageContent('section')
// this.sidebar = this.store.layout.getNavGroup(this.navSlug)
// this.sidebar['relays'].map(item => {
// item.count = this.store.relays.getCount(item.slug)
// console.log('mapping', item.slug, this.store.relays.getCount(item.slug))
// return item
// })
// this.store.relays.$subscribe( (mutation) => {
// // console.log('relays mutation', mutation)
// if(this.groups.has(mutation.events.key)) {
// this.sidebar = this.sidebar.map( item => {
// if(item.slug == mutation.events.key) {
// item.count = mutation.events.newValue
// console.log('ok', item.count)
// }
// return item
// })
// }
// })
},
methods: Object.assign(RelaysLib, {
setActive(section, slug){
this.active = slug
this.store.layout.setActive(section, slug)
},
}),
methods: Object.assign(RelaysLib, { mountNav, setActiveContent, loadContent, routeValid, parseHash, contentIsActive }),
computed: {
isActive(){
return (item) => item.slug==this.active
return (item) => item.slug==this.navActiveContent
}
},
// watch: {

140
src/components/relays/pages/RelaysHome.vue

@ -8,23 +8,23 @@
<MapSummary
:resultsProp="results"
:activePageItemProp="activePageItem"
v-if="activeNavItem == 'find'" />
:activePageItemProp="activeSubsection"
v-if="activeSection == 'find'" />
<div id="wrapper" class="mx-auto max-w-7xl">
<div v-if="activeNavItem == 'find'">
<div v-if="activeSection == 'find'">
<div class="pt-5 px-1 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<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">
{{ this.relaysCount[activePageItem] }}
{{ relaysCount[activeSubsection] }}
</span>
{{ activePageItem }} Relays
{{ activeSubsection }} Relays
</h1>
<p class="mt-2 text-xl text-gray-700">
<!-- {{ store.layout.getActiveItem('relays-find-pagenav') }} -->
{{ store.layout.getActiveItem('relays-find-pagenav').description }}
<!-- {{ store.layout.getActiveItem('relays/find') }} -->
{{ store.layout.getActiveItem('relays/find')?.description }}
</p>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
@ -44,27 +44,27 @@
</div>
<div
v-for="section in navSubsection"
:key="section.slug">
<!-- <div v-if="section.slug == activePageItem"> -->
<div :class="section.slug == activePageItem ? 'visible' : 'hidden'">
v-for="subsection in navSubsection"
:key="subsection.slug">
<!-- <div v-if="section.slug == activeSubsection"> -->
<div :class="subsection.slug == activeSubsection ? 'visible' : 'hidden'">
<ListClearnet
:resultsProp="results"
:activePageItemProp="section.slug"
:subsectionProp="subsection.slug"
v-bind:relaysCountProp="relaysCount"
/>
</div>
</div>
</div>
<RelayStatistics
:resultsProp="results"
v-if="activeNavItem == 'statistics'" />
v-if="activeSection == 'statistics'" />
<RelayStatistics
<MapInteractive
:resultsProp="results"
v-if="activeNavItem == 'map'" />
v-if="activeSection == 'map'" />
<div id="footer">
<span class="credit">
@ -80,15 +80,58 @@ import { useHead } from '@vueuse/head'
import { setupStore } from '@/store'
//shared methods
import RelaysLib from '@/shared/relays-lib.js'
import { parseHash } from '@/shared/hash-router.js'
//components
import MapSummary from '@/components/relays/MapSummary.vue'
import ListClearnet from '@/components/relays/ListClearnet.vue'
import SubnavComponent from '@/components/relays/SubnavComponent.vue'
import FindRelaysSubnav from '@/components/relays/FindRelaysSubnav.vue'
import ListClearnet from '@/components/relays/ListClearnet.vue'
import RelayStatistics from '@/components/relays/RelayStatistics.vue'
import MapInteractive from '@/components/relays/MapInteractive.vue'
//data
import { relays } from '../../../../relays.yaml'
import { geo } from '../../../../cache/geo.yaml'
const localMethods = {
relaysLoadData(){
this.store.relays.setRelays(relays)
this.store.relays.setGeo(geo)
this.relays = this.store.relays.getAll
this.lastUpdate = this.store.relays.lastUpdate
this.preferences = this.store.prefs.get
this.relays.forEach(relay => {
this.results[relay] = this.getCache(relay)
})
},
relaysMountNav(){
this.store.layout.$subscribe( (mutation) => {
if(mutation.events.key === 'relays'){
this.activeSection = mutation.events.newValue
}
if(mutation.events.key === 'relays/find'){
this.activeSubsection = mutation.events.newValue
}
})
this.activeSection = this.routeSection || this.store.layout.getActiveItem('relays')?.slug
this.activeSubsection = this.routeSubsection || this.store.layout.getActiveItem(`relays/${this.activeSection}`)?.slug
this.navSubsection.forEach( item => this.relaysCount[item.slug] = 0 )
}
// hashRouter: function(){
// const route = this.parseRouterHash()
// if(route.section)
// this.activeSection = route.section
// if(route.subsection)
// this.activeSubsection = route.subsection
// if(route.relay)
// this.activeSubsection = route.relay
// },
}
export default defineComponent({
name: 'HomePage',
@ -96,18 +139,18 @@ export default defineComponent({
MapSummary,
ListClearnet,
SubnavComponent,
FindRelaysSubnav
FindRelaysSubnav,
RelayStatistics,
MapInteractive
},
setup(){
useHead({
title: 'nostr.watch',
meta: [
{
meta: [{
name: `description`,
content: 'A robust client-side nostr relay monitor. Find fast nostr relays, view them on a map and monitor the network status of nostr.',
},
],
}]
})
return {
store : setupStore()
@ -121,58 +164,29 @@ export default defineComponent({
filteredRelays: [],
timeouts: {},
intervals: {},
activeNavItem: this.store.layout.getActive('relays-subnav') || 'relays',
activePageItem: this.store.layout.getActive('relays-find-pagenav') || 'find',
navSubsection: this.store.layout.getNavGroup('relays-find-pagenav'),
relaysCount: {}
relaysCount: {},
activeSection: this.routeSection || this.store.layout.getActiveItem('relays')?.slug,
activeSubsection: this.routeSubsection || this.store.layout.getActiveItem(`relays/${this.activeSection}`)?.slug,
}
},
updated(){},
async mounted() {
console.log('active page item', this.activePageItem)
this.store.relays.setRelays(relays)
this.store.relays.setGeo(geo)
this.relays = this.store.relays.getAll
this.lastUpdate = this.store.relays.lastUpdate
this.preferences = this.store.prefs.get
this.relays.forEach(relay => {
this.results[relay] = this.getCache(relay)
})
this.store.layout.$subscribe( (mutation) => {
console.log('layout', 'mutation detected')
if(mutation.events.key == 'relays-find-pagenav')
this.activePageItem = mutation.events.newValue
if(mutation.events.key == 'relays-subnav')
this.activeNavItem = mutation.events.newValue
})
this.store.relays.$subscribe( () => {
console.log('relays', 'mutation detected')
})
this.store.user.$subscribe( () => {
console.log('users', 'mutation detected')
})
this.store.relays.$subscribe( () => {
console.log('prefs', 'mutation detected')
})
beforeMount(){
this.routeSection = this.parseHash().section || false
this.routeSubsection = this.parseHash().subsection || false
},
this.navSubsection.forEach( item => this.relaysCount[item.slug] = 0 )
// this.psuedoRouter(this.store.layout.getNavGroup('relays-subnav'))
this.psuedoRouter()
async mounted() {
this.relaysLoadData()
this.relaysMountNav()
},
computed: {
navSubsection: function() { return this.store.layout.getNavGroup(`relays/${this.activeSection}`) || [] }
},
methods: RelaysLib,
methods: Object.assign(RelaysLib, localMethods, { parseHash }),
})
</script>

17
src/shared/dynamic-router.js

@ -1,17 +0,0 @@
import RelaysHome from '@/components/relays/pages/RelaysHome.vue'
// import ByStatus from '../pages/ByStatus.vue'
import RelaysSingle from '@/components/relays/pages/RelaysSingle.vue'
export const setRouterPaths = function(page, navItems){
let component
if(page == 'relays')
component = RelaysHome
navItems.forEach( item => {
this.$route.push({
name: item.name,
component: component
})
})
}

68
src/shared/hash-router.js

@ -0,0 +1,68 @@
const setupNavData = function(navSlug){
return {
navActiveContent: "",
navActiveData: {},
navItems: [],
navSlug: navSlug,
navType: ""
}
}
const mountNav = function(navType, navItems){
this.navType = navType
this.navItems = navItems
this.store.layout.setNavItems(this.navSlug, this.navItems)
this.navActiveContent = this.store.layout.getActive(this.navSlug) || this.navItems[0].slug
console.log('route', 'setting active content', this.navType, this.navActiveContent)
this.setActiveContent(this.navActiveContent)
this.loadContent()
console.log('route', 'mount', this.navType, this.navSlug, this.navActiveContent, this.navItems, this.navSlug)
}
const setActiveContent = function(slug){
this.navActiveContent = slug
this.store.layout.setActive(this.navSlug, slug)
console.log('set active content', this.navActiveContent)
// console.log('route', 'setActiveContent', this.navType, this.navSlug, this.navActiveContent, this.navItems, this.navSlug)
}
const loadContent = function(){
console.log('route', 'loadContent', this.navType, this.navSlug, this.navActiveContent, this.navItems, this.navSlug)
const route = this.parseHash()
if(!this.routeValid(route[this.navType]))
return
console.log(`route from ${this.navType} in ${this.navSlug}`, route[this.navType])
if(route[this.navType])
this.setActiveContent(route[this.navType])
else
this.navActiveContent = this.store.layout.getActive(this.navSlug) || this.navItems[0].slug
}
const routeValid = function(slug){
console.log('route', 'routeValid', this.navType, this.navSlug, this.navActiveContent, this.navItems, this.navSlug)
if( !(this.navItems instanceof Array) )
return false
return this.navItems.map(item => item.slug).includes(slug)
}
const parseHash = function(){
const hashParams = this.$route.hash.replace('#', '').replace(/^\/+/g, '').split('/')
const result = {}
result.page = hashParams[0]
result.section = hashParams[1]
result.subsection = hashParams[2]
return result
}
const contentIsActive = function(slug){
return slug == this.navActiveContent
}
export { setupNavData, mountNav, setActiveContent, loadContent, routeValid, parseHash, contentIsActive }

44
src/shared/relays-lib.js

@ -16,47 +16,15 @@ export default {
y = this.results?.[relay2]?.check?.connect
return (x === y)? 0 : x? -1 : 1;
})
sort(relays, (relay1, relay2) => {
let x = this.store.relays.isFavorite(relay1),
y = this.store.relays.isFavorite(relay2)
return (x === y)? 0 : x? -1 : 1;
})
if(this.store.prefs.doPinFavorites)
sort(relays, (relay1, relay2) => {
let x = this.store.relays.isFavorite(relay1),
y = this.store.relays.isFavorite(relay2)
return (x === y)? 0 : x? -1 : 1;
})
// relays = this.sortRelaysFavoritesOnTop(relays)
return relays
},
psuedoRouter: function(){
console.log(this)
const route = this.parseRouterHash()
console.log('pseudo route', route)
if(route.section)
this.activeNavItem = route.section
if(route.subsection)
this.activePageItem = route.subsection
},
parseRouterHash: function(){
const hashParams = this.$route.hash.replace('#', '').replace(/^\/+/g, '').split('/')
return {
page: hashParams[0],
section: hashParams[1],
subsection: hashParams[2]
}
},
writeRouterHashPath: function(page, section, subsection){
console.log('hash updated?', this.$route)
this.$route.hash = `#/${page}/${section}/${subsection}`
console.log('hash updated?', this.$route)
},
loadPageContent(which){
const route = this.parseRouterHash()
console.log(`route from ${which}`, route)
if(route[which])
this.setActive(this.navSlug, route[which])
else
this.active = this.store.layout.getActive(this.navSlug)
},
isExpired: function(){
return !this.store.relays.lastUpdate

2
src/store/layout.js

@ -9,7 +9,7 @@ export const useLayoutStore = defineStore('layout', {
}),
getters: {
getActive: (state) => (section) => state.active[section],
getActiveItem: (state) => (section) => state.nav[section].filter( item => item.slug == state.active[section])[0],
getActiveItem: (state) => (section) => state.nav[section]?.filter( item => item.slug == state.active[section])[0],
getNav: (state) => state.nav,
getNavGroup: (state) => (group) => state.nav[group]
},

3
src/store/prefs.js

@ -4,15 +4,18 @@ export const usePrefsStore = defineStore('prefs', {
state: () => ({
refresh: true,
duration: 30*60*1000,
pinFavorites: true
}),
getters: {
doRefresh: (state) => state.refresh,
expireAfter: (state) => state.duration,
doPinFavorites: (state) => state.pinFavorites,
},
actions: {
enable(){ this.refresh = true },
disable(){ this.refresh = false },
toggleRefresh(){ this.refresh = !this.refresh },
updateExpiration(dur) { this.duration = dur },
togglePinFavorites(){ this.pinFavorites = !this.pinFavorites },
},
})
Loading…
Cancel
Save