Browse Source
* Add relay.nostr.scot Add relay.nostr.scot * Add rsslay.nostr.moe (#210) A custom fork of the rss-bridge relay of fiatjaf [piraces/rsslay](https://github.com/piraces/rsslay) Co-authored-by: Sandwich <299465+dskvr@users.noreply.github.com> * remove e.e. * kind3 editing, ssl, various improvements * working relay list editor * remove debugging * move buttons * show/hide button for logged in/out * bugfix * fix column issue * remove debugging * reduce wait time * disable edit button while relays are refreshing * add some gates * resets after save Co-authored-by: Paul Rollo <kingrollo@gmail.com> Co-authored-by: Raul Piraces Alastuey <raul.piraces@gmail.com> Co-authored-by: dskvr <dskvr@users.noreply.github.com>develop
Sandwich
2 years ago
committed by
GitHub
25 changed files with 698 additions and 204 deletions
@ -0,0 +1,191 @@ |
|||||
|
<template> |
||||
|
<div class="inline" v-if="store.user.getPublicKey.length"> |
||||
|
<div class="inline text-left"> |
||||
|
|
||||
|
<span v-if="savedSuccess" class="inline-block mr-3"> |
||||
|
<svg class="h-4 w-4 inline-block" fill="none" stroke="#32CD32" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> |
||||
|
</svg> |
||||
|
Saved to <span>{{ savedSuccess }}</span> |
||||
|
</span> |
||||
|
|
||||
|
<button |
||||
|
:title="this.store.tasks.getActiveSlug === 'relays/check' ? 'disabled while relays are checking' : ''" |
||||
|
ref="btnRef" |
||||
|
type="button" |
||||
|
v-on:click="this.store.tasks.getActiveSlug === 'relays/check' ? false : toggleEditor()" |
||||
|
:class="{ |
||||
|
'cursor-not-allowed opacity-40': this.store.tasks.getActiveSlug === 'relays/check', |
||||
|
'cursor-pointer': this.store.tasks.getActiveSlug === 'user/relay/list' |
||||
|
}" |
||||
|
class="mr-3 inline-flex items-center justify-center rounded-md border border-transparent bg-white/20 px-4 py-2 text-m font-medium text-white shadow-sm hover:bg-white/40 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"> |
||||
|
<span v-if="this.store.layout.editorExpanded"> |
||||
|
Cancel |
||||
|
</span> |
||||
|
|
||||
|
<span v-if="!this.store.layout.editorExpanded"> |
||||
|
Edit Relay List |
||||
|
</span> |
||||
|
</button> |
||||
|
|
||||
|
<button |
||||
|
:title="!changed ? 'nothing to save' : ''" |
||||
|
ref="btnRef" |
||||
|
type="button" |
||||
|
v-on:click="changed ? persistChanges() : false" |
||||
|
v-if="this.store.layout.editorExpanded && store.tasks.getActiveSlug !== 'user/relay/list'" |
||||
|
:class="{ |
||||
|
'cursor-not-allowed opacity-40': !changed, |
||||
|
'cursor-pointer': changed |
||||
|
}" |
||||
|
class="mr-3 inline-flex items-center justify-center rounded-md border border-transparent bg-white/20 px-4 py-2 text-m font-medium text-white shadow-sm hover:bg-white/40 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"> |
||||
|
Save |
||||
|
</button> |
||||
|
|
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script> |
||||
|
// import { createPopper } from "@popperjs/core"; |
||||
|
import { defineComponent, toRefs } from 'vue' |
||||
|
import { setupStore } from '@/store/' |
||||
|
import safeStringify from 'fast-safe-stringify' |
||||
|
import { getEventHash, validateEvent, verifySignature } from 'nostr-tools' |
||||
|
import RelaysLib from '@/shared/relays-lib' |
||||
|
import { RelayPool } from 'nostr' |
||||
|
import objHash from 'object-hash' |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: "NostrSyncPopoverNag", |
||||
|
setup(props){ |
||||
|
const {editorProp: editor} = toRefs(props) |
||||
|
return { |
||||
|
store : setupStore(), |
||||
|
editor: editor |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
changed: false, |
||||
|
hashCache: null, |
||||
|
hashOG: null, |
||||
|
savedTo: [], |
||||
|
savedSuccess: null, |
||||
|
interval: null, |
||||
|
// editor: false, |
||||
|
// popoverShow: false |
||||
|
} |
||||
|
}, |
||||
|
mounted(){ |
||||
|
this.hashOG = objHash(structuredClone(this.store.user.getKind3)) |
||||
|
this.hashCache = structuredClone(this.hashOG) |
||||
|
this.store.layout.editorOff() |
||||
|
this.interval = setInterval( () => { |
||||
|
if(this.savedTo.length) |
||||
|
this.savedSuccess = this.savedTo.shift() |
||||
|
else |
||||
|
this.savedSuccess = null |
||||
|
|
||||
|
const hashCurrent = objHash(this.store.user.getKind3), |
||||
|
hashCache = this.hashCache, |
||||
|
hashOG = this.hashOG |
||||
|
|
||||
|
if(hashCache === hashCurrent) |
||||
|
return |
||||
|
|
||||
|
if(hashOG === hashCurrent ) |
||||
|
return this.changed = false |
||||
|
|
||||
|
console.log('input cache did not match', hashCache) |
||||
|
|
||||
|
console.log( |
||||
|
'changed?', |
||||
|
this.changed, |
||||
|
'ok..', |
||||
|
hashCache, |
||||
|
objHash(this.store.user.getKind3), |
||||
|
hashCache == objHash(this.store.user.getKind3) |
||||
|
) |
||||
|
|
||||
|
this.hashCache = objHash(structuredClone(this.store.user.getKind3)) |
||||
|
this.changed = true |
||||
|
|
||||
|
}, 500) |
||||
|
}, |
||||
|
unmounted(){ |
||||
|
clearInterval(this.interval) |
||||
|
this.store.layout.editorOff() |
||||
|
}, |
||||
|
methods: Object.assign(RelaysLib, { |
||||
|
toggleEditor: async function(){ |
||||
|
this.store.layout.toggleEditor() |
||||
|
if(this.store.layout.editorExpanded) |
||||
|
this.queueJob( |
||||
|
'user/relay/list', |
||||
|
async () => { |
||||
|
await this.store.user.setKind3() |
||||
|
.then( () => { |
||||
|
Object.keys(this.store.user.kind3).forEach( key => { |
||||
|
this.store.relays.setFavorite(key) |
||||
|
}) |
||||
|
this.store.tasks.completeJob() |
||||
|
}) |
||||
|
.catch( err => { |
||||
|
console.error('error!', err) |
||||
|
this.store.tasks.completeJob() |
||||
|
}) |
||||
|
}, |
||||
|
true |
||||
|
) |
||||
|
}, |
||||
|
persistChanges: async function(){ |
||||
|
const event = { |
||||
|
created_at: Math.floor(Date.now()/1000), |
||||
|
kind: 3, |
||||
|
content: safeStringify(this.store.user.kind3), |
||||
|
tags: [...this.store.user.kind3Event.tags], |
||||
|
pubkey: this.store.user.getPublicKey, |
||||
|
} |
||||
|
event.id = getEventHash(event) |
||||
|
|
||||
|
console.log('kind3 event', event) |
||||
|
|
||||
|
console.log(window.nostr, typeof window.nostr.signEvent) |
||||
|
|
||||
|
const signedEvent = await window.nostr.signEvent(structuredClone(event)) |
||||
|
|
||||
|
let ok = validateEvent(signedEvent) |
||||
|
let veryOk = verifySignature(signedEvent) |
||||
|
|
||||
|
if(!ok || !veryOk) |
||||
|
return |
||||
|
|
||||
|
console.log('valid event?', ok, veryOk) |
||||
|
|
||||
|
const pool = new RelayPool( Object.keys(this.store.user.kind3) ) |
||||
|
|
||||
|
pool.on('open', relay=>{ |
||||
|
relay.send(['EVENT', signedEvent]) |
||||
|
}) |
||||
|
pool.on('ok', relay => { |
||||
|
this.savedTo.push(relay.url) |
||||
|
}) |
||||
|
|
||||
|
this.hashOG = objHash(JSON.parse(event.content)) |
||||
|
this.hashCache = this.hashOG |
||||
|
this.changed = false |
||||
|
}, |
||||
|
togglePopover: function(){ |
||||
|
// if(this.popoverShow){ |
||||
|
// this.popoverShow = false; |
||||
|
// } else { |
||||
|
// this.popoverShow = true; |
||||
|
// createPopper(this.$refs.btnRef, this.$refs.popoverRef, { |
||||
|
// placement: "left" |
||||
|
// }); |
||||
|
// } |
||||
|
}, |
||||
|
}) |
||||
|
}) |
||||
|
</script> |
@ -1,53 +0,0 @@ |
|||||
<template> |
|
||||
<div class="inline"> |
|
||||
<div class="inline text-left"> |
|
||||
<button |
|
||||
ref="btnRef" |
|
||||
type="button" |
|
||||
v-on:click="togglePopover()" |
|
||||
class="items-start cursor-not-allowed inline-flex items-center rounded border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"> |
|
||||
Sync Favs with Nostr |
|
||||
</button> |
|
||||
<div ref="popoverRef" |
|
||||
v-bind:class="{'hidden': !popoverShow, 'block': popoverShow}" |
|
||||
class="bg-pink-600 border-0 mr-3 z-50 font-normal leading-normal text-sm max-w-xs text-left no-underline break-words rounded-lg |
|
||||
"> |
|
||||
<div> |
|
||||
<div class="bg-pink-600 text-white opacity-75 font-semibold p-3 mb-0 border-b border-solid border-slate-100 uppercase rounded-t-lg"> |
|
||||
Coming soon |
|
||||
</div> |
|
||||
<div class="text-white p-3"> |
|
||||
This feature depends on the finalization of |
|
||||
<a href="https://github.com/nostr-protocol/nips/pull/32" target="_blank">NIP-23 [link]</a>, |
|
||||
if you actively develop a nostr client, please provide input on the NIP to help make the |
|
||||
implementation of user relay lists easier for everyone. |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import { createPopper } from "@popperjs/core"; |
|
||||
|
|
||||
export default { |
|
||||
name: "NostrSyncPopoverNag", |
|
||||
data() { |
|
||||
return { |
|
||||
popoverShow: false |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
togglePopover: function(){ |
|
||||
if(this.popoverShow){ |
|
||||
this.popoverShow = false; |
|
||||
} else { |
|
||||
this.popoverShow = true; |
|
||||
createPopper(this.$refs.btnRef, this.$refs.popoverRef, { |
|
||||
placement: "left" |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -0,0 +1,125 @@ |
|||||
|
<template> |
||||
|
<span |
||||
|
v-if="this.store.tasks.getActiveSlug === taskSlug && isLoggedIn" |
||||
|
class="text-white lg:text-sm mr-10 ml-2 mt-1.5 text-xs"> |
||||
|
<span>Retrieving Relays List...</span> |
||||
|
</span> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
||||
|
|
||||
|
<script> |
||||
|
import crypto from 'crypto' |
||||
|
// import { RelayPool } from 'nostr' |
||||
|
|
||||
|
import { defineComponent, toRefs } from 'vue' |
||||
|
|
||||
|
import { setupStore } from '@/store' |
||||
|
|
||||
|
import SharedMethods from '@/shared/relays-lib.js' |
||||
|
import UserMethods from '@/shared/user-lib.js' |
||||
|
import SharedComputed from '@/shared/computed.js' |
||||
|
|
||||
|
import { relays } from '../../../../relays.yaml' |
||||
|
|
||||
|
const localMethods = new Object() |
||||
|
|
||||
|
localMethods.invalidate = function(force){ |
||||
|
if( !this.isExpired(this.taskSlug) && !force ) |
||||
|
return |
||||
|
|
||||
|
if( !this.isLoggedIn ) |
||||
|
return |
||||
|
|
||||
|
if( !this.store.prefs.useKind3 ) |
||||
|
return |
||||
|
|
||||
|
console.log('wtf?', this.taskSlug, !this.isExpired(this.taskSlug) && !force) |
||||
|
|
||||
|
this.queueKind3(this.taskSlug) |
||||
|
} |
||||
|
|
||||
|
localMethods.timeUntilRefresh = function(){ |
||||
|
return this.timeSince(Date.now()-(this.store.tasks.getLastUpdate(this.taskSlug)+this.store.prefs.duration-Date.now())) |
||||
|
} |
||||
|
|
||||
|
localMethods.timeSinceRefresh = function(){ |
||||
|
return this.timeSince(this.store.tasks.getLastUpdate(this.taskSlug)) || Date.now() |
||||
|
} |
||||
|
|
||||
|
localMethods.hash = function(relay){ |
||||
|
return crypto.createHash('md5').update(relay).digest('hex'); |
||||
|
} |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: 'TemplateTask', |
||||
|
components: {}, |
||||
|
data() { |
||||
|
return { |
||||
|
taskSlug: 'user/relay/list', |
||||
|
kind3Remote: new Object(), |
||||
|
kind3Local: {} |
||||
|
} |
||||
|
}, |
||||
|
setup(props){ |
||||
|
const {resultsProp: results} = toRefs(props) |
||||
|
const {forceProp: force} = toRefs(props) |
||||
|
return { |
||||
|
store : setupStore(), |
||||
|
results: results, |
||||
|
force: force |
||||
|
} |
||||
|
}, |
||||
|
created(){ |
||||
|
clearInterval(this.interval) |
||||
|
}, |
||||
|
unmounted(){ |
||||
|
clearInterval(this.interval) |
||||
|
}, |
||||
|
beforeMount(){ |
||||
|
this.lastUpdate = this.store.tasks.getLastUpdate(this.taskSlug) |
||||
|
this.untilNext = this.timeUntilRefresh() |
||||
|
this.sinceLast = this.timeSinceRefresh() |
||||
|
|
||||
|
this.relays = Array.from(new Set(relays)) |
||||
|
}, |
||||
|
mounted(){ |
||||
|
console.log('task', this.taskSlug, 'is processing:', this.store.tasks.isProcessing(this.taskSlug)) |
||||
|
if(this.store.tasks.isProcessing(this.taskSlug)) |
||||
|
this.invalidate(true) |
||||
|
else |
||||
|
this.invalidate(this.force) |
||||
|
}, |
||||
|
updated(){}, |
||||
|
computed: Object.assign(SharedComputed, { |
||||
|
getDynamicTimeout: function(){ |
||||
|
return this.averageLatency*this.relays.length |
||||
|
}, |
||||
|
}), |
||||
|
methods: Object.assign(localMethods, UserMethods, SharedMethods), |
||||
|
props: { |
||||
|
resultsProp: { |
||||
|
type: Object, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
forceProp: { |
||||
|
type: Boolean, |
||||
|
default(){ |
||||
|
return false |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
#refresh { font-size: 12pt; color:#666; margin-bottom:15px } |
||||
|
#refresh button { cursor: pointer; border-radius: 3px; border: 1px solid #a0a0a0; color:#333 } |
||||
|
#refresh button:hover {color:#000;} |
||||
|
#refresh button[disabled] {color:#999 !important; border-color:#e0e0e0} |
||||
|
</style> |
||||
|
|
Loading…
Reference in new issue