From 5fc98007d2b3c948e96a6da2c99be984d1288067 Mon Sep 17 00:00:00 2001
From: Jonathan Staab
Date: Sat, 10 Jun 2023 12:41:45 -0700
Subject: [PATCH] Add better relay review support
---
CHANGELOG.md | 4 ++++
ROADMAP.md | 4 +---
src/agent/network.ts | 6 +++---
src/app/shared/Feed.svelte | 6 ++++++
src/app/shared/NoteContent.svelte | 32 +++++++++++++++++++++++++++--
src/app/shared/PersonNotes.svelte | 2 +-
src/app/shared/RelayCard.svelte | 7 +++++++
src/app/shared/RelaySearch.svelte | 28 ++++++++++++++++++++++---
src/app/shared/RelayTitle.svelte | 7 +++++++
src/app/views/NoteDetail.svelte | 2 +-
src/app/views/RelayDetail.svelte | 19 +++++++++++++++--
src/app/views/RelayReview.svelte | 14 +++++++++----
src/partials/Compose.svelte | 7 ++++++-
src/partials/ContentEditable.svelte | 5 ++++-
src/partials/Rating.svelte | 8 ++++++--
src/util/misc.ts | 2 +-
src/util/nostr.ts | 11 +++++++++-
17 files changed, 139 insertions(+), 25 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4692c90..ad54097a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+# 0.2.31
+
+- [x] Added the ability to view and write reviews on relays, with ratings
+
# 0.2.30
- [x] Prefer followed users when mentioning people
diff --git a/ROADMAP.md b/ROADMAP.md
index a7d07d84..35ee514a 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -3,9 +3,6 @@
- [ ] List detail pages with follow all and add all to list
- [ ] Use vida to stream development
- [ ] Fix connection management stuff. Have GPT help
-- [ ] Relay reviews
- - Add curated relay list, and an easy way to view content on the relays
- - Deploy ontology.coracle.social
- [ ] Add preview proxy thing
- [ ] White-labeled
- [ ] Add invite code registration for relay
@@ -21,6 +18,7 @@
# Core
+- [ ] Deploy ontology.coracle.social
- [ ] Add threads - replies by self get shown at the top of replies?
- [ ] Show link previews when posting
- [ ] Embedded music players for Spotify, youtube, etc
diff --git a/src/agent/network.ts b/src/agent/network.ts
index 5cc9ed89..72f2d514 100644
--- a/src/agent/network.ts
+++ b/src/agent/network.ts
@@ -255,7 +255,7 @@ const loadParents = (notes, opts = {}) => {
return load({
relays: sampleRelays(aggregateScores(notesWithParent.map(getRelaysForEventParent)), 0.3),
- filter: {kinds: [1], ids: notesWithParent.map(findReplyId)},
+ filter: {ids: notesWithParent.map(findReplyId)},
...opts,
})
}
@@ -268,7 +268,7 @@ const streamContext = ({notes, onChunk, maxDepth = 2}) => {
const loadChunk = (events, depth) => {
// Remove anything from the chunk we've already seen
- events = events.filter(e => e.kind === 1 && !seen.has(e.id))
+ events = events.filter(e => ![7, 9735].includes(e.kind) && !seen.has(e.id))
// If we have no new information, no need to re-subscribe
if (events.length === 0) {
@@ -333,7 +333,7 @@ const streamContext = ({notes, onChunk, maxDepth = 2}) => {
const applyContext = (notes, context) => {
context = context.map(assoc("isContext", true))
- const replies = context.filter(propEq("kind", 1))
+ const replies = context.filter(e => ![7, 9735].includes(e.kind))
const reactions = context.filter(propEq("kind", 7))
const zaps = context.filter(propEq("kind", 9735))
diff --git a/src/app/shared/Feed.svelte b/src/app/shared/Feed.svelte
index 04105d31..b4835b06 100644
--- a/src/app/shared/Feed.svelte
+++ b/src/app/shared/Feed.svelte
@@ -21,6 +21,7 @@
export let shouldDisplay = always(true)
export let parentsTimeout = 500
export let invertColors = false
+ export let onEvent = null
let notes = []
let notesBuffer = []
@@ -90,6 +91,11 @@
// Show replies grouped by parent whenever possible
const merged = sortBy(e => -e.created_at, mergeParents(combined))
+ // Notify caller if they asked for it
+ for (const e of merged) {
+ onEvent?.(e)
+ }
+
// Split into notes before and after we started loading
const [bottom, top] = partition(e => e.created_at < since, merged)
diff --git a/src/app/shared/NoteContent.svelte b/src/app/shared/NoteContent.svelte
index 84b57c24..8913f839 100644
--- a/src/app/shared/NoteContent.svelte
+++ b/src/app/shared/NoteContent.svelte
@@ -1,14 +1,16 @@
diff --git a/src/app/shared/RelayCard.svelte b/src/app/shared/RelayCard.svelte
index 234815be..dc560c4c 100644
--- a/src/app/shared/RelayCard.svelte
+++ b/src/app/shared/RelayCard.svelte
@@ -8,12 +8,14 @@
import {displayRelay} from "src/util/nostr"
import {modal} from "src/partials/state"
import Toggle from "src/partials/Toggle.svelte"
+ import Rating from "src/partials/Rating.svelte"
import Anchor from "src/partials/Anchor.svelte"
import pool from "src/agent/pool"
import user from "src/agent/user"
import {loadAppData} from "src/app/state"
export let relay
+ export let rating = null
export let theme = "gray-8"
export let showStatus = false
export let hideActions = false
@@ -82,6 +84,11 @@
{message}
{/if}
+ {#if rating}
+
+
+
+ {/if}
{#if !hideActions}
diff --git a/src/app/shared/RelaySearch.svelte b/src/app/shared/RelaySearch.svelte
index ec40fcf9..35f25a88 100644
--- a/src/app/shared/RelaySearch.svelte
+++ b/src/app/shared/RelaySearch.svelte
@@ -1,9 +1,13 @@
@@ -35,7 +57,7 @@
{/if}
{#each !q && hideIfEmpty ? [] : search(q).slice(0, limit) as relay (relay.url)}
-
+
{/each}
diff --git a/src/app/shared/RelayTitle.svelte b/src/app/shared/RelayTitle.svelte
index 7440d998..1e5a33c3 100644
--- a/src/app/shared/RelayTitle.svelte
+++ b/src/app/shared/RelayTitle.svelte
@@ -4,10 +4,12 @@
import {displayRelay} from "src/util/nostr"
import {webSocketURLToPlainOrBase64} from "src/util/misc"
import {poll, stringToHue, hsl} from "src/util/misc"
+ import Rating from "src/partials/Rating.svelte"
import Anchor from "src/partials/Anchor.svelte"
import pool from "src/agent/pool"
export let relay
+ export let rating = null
let quality = null
let message = null
@@ -47,4 +49,9 @@
class:opacity-1={showStatus}>
{message}
+ {#if rating}
+
+
+
+ {/if}
diff --git a/src/app/views/NoteDetail.svelte b/src/app/views/NoteDetail.svelte
index 3e4c9249..ff541667 100644
--- a/src/app/views/NoteDetail.svelte
+++ b/src/app/views/NoteDetail.svelte
@@ -34,7 +34,7 @@
if (!displayNote.pubkey) {
await network.load({
relays: sampleRelays(relays),
- filter: {kinds: [1], ids: [displayNote.id]},
+ filter: {ids: [displayNote.id]},
onChunk: events => {
displayNote = asDisplayEvent(first(events))
},
diff --git a/src/app/views/RelayDetail.svelte b/src/app/views/RelayDetail.svelte
index ed2f7b27..74e6b4de 100644
--- a/src/app/views/RelayDetail.svelte
+++ b/src/app/views/RelayDetail.svelte
@@ -1,24 +1,33 @@
@@ -27,6 +36,11 @@
+ {#if rating}
+
+
+
+ {/if}
{#if relay.description}
{relay.description}
{/if}
@@ -34,9 +48,10 @@
{#if activeTab === "reviews"}
diff --git a/src/app/views/RelayReview.svelte b/src/app/views/RelayReview.svelte
index de54ba53..1b52d5ea 100644
--- a/src/app/views/RelayReview.svelte
+++ b/src/app/views/RelayReview.svelte
@@ -4,24 +4,26 @@
import Button from "src/partials/Button.svelte"
import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte"
- import Textarea from "src/partials/Textarea.svelte"
+ import Compose from "src/partials/Compose.svelte"
import Rating from "src/partials/Rating.svelte"
import user from "src/agent/user"
import cmd from "src/agent/cmd"
export let url
- let review
+ let compose
let rating
const onSubmit = () => {
+ const review = compose.parse()
+
cmd
.createLabel({
content: review,
tagClient: false,
tags: [
["L", "social.coracle.ontology"],
- ["l", "review", "social.coracle.ontology"],
+ ["l", "review/relay", "social.coracle.ontology", JSON.stringify({quality: rating})],
["r", url],
],
})
@@ -41,7 +43,11 @@
-
+
diff --git a/src/partials/Compose.svelte b/src/partials/Compose.svelte
index 8c893ab9..61702fda 100644
--- a/src/partials/Compose.svelte
+++ b/src/partials/Compose.svelte
@@ -217,7 +217,12 @@
-
+
diff --git a/src/partials/ContentEditable.svelte b/src/partials/ContentEditable.svelte
index b43fb48a..f9c6a168 100644
--- a/src/partials/ContentEditable.svelte
+++ b/src/partials/ContentEditable.svelte
@@ -1,4 +1,6 @@
+ import cx from "classnames"
import {range} from "hurdak/lib/hurdak"
+ export let inert = false
export let value = 1
{#each range(0, 5) as x}
= x / 5}
+ class={cx("fa fa-star text-gray-5", {
+ "cursor-pointer hover:text-warning hover:opacity-75": !inert,
+ "text-warning": value >= x / 5,
+ })}
on:click={() => {
value = x / 5
}} />
diff --git a/src/util/misc.ts b/src/util/misc.ts
index 170fb1f2..8e6b8889 100644
--- a/src/util/misc.ts
+++ b/src/util/misc.ts
@@ -219,7 +219,7 @@ export const defer = (): Deferred => {
return Object.assign(p, {resolve, reject})
}
-export const avg = xs => sum(xs) / xs.length
+export const avg = xs => (xs.length > 0 ? sum(xs) / xs.length : 0)
// https://stackoverflow.com/a/21682946
export const stringToHue = value => {
diff --git a/src/util/nostr.ts b/src/util/nostr.ts
index d5113d0a..7c1d82bf 100644
--- a/src/util/nostr.ts
+++ b/src/util/nostr.ts
@@ -2,7 +2,7 @@ import type {DisplayEvent} from "src/util/types"
import {is, fromPairs, mergeLeft, last, identity, objOf, prop, flatten, uniq} from "ramda"
import {nip19} from "nostr-tools"
import {ensurePlural, ellipsize, first} from "hurdak/lib/hurdak"
-import {tryJson} from "src/util/misc"
+import {tryJson, avg} from "src/util/misc"
import {invoiceAmount} from "src/util/lightning"
export const personKinds = [0, 2, 3, 10001, 10002]
@@ -60,6 +60,9 @@ export class Tags {
filter(f) {
return new Tags(this.tags.filter(f))
}
+ reject(f) {
+ return new Tags(this.tags.filter(t => !f(t)))
+ }
any(f) {
return this.filter(f).exists()
}
@@ -363,3 +366,9 @@ export const processZaps = (zaps, author) =>
export const fromNostrURI = s => s.replace(/^[\w\+]+:\/?\/?/, "")
export const toNostrURI = s => `web+nostr://${s}`
+
+export const getLabelQuality = (label, event) =>
+ tryJson(() => JSON.parse(last(Tags.from(event).type("l").equals(label).first())).quality)
+
+export const getAvgQuality = (label, events) =>
+ avg(events.map(e => getLabelQuality(label, e)).filter(identity))