Fix link parsing, use write relays to publish

This commit is contained in:
Jonathan Staab 2023-02-09 11:05:47 -06:00
parent 9b276885e6
commit 3d546e9004
15 changed files with 80 additions and 47 deletions

View File

@ -2,7 +2,7 @@
import "@fortawesome/fontawesome-free/css/fontawesome.css"
import "@fortawesome/fontawesome-free/css/solid.css"
import {find, identity, nthArg, pluck} from 'ramda'
import {find, is, identity, nthArg, pluck} from 'ramda'
import {onMount} from "svelte"
import {createMap} from 'hurdak/lib/hurdak'
import {writable, get} from "svelte/store"
@ -364,13 +364,24 @@
{#if $toast}
<div
class="fixed top-0 left-0 right-0 pointer-events-none z-10"
class="fixed top-0 left-0 right-0 z-10"
transition:fly={{y: -50, duration: 300}}
>
<div
class="rounded bg-accent shadow-xl mx-24 sm:mx-32 mt-2 p-3 text-white text-center border border-dark"
>
{#if is(String, $toast.message)}
{$toast.message}
{:else}
<div>
{$toast.message.text}
{#if $toast.message.link}
<a class="ml-1 underline" href={$toast.message.link.href}>
{$toast.message.link.text}
</a>
{/if}
</div>
{/if}
</div>
</div>
{/if}

View File

@ -1,6 +1,6 @@
import type {Person} from 'src/util/types'
import type {Readable} from 'svelte/store'
import {last, uniqBy, prop} from 'ramda'
import {reject, last, propEq, uniqBy, prop} from 'ramda'
import {derived, get} from 'svelte/store'
import {Tags} from 'src/util/nostr'
import pool from 'src/agent/pool'
@ -56,6 +56,9 @@ export const getRelays = (pubkey?: string) => {
return relays
}
export const getWriteRelays = (...args) =>
reject(propEq('write', '!'), getRelays(...args))
export const getEventRelays = event => {
return uniqBy(
prop('url'),

View File

@ -2,7 +2,7 @@ import {prop, pick, join, uniqBy, last} from 'ramda'
import {get} from 'svelte/store'
import {first} from "hurdak/lib/hurdak"
import {Tags, isRelay, roomAttrs, displayPerson} from 'src/util/nostr'
import {keys, publish, getRelays, database} from 'src/agent'
import {keys, publish, getWriteRelays, database} from 'src/agent'
const updateUser = (relays, updates) =>
publishEvent(relays, 0, {content: JSON.stringify(updates)})
@ -31,7 +31,7 @@ const createDirectMessage = (relays, pubkey, content) =>
const createNote = (relays, content, mentions = [], topics = []) => {
mentions = mentions.map(p => {
const {url} = first(getRelays(p))
const {url} = first(getWriteRelays(p))
const name = displayPerson(database.getPersonWithFallback(p))
return ["p", p, url, name]
@ -39,7 +39,7 @@ const createNote = (relays, content, mentions = [], topics = []) => {
topics = topics.map(t => ["t", t])
publishEvent(relays, 1, {content, tags: mentions.concat(topics)})
return publishEvent(relays, 1, {content, tags: mentions.concat(topics)})
}
const createReaction = (relays, note, content) => {
@ -56,7 +56,7 @@ const createReaction = (relays, note, content) => {
}
const createReply = (relays, note, content, mentions = [], topics = []) => {
mentions = mentions.map(p => ["p", p, prop('url', first(getRelays(p)))])
mentions = mentions.map(p => ["p", p, prop('url', first(getWriteRelays(p)))])
topics = topics.map(t => ["t", t])
const {url} = getBestRelay(relays, note)
@ -92,7 +92,7 @@ const getBestRelay = (relays, event) => {
return root
}
return first(getRelays(event.pubkey).concat(relays))
return first(getWriteRelays(event.pubkey).concat(relays))
}
const publishEvent = (relays, kind, {content = '', tags = []} = {}) => {

View File

@ -5,7 +5,7 @@ import {createMap, ellipsize} from 'hurdak/lib/hurdak'
import {get} from 'svelte/store'
import {renderContent} from 'src/util/html'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {user, database, getRelays, keys} from 'src/agent'
import {user, database, getRelays, getWriteRelays, keys} from 'src/agent'
import defaults from 'src/agent/defaults'
import {toast, routes, modal, settings, logUsage} from 'src/app/ui'
import cmd from 'src/app/cmd'
@ -71,7 +71,7 @@ export const removeRelay = async url => {
defaults.relays = modify(defaults.relays)
if (person) {
await cmd.setRelays(getRelays(), modify(person.relays || []))
await cmd.setRelays(getWriteRelays(), modify(person.relays || []))
}
}
@ -83,7 +83,7 @@ export const setRelayWriteCondition = async (url, write) => {
defaults.relays = modify(defaults.relays)
if (person) {
await cmd.setRelays(getRelays(), modify(person.relays || []))
await cmd.setRelays(getWriteRelays(), modify(person.relays || []))
}
}

View File

@ -17,7 +17,7 @@ export const routes = {
// Toast
export interface Toast<T> extends Writable<T> {
show(type: string, message: string, timeout?: number): void
show(type: string, message: any, timeout?: number): void
}
export const toast = writable(null) as Toast<any>
@ -34,10 +34,8 @@ toast.show = (type, message, timeout = 5) => {
}, timeout * 1000)
}
// Modals
export const modal = {
history: [],
set: data => {

View File

@ -14,7 +14,7 @@
import Badge from "src/partials/Badge.svelte"
import Compose from "src/partials/Compose.svelte"
import Card from "src/partials/Card.svelte"
import {user, database, getRelays, getEventRelays} from 'src/agent'
import {user, database, getEventRelays} from 'src/agent'
import cmd from 'src/app/cmd'
export let note
@ -33,7 +33,6 @@
const links = $settings.showLinkPreviews ? extractUrls(note.content) || [] : []
const showEntire = anchorId === note.id
const interactive = !anchorId || !showEntire
const relays = getEventRelays(note)
const person = database.watch('people', () => database.getPersonWithFallback(note.pubkey))
let likes, flags, like, flag
@ -50,7 +49,7 @@
const target = e.target as HTMLElement
if (interactive && !['I'].includes(target.tagName) && !target.closest('a')) {
modal.set({type: 'note/detail', note, relays})
modal.set({type: 'note/detail', note, relays: getEventRelays(note)})
}
}
@ -66,7 +65,7 @@
return navigate('/login')
}
const event = await cmd.createReaction(getRelays(), note, content)
const event = await cmd.createReaction(getEventRelays(note), note, content)
if (content === '+') {
likes = likes.concat(event)
@ -78,7 +77,7 @@
}
const deleteReaction = e => {
cmd.deleteEvent(getRelays(), [e.id])
cmd.deleteEvent(getEventRelays(note), [e.id])
if (e.content === '+') {
likes = reject(propEq('pubkey', $user.pubkey), likes)
@ -139,7 +138,10 @@
<div class="flex gap-4 items-center justify-between">
<Badge person={$person} />
<Anchor
href={"/" + nip19.neventEncode({id: note.id, relays: pluck('url', relays.slice(0, 5))})}
href={"/" + nip19.neventEncode({
id: note.id,
relays: pluck('url', getEventRelays(note).slice(0, 5)),
})}
class="text-sm text-light"
type="unstyled">
{formatTimestamp(note.created_at)}

View File

@ -1,14 +1,24 @@
<script lang="ts">
import {objOf} from 'ramda'
import {onMount} from 'svelte'
import {nip19} from 'nostr-tools'
import {navigate} from 'svelte-routing'
import Content from 'src/partials/Content.svelte'
import NoteDetail from 'src/views/NoteDetail.svelte'
import Person from 'src/routes/Person.svelte'
export let entity
const {type, data} = nip19.decode(entity) as {type: string, data: any}
const relays = (data.relays || []).map(objOf('url'))
let type, data, relays
onMount(() => {
try {
({type, data} = nip19.decode(entity) as {type: string, data: any})
relays = (data.relays || []).map(objOf('url'))
} catch (e) {
navigate('/')
}
})
</script>
{#if type === "nevent"}

View File

@ -13,7 +13,7 @@
let {data: roomId} = nip19.decode(entity) as {data: string}
let room = database.watch('rooms', rooms => rooms.get(roomId))
const getRoomRelays = $room => {
const getRoomRelays = () => {
let relays = getRelays()
if ($room) {
@ -24,7 +24,7 @@
}
const listenForMessages = async cb => {
const relays = getRoomRelays(database.rooms.get(roomId))
const relays = getRoomRelays()
return listen(
relays,
@ -42,7 +42,7 @@
}
const loadMessages = async ({until, limit}) => {
const relays = getRoomRelays($room)
const relays = getRoomRelays()
const events = await load(relays, {kinds: [42], '#e': [roomId], until, limit})
if (events.length) {
@ -57,7 +57,7 @@
}
const sendMessage = content =>
cmd.createChatMessage(getRelays(), roomId, content)
cmd.createChatMessage(getRoomRelays(), roomId, content)
</script>
<Channel

View File

@ -4,7 +4,7 @@
import {personKinds} from 'src/util/nostr'
import {batch, now} from 'src/util/misc'
import Channel from 'src/partials/Channel.svelte'
import {database, getRelays, user, listen, keys} from 'src/agent'
import {database, getRelays, getWriteRelays, user, listen, keys} from 'src/agent'
import {messages} from 'src/app'
import {routes} from 'src/app/ui'
import cmd from 'src/app/cmd'
@ -52,8 +52,9 @@
}
const sendMessage = async content => {
const relays = getWriteRelays().concat(getRelays(pubkey))
const cyphertext = await crypt.encrypt(pubkey, content)
const event = await cmd.createDirectMessage(getRelays(), pubkey, cyphertext)
const event = await cmd.createDirectMessage(relays, pubkey, cyphertext)
// Return unencrypted content so we can display it immediately
return {...event, content}

View File

@ -14,7 +14,7 @@
import Notes from "src/views/person/Notes.svelte"
import Likes from "src/views/person/Likes.svelte"
import Network from "src/views/person/Network.svelte"
import {database, getRelays, listen, user, keys} from "src/agent"
import {database, getRelays, getWriteRelays, listen, user, keys} from "src/agent"
import {modal} from "src/app"
import loaders from "src/app/loaders"
import {routes} from "src/app/ui"
@ -76,13 +76,13 @@
const tag = ["p", pubkey, relay.url, person.name || ""]
const petnames = reject(t => t[1] === pubkey, $user.petnames).concat([tag])
cmd.setPetnames(getRelays(), petnames)
cmd.setPetnames(getWriteRelays(), petnames)
}
const unfollow = async () => {
const petnames = reject(t => t[1] === pubkey, $user.petnames)
cmd.setPetnames(getRelays(), petnames)
cmd.setPetnames(getWriteRelays(), petnames)
}
const openAdvanced = () => {

View File

@ -10,7 +10,7 @@
import Button from "src/partials/Button.svelte"
import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte"
import {user, getRelays} from "src/agent"
import {user, getWriteRelays} from "src/agent"
import {toast} from "src/app"
import {routes} from "src/app/ui"
import cmd from "src/app/cmd"
@ -45,7 +45,7 @@
const submit = async event => {
event.preventDefault()
await cmd.updateUser(getRelays(), values)
await cmd.updateUser(getWriteRelays(), values)
navigate(routes.person($user.pubkey, 'profile'))

View File

@ -74,14 +74,12 @@ export const fromParentOffset = (element, offset): [HTMLElement, number] => {
}
export const extractUrls = content => {
const regex = /(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z]{1,6}\b([-a-zA-Z0-9@:%_\+.~#?!&//=;]*)/gi
const regex = /(https?:\/\/)?(www\.)?[-a-z0-9@:%._\+~#=]{1,256}\.[a-z]{1,6}\b([-a-z0-9@:%_\+.~#?!&//=;]*)/gi
const urls = content.match(regex)
return (urls || [])
// Skip decimals like 3.5 and ellipses which have more than one dot in a row
.filter(url => !url.match(/^[\d\.]+$/) && !url.match(/\.{2}/))
// Add protocol on to the beginning of the url
.map(url => url.startsWith('http') ? url : '//' + url)
}
export const renderContent = content => {
@ -92,7 +90,7 @@ export const renderContent = content => {
for (const url of extractUrls(content)) {
const $a = document.createElement('a')
$a.href = url
$a.href = 'https://' + url
$a.target = "_blank"
$a.className = "underline"

View File

@ -6,7 +6,7 @@
import Content from "src/partials/Content.svelte"
import Textarea from "src/partials/Textarea.svelte"
import Button from "src/partials/Button.svelte"
import {getRelays, database} from 'src/agent'
import {getWriteRelays, database} from 'src/agent'
import {toast, modal} from "src/app"
import cmd from "src/app/cmd"
@ -35,8 +35,8 @@
toast.show("error", "Please enter a name for your room.")
} else {
const event = room.id
? await cmd.updateRoom(getRelays(), room)
: await cmd.createRoom(getRelays(), room)
? await cmd.updateRoom(getWriteRelays(), room)
: await cmd.createRoom(getWriteRelays(), room)
await database.rooms.bulkPatch({id: room.id || event.id, joined: 1})

View File

@ -1,7 +1,8 @@
<script>
import {onMount} from "svelte"
import {nip19} from 'nostr-tools'
import {quantify} from 'hurdak/lib/hurdak'
import {last, whereEq, find, reject, propEq} from 'ramda'
import {last, whereEq, find, reject, pluck, propEq} from 'ramda'
import {fly} from 'svelte/transition'
import {navigate} from "svelte-routing"
import {fuzzy} from "src/util/misc"
@ -12,12 +13,12 @@
import Content from "src/partials/Content.svelte"
import Modal from "src/partials/Modal.svelte"
import Heading from 'src/partials/Heading.svelte'
import {database, user, getRelays} from "src/agent"
import {database, user, getWriteRelays} from "src/agent"
import {toast, modal} from "src/app"
import cmd from "src/app/cmd"
let input = null
let relays = getRelays()
let relays = getWriteRelays()
let showSettings = false
let q = ''
let search
@ -34,9 +35,18 @@
const {content, mentions, topics} = input.parse()
if (content) {
await cmd.createNote(relays, content, mentions, topics)
const event = await cmd.createNote(relays, content, mentions, topics)
toast.show("info", `Your note has been created!`)
toast.show("info", {
text: `Your note has been created!`,
link: {
text: 'View',
href: "/" + nip19.neventEncode({
id: event.id,
relays: pluck('url', relays.slice(0, 5)),
}),
},
})
modal.clear()
}

View File

@ -5,7 +5,7 @@
import Button from "src/partials/Button.svelte"
import Content from 'src/partials/Content.svelte'
import SelectButton from "src/partials/SelectButton.svelte"
import {user, getRelays} from 'src/agent'
import {user, getWriteRelays} from 'src/agent'
import {modal} from 'src/app'
import cmd from 'src/app/cmd'
@ -28,7 +28,7 @@
.concat([["p", $modal.person.pubkey, muffleValue.toString()]])
.filter(t => last(t) !== "1")
cmd.muffle(getRelays(), muffleTags)
cmd.muffle(getWriteRelays(), muffleTags)
history.back()
}