dskvr
2 years ago
13 changed files with 439 additions and 422 deletions
@ -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> |
@ -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> |
|||
|
@ -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 |
|||
}) |
|||
}) |
|||
} |
@ -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 } |
Loading…
Reference in new issue