Browse Source

Release 0.1.1

develop
dskvr 2 years ago
parent
commit
bc5c1f0eaf
  1. 1
      README.md
  2. 4
      package.json
  3. 135
      src/components/layout/HeaderComponent.vue
  4. 28
      src/components/partials/DarkMode.vue
  5. 12
      src/components/relays/blocks/MapSummary.vue
  6. 23
      src/components/relays/blocks/RelaysResultTable.vue
  7. 14
      src/components/relays/nav/RelaysFindNav.vue
  8. 15
      src/components/relays/nav/RelaysNav.vue
  9. 19
      src/components/relays/pages/RelaysFind.vue
  10. 2
      src/components/relays/pages/RelaysHome.vue
  11. 115
      src/components/relays/pages/RelaysSingle.vue
  12. 130
      src/components/relays/tasks/RefreshTask.vue
  13. 7
      src/components/relays/tasks/TasksManager.vue
  14. 4
      src/router/index.js
  15. 10
      src/shared/computed.js
  16. 39
      src/shared/layout.js
  17. 2
      src/store/prefs.js
  18. 124
      src/styles/main.scss
  19. 20
      tailwind.config.js
  20. 162
      yarn.lock

1
README.md

@ -26,7 +26,6 @@ Develop branch is deployed to https://next.nostr.watch
- [ ] Discover relays at runtime (currently buildtime, ready to move to runtime with 0.1)
- [ ] Discover geo at runtime
## Project setup
```
yarn install

4
package.json

@ -21,6 +21,7 @@
"@heroicons/vue": "2.0.13",
"@popperjs/core": "2.11.6",
"@vue-leaflet/vue-leaflet": "0.6.1",
"@vueuse/core": "9.10.0",
"@vueuse/head": "1.0.22",
"array-timsort": "1.0.3",
"country-code-emoji": "2.3.0",
@ -36,7 +37,6 @@
"nostr-tools": "1.1.1",
"object-sizeof": "1.6.3",
"onion-regex": "2.0.8",
"path-segments": "0.1.1",
"pinia": "2.0.28",
"pinia-plugin-persistedstate-2": "2.0.8",
"pinia-shared-state": "0.2.10",
@ -49,6 +49,7 @@
"vue-meta": "3.0.0-alpha.8",
"vue-router": "4.1.6",
"vue-tailwind": "2.5.1",
"vue-use": "0.2.0",
"vue3-storage": "0.1.11",
"webpack": "5.75.0",
"write-yaml-file": "4.2.0"
@ -81,6 +82,7 @@
"style-loader": "3.3.1",
"tailwindcss": "3.2.4",
"vue-cli-plugin-yaml-loader": "~1.0.0",
"vue-template-compiler": "2.7.14",
"webpack-cli": "5.0.0",
"yaml-loader": "^0.6.0"
},

135
src/components/layout/HeaderComponent.vue

@ -2,58 +2,52 @@
<Disclosure as="nav" class="bg-gray-800" v-slot="{ open }">
<!-- <div class="mx-auto max-w-none"> -->
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="relative flex h-16 items-center mx-3 justify-between">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<!-- Mobile menu button-->
<DisclosureButton class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
<span class="sr-only">Open main menu</span>
<Bars3Icon v-if="!open" class="block h-6 w-6" aria-hidden="true" />
<XMarkIcon v-else class="block h-6 w-6" aria-hidden="true" />
</DisclosureButton>
</div>
<span class="block text-center text-slate-50 text-xl">
nostr.watch
<sup class="relative -top-2" style="font-size: 0.6rem">{{ version }}</sup>
</span>
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<!-- <div class="w-32 flex flex-shrink-0 items-center">
</div> -->
<div class="hidden sm:ml-6 sm:block">
<div class="flex space-x-4">
<router-link
to="/relays/find"
:active-class="`bg-white/25`"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Relays
</router-link>
<a
href="https://github.com/dskvr/nostr-watch/issues/new/choose"
target="_blank"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
<div class="relative h-16 flex-none md:flex lg:flex items-center mx-3 justify-between">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<!-- Mobile menu button-->
<DisclosureButton class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
<span class="sr-only">Open main menu</span>
<Bars3Icon v-if="!open" class="block h-6 w-6" aria-hidden="true" />
<XMarkIcon v-else class="block h-6 w-6" aria-hidden="true" />
</DisclosureButton>
</div>
<span class="inline-block text-center text-slate-50 text-xl mt-4 md:mt-0">
nostr.watch
<sup class="relative -top-2" style="font-size: 0.6rem">{{ version }}</sup>
</span>
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div class="hidden sm:ml-6 md:block">
<div class="flex space-x-4">
<router-link
to="/relays/find"
:active-class="`bg-white/25`"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Feedback
</a>
<a
href="https://github.com/dskvr/nostr-watch"
target="_blank"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Git
</a>
<!-- <router-link to="/about">about</router-link>
<a
href="/"
:aria-current="item.current ? 'page' : undefined">
{{ item.name }}
</a> -->
</div>
</div>
Relays
</router-link>
<a
href="https://github.com/dskvr/nostr-watch/issues/new/choose"
target="_blank"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Feedback
</a>
<a
href="https://github.com/dskvr/nostr-watch"
target="_blank"
class="text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Git
</a>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<DarkMode />
<!-- Profile dropdown -->
<Menu as="div" class="relative ml-3">
<!-- <Menu as="div" class="relative ml-3"> -->
@ -61,8 +55,7 @@
<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.getNip05 || store.user.getName }}</span>
<span class="text-white mt-1.5 mr-3">{{ }}</span>
<span class="text-white mt-1.5 mr-3 hidden md:block">{{ store.user.getNip05 || store.user.getName }}</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>
@ -91,7 +84,39 @@
<DisclosurePanel class="sm:hidden">
<div class="space-y-1 px-2 pt-2 pb-3">
<DisclosureButton v-for="item in navigation" :key="item.name" as="a" :href="item.href" :class="[item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', 'block px-3 py-2 rounded-md text-base font-medium']" :aria-current="item.current ? 'page' : undefined">{{ item.name }}</DisclosureButton>
<!-- <div class="block sm:ml-6 md:hidden">
<div class="flex space-x-4"> -->
<router-link
to="/relays/find"
:active-class="`bg-white/25`"
class="block text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Relays
</router-link>
<a
href="https://github.com/dskvr/nostr-watch/issues/new/choose"
target="_blank"
class="block text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Feedback
</a>
<a
href="https://github.com/dskvr/nostr-watch"
target="_blank"
class="block text-white hover:bg-white/25 px-3 py-2 rounded-md text-sm font-medium"
>
Git
</a>
<!-- <router-link to="/about">about</router-link>
<a
href="/"
:aria-current="item.current ? 'page' : undefined">
{{ item.name }}
</a> -->
<!-- </div>
</div> -->
</div>
</DisclosurePanel>
</Disclosure>
@ -101,10 +126,10 @@ nav.menu {
position:relative;
z-index:10;
}
nav span,
/* nav span,
nav.menu a {
display: inline-block;
}
} */
nav.menu a {
text-decoration: none;
@ -136,6 +161,7 @@ import UserLib from '@/shared/user-lib.js'
// import PreferencesComponent from '@/components/PreferencesComponent.vue'
import AuthComponent from '@/components/user/AuthComponent.vue'
import DarkMode from '@/components/partials/DarkMode.vue'
import {version} from '../../../package.json'
@ -146,6 +172,7 @@ export default defineComponent({
components: {
// PreferencesComponent,
AuthComponent,
DarkMode,
Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems,
Bars3Icon, XMarkIcon
},

28
src/components/partials/DarkMode.vue

@ -0,0 +1,28 @@
<template>
<div class="toggle-dark-mode cursor-pointer">
<svg v-on:click="toggleDark()" v-if="!isDark" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="#fff">
<path strokeLinecap="round" strokeLinejoin="round" d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
</svg>
<svg v-on:click="toggleDark()" v-if="isDark" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="#fff">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
</svg>
</div>
</template>
<script setup>
import { useDark, useToggle } from "@vueuse/core";
const isDark = useDark({
selector: "html", //element to add attribute to
attribute: "theme", // attribute name
valueDark: "dark", // attribute value for dark mode
valueLight: "light", // attribute value for light mode
})
const toggleDark = useToggle(isDark);
</script>
<style>
</style>

12
src/components/relays/blocks/MapSummary.vue

@ -51,7 +51,7 @@
</l-marker>
</l-map>
<span @click="this.handleToggleMap()">
<span @click="this.handleToggleMap()" id="map_control">
<button class="bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" v-if="!store.layout.mapIsExpanded">full map</button>
<button class="bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" v-if="store.layout.mapIsExpanded">relay list</button>
</span>
@ -585,17 +585,7 @@ export default defineComponent({
</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>

23
src/components/relays/blocks/RelaysResultTable.vue

@ -1,5 +1,5 @@
<template>
<div class="-mt-10 pt-0 px-1 sm:px-6 lg:px-8">
<div class="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">
@ -8,16 +8,16 @@
<thead>
<tr>
<th scope="col" class="text-left" colspan="2">
<span class="mr-3">
<span class="mr-3 hidden lg:inline-block">
<span
:class="getThemeBtnClass('compact')"
@click="store.prefs.changeTheme('compact')">compact</span>
@click="store.prefs.setRowTheme('compact')">compact</span>
<span
:class="getThemeBtnClass('comfortable')"
@click="store.prefs.changeTheme('comfortable')">comfortable</span>
@click="store.prefs.setRowTheme('comfortable')">comfortable</span>
<span
:class="getThemeBtnClass('spacious')"
@click="store.prefs.changeTheme('spacious')">spacious</span>
@click="store.prefs.setRowTheme('spacious')">spacious</span>
</span>
<NostrSyncPopoverNag v-if="subsection == 'favorite'" />
<span v-if="subsection != 'favorite' && store.relays.getFavorites.length" class="ml-6 text-slate-600">
@ -152,6 +152,7 @@
import RelaysLib from '@/shared/relays-lib.js'
import UserLib from '@/shared/user-lib.js'
// import { screenIs } from '@/shared/layout.js'
import {validateEvent, getEventHash, verifySignature} from 'nostr-tools'
@ -203,6 +204,14 @@
mounted(){
//console.log('navdata', this.navData, this.navData.filter( item => item.slug == this.subsection )[0], this.navData.filter( item => item.slug == this.subsection ))
this.activePageData = this.navData.filter( item => item.slug == this.subsection )[0]
// if(screenIs('sm')){
// this.user.prefs.setRowTheme('compact')
// }
// if(screenIs('md')){
// this.user.prefs.setRowTheme('comfortable')
// }
},
updated(){
// //console.log('state, updated')
@ -268,8 +277,8 @@
'bg-slate-100': index % 2,
'bg-red-50 hover:bg-red-100': this.store.relays.isFavorite(relay),
'bg-gray-50 hover:bg-slate-200': !this.store.relays.isFavorite(relay),
'text-2xl h-16': this.store.prefs.getTheme === 'spacious',
'text-xl h-9': this.store.prefs.getTheme === 'comfortable',
'xl:text-2xl xl:h-16': this.store.prefs.getTheme === 'spacious',
'xl:text-xl xl:h-9': this.store.prefs.getTheme === 'comfortable',
// '': this.store.prefs.getTheme === 'compact',
}
}

14
src/components/relays/nav/RelaysFindNav.vue

@ -1,9 +1,9 @@
<template>
<Disclosure as="nav" class="bg-white mb-5" v-slot="{ open }">
<Disclosure as="nav" id="subsection_nav" class="bg-white mb-5" v-slot="{ open }">
<div class="mx-auto max-w-7xl px-0">
<div class="flex h-12 justify-between">
<div class="flex h-12 justify-center md:justify-between">
<div class="flex px-2 lg:px-0">
<div class="hidden lg:flex lg:space-x-2">
<div class="hidden md:flex md:space-x-2 lg:flex lg:space-x-2">
<a v-for="item in store.layout.getNavGroup(this.navSlug)"
:key="`subnav-${item.slug}`"
:href="item.href"
@ -14,13 +14,13 @@
</a>
</div>
</div>
<div class="flex flex-1 items-center justify-center px-2 lg:ml-6 lg:justify-end">
<div class="hidden md:flex md:flex-1 items-center justify-center px-2 lg:ml-6 lg:justify-end">
<!-- <RelaysSearchFilter /> -->
</div>
<div class="flex items-center lg:hidden">
<div class="flex justify-center md:hidden lg:hidden">
<!-- Mobile menu button -->
<DisclosureButton class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
<span class="sr-only">Open main menu</span>
<DisclosureButton class="inline-flex items-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
<span>Menu</span>
<Bars3Icon v-if="!open" class="block h-6 w-6" aria-hidden="true" />
<XMarkIcon v-else class="block h-6 w-6" aria-hidden="true" />
</DisclosureButton>

15
src/components/relays/nav/RelaysNav.vue

@ -1,7 +1,7 @@
<template>
<div class="bg-slate-700 px-2 sm:px-4 lg:px-8 ">
<div class="lg:flex lg:h-8 mx-auto max-w-7xl h-16">
<div class="flex lg:w-32 lg:px-8 lg:ml-8 sm:px-0 sm:w-0 md:w-0"></div>
<div class="lg:flex lg:h-8 mx-auto max-w-7xl h-8">
<div class="flex lg:w-32 lg:px-8 lg:ml-8 sm:hidden sm:w-0 md:w-0"></div>
<div class="lg:flex lg:px-0">
<div class="lg:ml-6 lg:flex lg:space-x-8">
<!-- <router-link to="/relays/find" :class="isActive ? 'bg-color-white-100 text-white' : inactiveClass" class="inline-flex items-center mx-1 text-sm font-medium text-white">Relays</router-link> -->
@ -27,10 +27,11 @@
<script>
import { defineComponent, defineAsyncComponent, toRefs } from 'vue'
import {useRoute} from 'vue-router'
import { setupStore } from '@/store'
import RelaysLib from '@/shared/relays-lib.js'
import SharedComputed from '@/shared/computed.js'
import { setupNavData, mountNav, setActiveContent, loadNavContent, routeValid, parseHash, contentIsActive } from '@/shared/hash-router.js'
// import RefreshTask from '@/components/relays/tasks/RefreshTask.vue'
@ -76,12 +77,6 @@ export default defineComponent({
//console.log('mounted in relays find nav')
},
methods: Object.assign(RelaysLib, setupNavData, mountNav, setActiveContent, loadNavContent, routeValid, parseHash, contentIsActive),
computed: {
path: function() { return useRoute().path },
// isActive(){
// return (item) => item.slug==this.navActiveContent
// }
},
computed: SharedComputed,
});
</script>

19
src/components/relays/pages/RelaysFind.vue

@ -7,8 +7,10 @@
:activeSubsectionProp="activeSubsection" />
<div id="wrapper" class="mx-auto max-w-7xl">
<div class="pt-5 px-1 sm:px-6 lg:px-8" :class="{
'absolute z-900 w-1/2 top-32 bg-white/50': this.store.layout.mapIsExpanded
<div id="subsection_header" class="pt-5 px-1 sm:px-6 lg:px-8" :class="{
'absolute z-900 w-1/2 top-32': this.store.layout.mapIsExpanded,
// 'bg-white/50': !this.isMapDark,
// 'bg-black/50': this.isMapDark
}"
style="z-index:9999">
<div class="sm:flex sm:items-center">
@ -37,7 +39,7 @@
<RelaysFindNav />
</div>
</div>
<div v-if="!this.store.layout.mapIsExpanded">
<div id="relays_list_wrapper" v-if="!this.store.layout.mapIsExpanded">
<div
v-for="subsection in navSubsection"
:key="subsection.slug" >
@ -54,11 +56,11 @@
<script>
//vue
import { defineComponent, defineAsyncComponent } from 'vue'
import {useRoute} from 'vue-router'
//pinia
import { setupStore } from '@/store'
//shared methods
import RelaysLib from '@/shared/relays-lib.js'
import SharedComputed from '@/shared/computed.js'
import { parseHash } from '@/shared/hash-router.js'
//components
// import RelaysNav from '@/components/relays/nav/RelaysNav.vue'
@ -145,6 +147,7 @@ export default defineComponent({
},
async mounted() {
console.log('map expanded', this.store.layout.mapIsExpanded, 'is dark', localStorage.getItem('isDark'))
//console.log("findrelays mounted", this.results)
this.navSubsection.forEach( item => this.relaysCount[item.slug] = 0 ) //move this
@ -155,8 +158,7 @@ export default defineComponent({
// this.relaysMountNav()
},
computed: {
path: function() { return useRoute().path },
computed: Object.assign(SharedComputed, {
activeSection: function(){ return this.store.layout.getActiveItem('relays')?.slug },
activeSubsection: function(){ return this.store.layout.getActiveItem(`relays/find`)?.slug },
navSubsection: function() { return this.store.layout.getNavGroup(`relays/find`) || [] },
@ -169,8 +171,11 @@ export default defineComponent({
return this.store.relays.getAll.filter( (relay) => this.results?.[relay]?.aggregate == subsection).length
}
},
isMapDark: function(){
// return this.store.layout.mapIsExpanded && this.$storage.('isDark') == true
},
parseHash
},
}),
methods: Object.assign(RelaysLib, localMethods),

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

@ -24,7 +24,6 @@
//vue
import { defineComponent } from 'vue'
import {useRoute} from 'vue-router'
import { useHead } from '@vueuse/head'
import { setupStore } from '@/store'
//shared methods
@ -111,7 +110,6 @@ export default defineComponent({
computed: {
// activeSection: function(){ return this.store.layout.getActiveItem('relays')?.slug },
path: function() { return useRoute().path },
activeSubsection: function(){ return this.navSubsection?.slug },
navSubsection: function() { return this.store.layout.getNavGroup(`relays/${this.activeSection}`) || [] },
parseHash

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

@ -1,5 +1,4 @@
<template>
<RelaysNav/>
<MapSingle
@ -9,10 +8,9 @@
v-if="(geo instanceof Object)"
/>
<div id="wrapper" class="mt-8 mx-auto max-w-7xl">
<div id="wrapper" class="mt-8 mx-auto w-auto max-w-7xl">
<div v-if="store.tasks.isProcessing('relays') && !result" class="flex bg-slate-100 mt-12 shadow">
<div v-if="store.tasks.isProcessing('relays/check') && !result" class="data-card flex bg-slate-100 mt-12 shadow">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<span class="block lg:text-lg"><strong>Data has not yet populated and is currently being processed.</strong> Depending on the availability of of the <strong>{{ relay }}</strong>, this may or may not be populated shortly.</span>
</div>
@ -20,10 +18,10 @@
<section v-if="result">
<div class="overflow-hidden bg-slate-100 shadow sm:rounded-lg">
<div class="data-card overflow-hidden bg-slate-100 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 w-full text-xl text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
<p class="mt-1 w-auto text-xl text-gray-500" v-if="result?.info?.description">{{ result.info.description }}</p>
</div>
</div>
@ -31,13 +29,13 @@
<!-- this.result?.check?.[which] ? 'green' : 'red' -->
<div class="flex">
<div v-for="key in ['connect', 'read', 'write']" :key="key" class="text-white text-3xl flex-1 block py-6" :class="check(key)">
<div v-for="key in ['connect', 'read', 'write']" :key="key" class="text-white text-lg md:text-xl lg:text-3xl flex-1 block py-6" :class="check(key)">
<span>{{key}}</span>
</div>
</div>
<div v-if="!result?.check?.connect">
<div class="block mt-1 py-24 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">
<div class="data-card block mt-1 py-24 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-red-600 dark:text-red-300">This Relay Appears to be offline</h5>
</div>
<div class="flex bg-slate-50 shadow mt-12" v-if="Object.keys(this.result?.geo).length">
@ -47,9 +45,9 @@
</div>
</div>
<div class="mb-10 overflow-hidden bg-slate-400 border-slate-200 shadow sm:rounded-lg">
<div v-if="result?.info?.supported_nips" class="mb-10 overflow-hidden bg-slate-400 border-slate-200 shadow sm:rounded-lg">
<div class="px-1 py-2 sm:px-6">
<div class="flex" v-if="result?.info?.supported_nips">
<div class="flex">
<div class="flex-none">
<h3 class="text-lg md:text-lg lg:text-xl xl:text-3xl mb-2 px-2 align-middle mt-4 font-black">nips</h3>
</div>
@ -61,7 +59,7 @@
</div>
</div>
<div class="flex sm:rounded-lg bg-slate-50 border-slate-200 mb-10" v-if="geo?.dns">
<div class="data-card flex sm:rounded-lg bg-slate-50 border-slate-200 mb-10" v-if="geo?.dns">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<span>
The IP of <strong>{{ geo?.dns.name }}</strong> is <strong>{{ geo?.dns.data }}</strong> <br />
@ -71,7 +69,7 @@
</div>
</div>
<div class="flex sm:rounded-lg bg-slate-50 border-slate-200 border mb-10" v-if="this.result?.info?.software">
<div class="data-card flex sm:rounded-lg bg-slate-50 border-slate-200 border mb-10" v-if="this.result?.info?.software">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<span>
The current date/time in <strong>{{ geo?.city }}</strong> is <strong>{{ getLocalTime }}</strong>
@ -79,23 +77,9 @@
</div>
</div>
<div class="flex sm:rounded-lg bg-slate-50 border-slate-200 shadow" v-if="this.result?.info?.software">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<span>It's running <strong>{{ getSoftware }}:{{ result.info.version }}</strong></span>
<span class="text-sm block">
Some links...
<a
v-if="result?.info?.software.includes('+http')"
:href="result?.info?.software.replace('git+', '')"
target="_blank">
{{ result?.info?.software.includes('+https') ? 'https' : ' http' }}
</a>
<a
v-if="result?.info?.software.includes('git+')"
:href="result?.info?.software.replace('+http', '').replace('+https', '')">
git
</a>
</span>
<div class="data-card flex sm:rounded-lg bg-slate-50 border-slate-200 shadow" v-if="this.result?.info?.software">
<div class="text-clip overflow-ellipsis text-slate-800 text-lg md:text-xl lg:text-3xl flex-none w-full block py-1 text-center">
It's running <strong>{{ getSoftware }}:{{ result.info.version }}</strong>
</div>
</div>
@ -106,57 +90,56 @@
</div>
</div> -->
<div class="flex bg-slate-50 border-slate-200 mt-12 shadow" v-if="this.result?.info?.pubkey">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<div class="data-card flex bg-slate-50 border-slate-200 mt-12 shadow" v-if="this.result?.info?.pubkey">
<div class="text-slate-800 w-full text-sm md:text-lg lg:text-3xl overflow-ellipsis flex-none block py-1 text-center">
<code class="block">{{ this.result?.info.pubkey }}</code>
<span class="block lg:text-lg">was recieved via {{ relayFromUrl }}/.well-known/nostr.json</span>
</div>
</div>
<div class="flex bg-slate-50 border-slate-200 shadow mt-12" v-if="this.result?.info?.pubkey">
<div class="text-slate-800 text-3xl flex-none w-full block py-1 text-center">
<div class="data-card flex bg-slate-50 border-slate-200 shadow mt-12" v-if="this.result?.info?.pubkey">
<div class="text-slate-800 w-full flex-none block py-1 text-center">
Here's the details...
</div>
</div>
<div class="py-5 col-span-3" v-if="typeof result?.info !== 'undefined'">
<div class="overflow-hidden bg-white shadow sm:rounded-lg relative">
<div class="py-5" v-if="typeof result?.info !== 'undefined' && Object.keys(result?.info).length">
<div class="data-card overflow-hidden bg-white shadow sm:rounded-lg relative">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg md:text1xl lg:text-2xl xl:text-3xl">Relay Info <code class="text-gray-300 text-xs absolute top-3 right-3">NIP-11</code></h3>
<h3 class="text-lg md:text1xl lg:text-2xl xl:text-3xl">Relay Info</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-if="result?.info?.version">
<!-- <div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-lg font-medium text-gray-500">Connection Status</dt>
<dd class="mt-1 text-lg text-gray-900 sm:col-span-2 sm:mt-0">
<dd class="mt-1 text-lg text-gray-900 sm:mt-0">
</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">
<!-- <div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result.info?.supported_nips">
<dt class="text-lg font-medium text-gray-500">Supported Nips</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<dd class="mt-1 text-sm text-gray-900 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">
<div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.name">
<dt class="text-lg 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>
<dd class="mt-1 text-sm text-gray-900 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">
<div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.pubkey">
<dt class="text-lg 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>
<dd class="mt-1 text-sm text-gray-900 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">
<div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.email">
<dt class="text-lg 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>
<dd class="mt-1 text-sm text-gray-900 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">
<div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.software">
<dt class="text-lg font-medium text-gray-500">Software</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<dd class="mt-1 text-sm text-gray-900 sm:mt-0">
{{ getSoftware }}
<br />
{{result.info.software}}<br />
@ -174,9 +157,9 @@
</a>
</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">
<div class="py-4 sm:gap-4 sm:py-5 sm:px-6 font-extrabold" v-if="result?.info?.version">
<dt class="text-lg 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>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0"><code class="text-xs">{{ result.info.version }}</code></dd>
</div>
</dl>
@ -186,29 +169,29 @@
<div :class="getGeoWrapperClass">
<div :class="getDnsClass" class="overflow-hidden bg-white shadow sm:rounded-lg mt-8" v-if="geo">
<div :class="getDnsClass" class="data-card overflow-hidden bg-white shadow sm:rounded-lg mt-8" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg md:text1xl lg:text-2xl xl:text-3xl">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}`">
<div class="py-4 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>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
</div>
</div>
<div class="overflow-hidden bg-white shadow sm:rounded-lg mt-8" :class="getGeoClass" v-if="geo">
<div class="data-card overflow-hidden bg-white shadow sm:rounded-lg mt-8" :class="getGeoClass" v-if="geo">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg md:text1xl lg:text-2xl xl:text-3xl">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}`">
<div class="py-4 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>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0">{{ value[1] }}</dd>
</div>
</dl>
</div>
@ -217,14 +200,14 @@
<span v-if="this.events?.['0']">
<!-- <span v-if="this.events?.['0']">
<h1>OK</h1>
</span>
</span> -->
<div class="flow-root" v-if="this.events?.['1']">
<ul role="list" class="-mb-8">
<li v-for="(event, key) in this.events?.['1']" :key="key">
<!-- <li v-for="(event, key) in this.events?.['1']" :key="key">
<div class="relative pb-8" v-if="Object.keys(event).length">
<span class="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
<div class="relative flex items-start space-x-3">
@ -239,7 +222,7 @@
<div class="min-w-0 flex-1">
<div>
<a href="#" class="font-medium text-gray-900">
<!-- {{ Object.entries(this.events['0']).map( event => event[1])[0] }} -->
{{ Object.entries(this.events['0']).map( event => event[1])[0]?.lud06 }} <br/>
{{ Object.entries(this.events['0']).map( event => event[1])[0]?.name }}<br/>
{{ Object.entries(this.events['0']).map( event => event[1])[0]?.picture }}<br/>
@ -256,7 +239,7 @@
</div>
</div>
</div>
</li>
</li> -->
@ -432,8 +415,6 @@ import SharedComputed from '@/shared/computed.js'
import { countryCodeEmoji } from 'country-code-emoji';
import emoji from 'node-emoji';
import pathSegments from 'path-segments'
import { setupStore } from '@/store'
import { useHead } from '@vueuse/head'
@ -612,15 +593,11 @@ export default defineComponent({
return formatter.format(new Date())
},
getSoftware: function(){
return pathSegments(this.result?.info?.software, { last: 2 })
return this.result?.info?.software
},
cleanUrl: function(){
return (relay) => relay.replace('wss://', '')
},
relayFromUrl() {
// We will see what `params` is shortly
return `wss://${this.$route.params.relayUrl}`
},
badgeLink(){
return (nip) => `https://img.shields.io/static/v1?style=for-the-badge&label=NIP&message=${this.nipSignature(nip)}&color=black`

130
src/components/relays/tasks/RefreshTask.vue

@ -1,29 +1,35 @@
<template>
<span
v-if="!store.tasks.isActive || store.tasks.getActiveSlug === this.taskSlug"
class="text-white lg:text-sm mr-2 ml-2 mt-1.5 text-xs">
<span v-if="!store.tasks.isProcessing(this.taskSlug)">Checked {{ sinceLast }} ago</span>
<span v-if="store.tasks.isProcessing(this.taskSlug)" class="italic lg:pr-9">
<svg class="animate-spin mr-1 -mt-0.5 h-4 w-5 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{{ this.store.tasks.getProcessed(this.taskSlug).length }}/{{ this.relays.length }} Relays Checked
<div
v-if="(!store.tasks.isActive || store.tasks.getActiveSlug === this.taskSlug) && !this.isSingle"
class="inline">
<span class="text-white lg:text-sm mr-2 ml-2 mt-1 text-xs">
<span v-if="!store.tasks.isProcessing(this.taskSlug)">Checked {{ sinceLast }} ago</span>
<span v-if="store.tasks.isProcessing(this.taskSlug)" class="italic lg:pr-9 text-white lg:text-sm mr-2 ml-2 block mt-1.5 md:pt-1.5 md:mt-0 text-xs">
<svg class="animate-spin mr-1 -mt-0.5 h-4 w-5 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{{ this.store.tasks.getProcessed(this.taskSlug).length }}/{{ this.relays.length }} Relays Checked
</span>
</span>
<span class="text-white lg:text-sm mr-2 ml-2 text-xs" v-if="!store.tasks.isProcessing(this.taskSlug)">-</span>
<span class="text-white lg:text-sm mr-2 ml-2 text-xs" v-if="store.prefs.refresh && !store.tasks.isProcessing(this.taskSlug)">
Next check in: {{ untilNext }}
</span>
<button
v-if="!store.tasks.isProcessing(this.taskSlug)"
class="mr-8 my-1 py-1 px-3 text-xs rounded border-b-3 border-slate-700 bg-slate-500 font-bold text-white hover:border-slate-500 hover:bg-slate-400"
:disabled='store.tasks.isProcessing(this.taskSlug)'
@click="refreshNow()">
Check{{ relay ? ` ${relay}` : "" }} Now
</button>
</div>
<span
v-if="(store.tasks.getActiveSlug === this.taskSlug) && this.isSingle"
class="text-white lg:text-sm mr-2 ml-2 mt-1.5 text-xs mr-11">
Loading {{ relayFromUrl }}
</span>
<span class="text-white text-sm mr-2 mt-1.5" v-if="!store.tasks.isProcessing(this.taskSlug)">-</span>
<span class="text-white text-sm mr-2 mt-1.5" v-if="store.prefs.refresh && !store.tasks.isProcessing(this.taskSlug)">
Next check in: {{ untilNext }}
</span>
<button
v-if="!store.tasks.isProcessing(this.taskSlug)"
class="mr-8 my-1 py-0 px-3 text-xs rounded border-b-3 border-slate-700 bg-slate-500 font-bold text-white hover:border-slate-500 hover:bg-slate-400"
:disabled='store.tasks.isProcessing(this.taskSlug)'
@click="refreshNow()">
Check{{ relay ? ` ${relay}` : "" }} Now
</button>
</template>
<style scoped>
@ -35,11 +41,9 @@ import { defineComponent, toRefs } from 'vue'
import { setupStore } from '@/store'
import RelaysLib from '@/shared/relays-lib.js'
// import History from '@/shared/history.js'
import SharedComputed from '@/shared/computed.js'
import { Inspector } from 'nostr-relay-inspector'
// import sizeof from 'object-sizeof'
import { relays } from '../../../../relays.yaml'
import { geo } from '../../../../cache/geo.yaml'
@ -107,11 +111,9 @@ const localMethods = {
},
invalidate: async function(force, single){
// console.log('expired', !this.store.tasks.getLastUpdate(this.taskSlug), Date.now() - this.store.tasks.getLastUpdate(this.taskSlug) > this.store.prefs.expireAfter)
if( (!this.isExpired(this.taskSlug) && !force) )
return
// console.log('windowActive', this.windowActive)
if(!this.windowActive)
return
@ -123,6 +125,8 @@ const localMethods = {
if(single) {
await this.check(single)
.then((result) => this.completeRelay(single, result) )
.catch( () => this.completeRelay(single) )
}
else {
for(let index = 0; index < relays.length; index++) {
@ -130,15 +134,10 @@ const localMethods = {
const relay = relays[index]
this.check(relay)
.then((result) => this.completeRelay(relay, result) )
// .then( async () => {
// // this.history = await History()
// })
.catch( () => this.completeRelay(relay) )
.catch( () => this.completeRelay(relay) ) //wait, what? TODO: fix
}
}
}, true)
// console.log('queue', this.store.tasks.getActive)
},
@ -152,20 +151,17 @@ const localMethods = {
this.results[relay] = result
this.setCache(result)
}
if(this.store.tasks.getProcessed(this.taskSlug).length >= this.relays.length)
if(this.isSingle)
this.completeAll()
else if(this.store.tasks.getProcessed(this.taskSlug).length >= this.relays.length)
this.completeAll()
},
completeAll: function(){
//console.log('completed')
this.store.tasks.completeJob()
// this.store.tasks.finishProcessing(this.taskSlug)
this.store.tasks.updateNow(this.taskSlug)
this.store.tasks.completeJob(this.taskSlug)
this.store.relays.setAggregateCache('public', Object.keys(this.results).filter( result => this.results[result].aggregate === 'public' ))
this.store.relays.setAggregateCache('restricted', Object.keys(this.results).filter( result => this.results[result].aggregate === 'restricted' ))
this.store.relays.setAggregateCache('offline', Object.keys(this.results).filter( result => this.results[result].aggregate === 'offline' ))
//console.log('all are complete?', !this.store.tasks.isProcessing)
this.setAverageLatency()
},
@ -195,15 +191,6 @@ const localMethods = {
resolve(instance.result)
})
.run()
// .on('close', () => {
// //console.log(`${relay.url} has closed`)
// // reject()
// })
// .on('error', () => {
// // console.log(result)
// // reject()
// })
})
},
@ -231,8 +218,11 @@ const localMethods = {
}
export default defineComponent({
name: 'RefreshComponent',
components: {},
setup(props){
const {resultsProp: results} = toRefs(props)
return {
@ -240,6 +230,7 @@ export default defineComponent({
results: results
}
},
data() {
return {
relay: "",
@ -256,26 +247,16 @@ export default defineComponent({
// history: null
}
},
created(){
clearInterval(this.interval)
// document.addEventListener("visibilitychange", () => {
// if(document.visibilityState == 'visible')
// this.store.layout.setActiveTab(this.$tabid)
// // if
// // document.title = document.hidden ? "I'm away" : "I'm here";
// });
document.body.onfocus = () => {
// alert('tab focused')
//console.log(`tab #${this.$tabId} is active`)
}
document.addEventListener('visibilitychange', this.handleVisibility, false)
},
unmounted(){
clearInterval(this.interval)
// document.removeEventListener("visibilitychange", this.handleVisibility, false);
},
beforeMount(){
this.lastUpdate = this.store.tasks.getLastUpdate(this.taskSlug)
this.untilNext = this.timeUntilRefresh()
@ -284,36 +265,40 @@ export default defineComponent({
this.relays = Array.from(new Set(relays))
this.store.relays.setRelays(relays)
this.store.relays.setGeo(geo)
//console.log('total relays', this.relays, this.relays.length)
for(let ri=0;ri-this.relays.length;ri++){
const relay = this.relays[ri],
cache = this.getCache(relay)
this.results[relay] = cache
// //console.log('result', 'from result', this.results[relay], 'from cache', cache)
}
},
mounted(){
this.migrateLegacy()
// console.log('is processing', this.store.tasks.isProcessing(this.taskSlug))
if(this.store.tasks.isProcessing(this.taskSlug))
this.invalidate(true)
else
this.invalidate()
if( this.isSingle ){
this.taskSlug = 'relays/single'
this.invalidate(true, this.relayFromUrl)
} else {
if(this.store.tasks.isProcessing(this.taskSlug))
this.invalidate(true)
else
this.invalidate()
}
this.setRefreshInterval()
},
updated(){},
computed: Object.assign(SharedComputed, {
getDynamicTimeout: function(){
const calculated = this.averageLatency*this.relays.length
return calculated > 10000 ? calculated : 10000
},
}),
methods: Object.assign(localMethods, RelaysLib),
props: {
resultsProp: {
type: Object,
@ -322,6 +307,7 @@ export default defineComponent({
}
},
},
})
</script>

7
src/components/relays/tasks/TasksManager.vue

@ -9,13 +9,14 @@
<script>
import { defineComponent, toRefs } from 'vue'
import { useRoute } from 'vue-router'
import { setupStore } from '@/store'
import SharedComputed from '@/shared/computed.js'
import RefreshTask from './RefreshTask.vue'
// import RelayCanonicalsTask from './RelayCanonicalsTask.vue'
// import RelayOperatorTask from './RelayOperatorTask.vue'
@ -74,8 +75,6 @@ export default defineComponent({
this.store.tasks.active.handler()
}
},
computed: Object.assign(SharedComputed, {
path: function() { return useRoute().path },
})
computed: Object.assign(SharedComputed, {})
});
</script>

4
src/router/index.js

@ -40,6 +40,10 @@ const routes = [
},
]
},
{
path: '/relay/:protocol(ws?s)/:relayUrl(.*)',
component: RelaysSingle
},
{
path: '/relay/:relayUrl(.*)',
component: RelaysSingle

10
src/shared/computed.js

@ -1,5 +1,15 @@
import {useRoute} from 'vue-router'
export default {
isExpired: function(){
return (slug) => !this.store.tasks.getLastUpdate(slug) || Date.now() - this.store.tasks.getLastUpdate(slug) > this.store.prefs.expireAfter
},
path: function() { return useRoute().path },
relayFromUrl() {
const protocol = this.$route.params.protocol ? this.$route.params.protocol : 'wss'
return `${protocol}://${this.$route.params.relayUrl}`
},
isSingle(){
return typeof this.$route.params.relayUrl !== 'undefined'
}
}

39
src/shared/layout.js

@ -0,0 +1,39 @@
import tw from '../../tailwind.config.js'
/**
*
* Find out if a tailwind screen value matches the current window
*
* @param {string} screen
*
* @return {Object|Boolean}
*/
export const screenIs = (screen = '') => {
// "Theme" is an alias to where you keep your tailwind.config.js - most likely your project root
const screens = tw.theme.extend.screens
console.log(screens)
// create a keyed object of screens that match
const matches = Object.entries(screens).reduce((results, [name, size]) => {
const mediaQuery = typeof size === 'string' ? `(min-width: ${size})` : `(max-width: ${size.max})`;
results[name] = window.matchMedia(mediaQuery).matches;
return results;
}, {});
// show all matches when there is no screen choice
if (screen === '') {
return matches;
}
// invalid screen choice
if (!screens[screen]) {
console.error(`No match for "${screen}"`);
return false;
}
return matches[screen];
};

2
src/store/prefs.js

@ -22,7 +22,7 @@ export const usePrefsStore = defineStore('prefs', {
toggleRefresh(){ this.refresh = !this.refresh },
updateExpiration(dur) { this.duration = dur },
togglePinFavorites(){ this.pinFavorites = !this.pinFavorites },
changeTheme(theme){ this.rowTheme = theme },
setRowTheme(theme){ this.rowTheme = theme },
addFilter(key, fn){
if(this.filters.includes(key))
return

124
src/styles/main.scss

@ -3,6 +3,98 @@
@tailwind components;
@tailwind utilities;
.toggle-dark-mode {
width:32px;
height:32px;
}
#subsection_header {
background-color: rgba(255,255,255, .5)
}
:root {
--map-tiles-filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);
}
//TODO: When building out the stylesheet, convert these to WS dark: conditional.
html[theme=dark] {
background: #16171d;
color: #fff;
#subsection_header {
background-color: rgba(22, 23, 29, .5)
}
#relays_list_wrapper {
@apply bg-black/20 rounded-lg
}
div[role=menu] {
background: #16171d;
color: #fff;
a:hover {
@apply bg-white/10
}
}
#subsection_nav {
@apply bg-transparent
}
#map_control button {
background: #16171d;
color: #fff;
}
table tbody {
border-top:1px solid #333 !important;
}
table tbody tr:hover {
@apply bg-slate-50/10
}
* {
color: #f0f0f0
}
tr, tbody {
@apply bg-transparent
}
main {
.data-card {
* {
@apply border-slate-100/10
}
dl > div {
@apply border-none
}
@apply bg-slate-50/10 border-none
}
}
.leaflet-tile {
filter:var(--map-tiles-filter, none);
}
.leaflet-control-attribution {
background:#000
}
}
td ul { padding:0; margin:0; list-style: none; }
td ul li { list-style: none; }
@ -307,25 +399,25 @@ tr.offline .location {
}
@media only screen and (min-device-width: 640px)
and (max-device-width: 480px)
and (orientation: portrait) {
// @media only screen and (min-device-width: 640px)
// and (max-device-width: 480px)
// and (orientation: portrait) {
table .latency,
table .verified,
table .nip {
display: none;
}
// table .latency,
// table .verified,
// table .nip {
// display: none;
// }
}
// }
@media only screen and (min-device-width: 640px)
and (max-device-width: 480px)
and (orientation: landscape) {
// @media only screen and (min-device-width: 640px)
// and (max-device-width: 480px)
// and (orientation: landscape) {
table .nip {
display: none;
}
// table .nip {
// display: none;
// }
}
// }

20
tailwind.config.js

@ -5,12 +5,22 @@ module.exports = {
],
theme: {
extend: {
extend: {
zIndex: {
'9000': '9000',
}
zIndex: {
'9000': '9000',
},
screens: {
'sm': '640px',
// => @media (min-width: 640px) { ... }
'md': '768px',
// => @media (min-width: 768px) { ... }
'lg': '1024px',
// => @media (min-width: 1024px) { ... }
'xl': '1280px',
// => @media (min-width: 1280px) { ... }
'2xl': '1536px',
// => @media (min-width: 1536px) { ... }
}
},
}
},
variants: {
extend: {},

162
yarn.lock

@ -295,7 +295,7 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.16.4", "@babel/parser@^7.20.7":
"@babel/parser@^7.16.4", "@babel/parser@^7.18.4", "@babel/parser@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
@ -1556,6 +1556,11 @@
dependencies:
"@types/node" "*"
"@types/web-bluetooth@^0.0.16":
version "0.0.16"
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8"
integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==
"@types/ws@^8.5.1":
version "8.5.4"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"
@ -1576,33 +1581,33 @@
"@types/yargs-parser" "*"
"@unhead/dom@^1.0.9":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.0.14.tgz#8c20133c34f3c01f0725072e1af03e531cb0e735"
integrity sha512-AMOOMUNbybHrvtuddy5aEkDibL3grwUCoUT1aHHqfvtEy86t/ftRGKT4T9znx54IxlXN9FnoNpeSOg4D8OP/5w==
version "1.0.15"
resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.0.15.tgz#03d65840fa4dea279ad70241e36939aa33ee61c1"
integrity sha512-W3P9eGazfQPMZTG4ryb5oOA02Z4o16Jxo8DAihF/7Xmg/FVYY5Up9p9et7Nbb6AKNgt1PEz3Sp0xBaw+F6Uyjw==
dependencies:
"@unhead/schema" "1.0.14"
"@unhead/schema" "1.0.15"
"@unhead/schema@1.0.14", "@unhead/schema@^1.0.9":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.0.14.tgz#6ef58241b330ff8ca841f29f915ef6f892b9795e"
integrity sha512-eL03X15KvI10igudFkvV1sWUIMcMGQed0qOk+V5fyp036NZLQ5MF9ygyZOKkZ71jbdL8/1Afe8+f2tklc4v8zQ==
"@unhead/schema@1.0.15", "@unhead/schema@^1.0.9":
version "1.0.15"
resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.0.15.tgz#1e0ac9ff3261ce15adfcd69eb323768ce973811a"
integrity sha512-aWgHDHcemcx20zZun2hCFvZC6Ob4h14D4puhknuQoMkMOZcxh2ffYoJHb6mS3TeQRwAXCOsSFIHAgwIbayjk0w==
dependencies:
"@zhead/schema" "^1.0.9"
hookable "^5.4.2"
"@unhead/ssr@^1.0.9":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@unhead/ssr/-/ssr-1.0.14.tgz#b0e2e1b54f6ae0ac39e6f5fff72c748264825114"
integrity sha512-ScireDoj8AJenQPi60KjGM21d9RePBqMKOKgJbTMLQIyEC34mssoOYU1r5tOexKZJiWJXzbu5qE+NIwoY8P/yA==
version "1.0.15"
resolved "https://registry.yarnpkg.com/@unhead/ssr/-/ssr-1.0.15.tgz#96a9d3cd1c2e583d86553214d4e04622e8063304"
integrity sha512-WNFljr+HaWdrBVYyKcgLXIk5EldPSKEVJlFbo2ixmSVGnFlqMHMCui/ZrfMLG1QvvFw0ZkXonapkEc/S6loSCQ==
dependencies:
"@unhead/schema" "1.0.14"
"@unhead/schema" "1.0.15"
"@unhead/vue@^1.0.9":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@unhead/vue/-/vue-1.0.14.tgz#fb5691baa72d0bf1dd963e9233849bdbff2eb26d"
integrity sha512-PwJLCSq/eVLbLw7SbMCa20rEeL301w52UG2YhRpQTfsChBMBRGg3Nsl/PPWIurwkBXPpCsVL3AFppaCK5BCJ5Q==
version "1.0.15"
resolved "https://registry.yarnpkg.com/@unhead/vue/-/vue-1.0.15.tgz#b5cc91045e101e82276f9966976a608abe61f957"
integrity sha512-D2NQH8fBKdYgTdIDrarx24qDEblgLIwzPDeAY36PyP6ib8oG6oaI+5lrpMG+NtQ7k9LBqL5d6mQgYn/02kdjZg==
dependencies:
"@unhead/schema" "1.0.14"
"@unhead/schema" "1.0.15"
hookable "^5.4.2"
"@vue-leaflet/vue-leaflet@0.6.1":
@ -1869,6 +1874,15 @@
"@vue/compiler-core" "3.2.45"
"@vue/shared" "3.2.45"
"@vue/compiler-sfc@2.7.14":
version "2.7.14"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz#3446fd2fbb670d709277fc3ffa88efc5e10284fd"
integrity sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==
dependencies:
"@babel/parser" "^7.18.4"
postcss "^8.4.14"
source-map "^0.6.1"
"@vue/compiler-sfc@3.2.45":
version "3.2.45"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz#7f7989cc04ec9e7c55acd406827a2c4e96872c70"
@ -1983,6 +1997,16 @@
resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
integrity sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==
"@vueuse/core@9.10.0":
version "9.10.0"
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.10.0.tgz#2ef6e55ca773c5b2db1e3f13b8292af96dd32214"
integrity sha512-CxMewME07qeuzuT/AOIQGv0EhhDoojniqU6pC3F8m5VC76L47UT18DcX88kWlP3I7d3qMJ4u/PD8iSRsy3bmNA==
dependencies:
"@types/web-bluetooth" "^0.0.16"
"@vueuse/metadata" "9.10.0"
"@vueuse/shared" "9.10.0"
vue-demi "*"
"@vueuse/head@1.0.22":
version "1.0.22"
resolved "https://registry.yarnpkg.com/@vueuse/head/-/head-1.0.22.tgz#66b591d6e4dc636c6578d1c04a96bd6c2e00f3b6"
@ -1993,6 +2017,18 @@
"@unhead/ssr" "^1.0.9"
"@unhead/vue" "^1.0.9"
"@vueuse/metadata@9.10.0":
version "9.10.0"
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.10.0.tgz#1a5eb94ca755bd8e666505f47da7d88969cffdc7"
integrity sha512-G5VZhgTCapzU9rv0Iq2HBrVOSGzOKb+OE668NxhXNcTjUjwYxULkEhAw70FtRLMZc+hxcFAzDZlKYA0xcwNMuw==
"@vueuse/shared@9.10.0":
version "9.10.0"
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.10.0.tgz#49874a0f9955d28689b3133de660367c63dbc030"
integrity sha512-vakHJ2ZRklAzqmcVBL38RS7BxdBA4+5poG9NsSyqJxrt9kz0zX3P5CXMy0Hm6LFbZXUgvKdqAS3pUH1zX/5qTQ==
dependencies:
vue-demi "*"
"@webassemblyjs/ast@1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
@ -3464,11 +3500,21 @@ csstype@^2.6.8:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e"
integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
csstype@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
data-uri-to-buffer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b"
integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
debug@*, debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -3801,11 +3847,6 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
ends-with@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ends-with/-/ends-with-0.2.0.tgz#2f9da98d57a50cfda4571ce4339000500f4e6b8a"
integrity sha512-lRppY4dK3VkqBdR242sKcAJeYc8Gf/DhoX9AWvWI2RzccmLnqBQfwm2k4oSDv5MPDjUqawCauXhZkyWxkVhRsg==
enhanced-resolve@^5.10.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
@ -5728,11 +5769,6 @@ lodash@4, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lo
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lodash@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e"
integrity sha512-Kak1hi6/hYHGVPmdyiZijoQyz5x2iGVzs6w9GYB/HiXEtylY7tIoYEROMjvM1d9nXJqPOrG2MNPMn01bJ+S0Rw==
log-symbols@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
@ -6371,13 +6407,6 @@ normalize-path@^1.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
integrity sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==
normalize-path@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==
dependencies:
remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@ -6699,13 +6728,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-filepath@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-0.3.1.tgz#f51a543109c1f984688c8dc96eb0d2c3c275cc29"
integrity sha512-PxZPkBZGlOYpdToXy0OkWLZkiDvpwjCC+x3lgNDAFjghDRUPot9Yo4BGwk/BSE1jdMZY4nszloNBzsy4NqsEOg==
dependencies:
path-ends-with "^0.2.1"
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@ -6751,14 +6773,6 @@ path-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-ends-with@^0.2.1:
version "0.2.3"
resolved "https://registry.yarnpkg.com/path-ends-with/-/path-ends-with-0.2.3.tgz#2a4a4b614a5bee39dafa0f5da48e862c6d48c3de"
integrity sha512-uUACA5FDLtyXZUQNwy1FHx4s4ZIu8dycoevQztd3t0UduXsluf56JZxokQNzRW0EqtJXPe3h/Ck8jB8C+xCOsw==
dependencies:
ends-with "^0.2.0"
normalize-path "^2.0.0"
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@ -6784,14 +6798,6 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-segments@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/path-segments/-/path-segments-0.1.1.tgz#ff908ddf8577e77f6ada4831528ac2283c1527c8"
integrity sha512-wnK1l8a9T502qoaVyBGZKu9iGcmnO5wStW0BQEa5yuLhKd/PeeQlJwm9WzWYTd6MW7n0K04n88x1giEcX/62LA==
dependencies:
lodash "^2.4.1"
parse-filepath "^0.3.0"
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
@ -7457,7 +7463,7 @@ postcss@^7.0.14, postcss@^7.0.36:
picocolors "^0.2.1"
source-map "^0.6.1"
postcss@^8.1.10, postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.17, postcss@^8.4.18, postcss@^8.4.19:
postcss@^8.1.10, postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.18, postcss@^8.4.19:
version "8.4.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
@ -7773,11 +7779,6 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==
renderkid@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
@ -9004,6 +9005,11 @@ vue-router@4.1.6, vue-router@^4.0.0-0:
dependencies:
"@vue/devtools-api" "^6.4.5"
vue-router@^3.0.1:
version "3.6.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.6.5.tgz#95847d52b9a7e3f1361cb605c8e6441f202afad8"
integrity sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==
vue-style-loader@^4.1.0, vue-style-loader@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"
@ -9029,11 +9035,28 @@ vue-tailwind@2.5.1:
lodash.pick "^4.4.0"
lodash.range "^3.2.0"
vue-template-compiler@2.7.14:
version "2.7.14"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz#4545b7dfb88090744c1577ae5ac3f964e61634b1"
integrity sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==
dependencies:
de-indent "^1.0.2"
he "^1.2.0"
vue-template-es2015-compiler@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue-use@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/vue-use/-/vue-use-0.2.0.tgz#55b94c0e734ef67c191790f3ae51cb17ff83a9db"
integrity sha512-5AXDyzSQOSTL5MpiDEDZ9OiBCboM1KhbXFA+zgmrYgdSbZn9ntfBtAKQt4ous8xZOKiGehaNIB6w4qHEQ3kdmg==
dependencies:
vue "^2.5.16"
vue-router "^3.0.1"
vuex "^3.0.1"
vue3-storage@0.1.11:
version "0.1.11"
resolved "https://registry.yarnpkg.com/vue3-storage/-/vue3-storage-0.1.11.tgz#c92ae70b0d57a1959ba86a7d0178884972b1fc10"
@ -9045,6 +9068,14 @@ vue3-storage@0.1.11:
vue-class-component "^8.0.0-0"
vue-router "^4.0.0-0"
vue@^2.5.16:
version "2.7.14"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.14.tgz#3743dcd248fd3a34d421ae456b864a0246bafb17"
integrity sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==
dependencies:
"@vue/compiler-sfc" "2.7.14"
csstype "^3.1.0"
vue@^3.0.0, vue@^3.2.45:
version "3.2.45"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.45.tgz#94a116784447eb7dbd892167784619fef379b3c8"
@ -9056,6 +9087,11 @@ vue@^3.0.0, vue@^3.2.45:
"@vue/server-renderer" "3.2.45"
"@vue/shared" "3.2.45"
vuex@^3.0.1:
version "3.6.2"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"
integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"

Loading…
Cancel
Save