Add support for highlights

This commit is contained in:
Jonathan Staab 2023-06-16 17:46:23 -07:00
parent 7c9c2ee692
commit 4234cbc0e1
9 changed files with 171 additions and 104 deletions

View File

@ -1,7 +1,10 @@
# Current
- [ ] Spam
- Add configurable POW req for replies
- Add event queue and undo, use the delay to calculate POW
- [ ] Support other kinds
- Fix note truncation
- Fix note truncation, sometimes an ellipsis ends up after the last one
- [ ] Feeds load forever if a modal is open
- [ ] Support other list types than 30001
- [ ] Fix connection management stuff. Have GPT help

View File

@ -1,107 +1,20 @@
<script lang="ts">
import {pluck, without} from "ramda"
import {switcher, switcherFn} from "hurdak/lib/hurdak"
import {displayPerson, getLabelQuality, displayRelay, Tags} from "src/util/nostr"
import {parseContent, truncateContent, LINK, INVOICE, NEWLINE, TOPIC} from "src/util/notes"
import {modal} from "src/partials/state"
import MediaSet from "src/partials/MediaSet.svelte"
import QRCode from "src/partials/QRCode.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Rating from "src/partials/Rating.svelte"
import NoteContentNewline from "src/app/shared/NoteContentNewline.svelte"
import NoteContentTopic from "src/app/shared/NoteContentTopic.svelte"
import NoteContentLink from "src/app/shared/NoteContentLink.svelte"
import NoteContentPerson from "src/app/shared/NoteContentPerson.svelte"
import NoteContentQuote from "src/app/shared/NoteContentQuote.svelte"
import NoteContentEntity from "src/app/shared/NoteContentEntity.svelte"
import NoteContentKind1 from "src/app/shared/NoteContentKind1.svelte"
import NoteContentKind1985 from "src/app/shared/NoteContentKind1985.svelte"
import NoteContentKind9802 from "src/app/shared/NoteContentKind9802.svelte"
import user from "src/agent/user"
import {getPersonWithFallback} from "src/agent/db"
export let note
export let anchorId = null
export let maxLength = 700
export let showEntire = false
export let showMedia = user.getSetting("showMedia")
const getLinks = parts =>
pluck(
"value",
parts.filter(x => x.type === LINK && x.canDisplay)
)
const isStandalone = i => {
return (
!shortContent[i - 1] ||
shortContent[i - 1].type === NEWLINE ||
!shortContent[i + 1] ||
shortContent[i + 1].type === NEWLINE
)
}
const fullContent = parseContent(note)
const shortContent = truncateContent(fullContent, {maxLength, showEntire, showMedia})
const rating = note.kind === 1985 ? getLabelQuality("review/relay", note) : null
const links = getLinks(shortContent)
const extraLinks = without(links, getLinks(fullContent))
</script>
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
<p>
{#if rating}
{@const [type, value] = Tags.from(note)
.reject(t => ["l", "L"].includes(t[0]))
.first()}
{@const action = switcher(type, {
r: () => modal.push({type: "relay/detail", url: value}),
p: () => modal.push({type: "person/feed", pubkey: value}),
e: () => modal.push({type: "note/detail", note: {id: value}}),
})}
{@const display = switcherFn(type, {
r: () => displayRelay({url: value}),
p: () => displayPerson(getPersonWithFallback(value)),
e: () => "a note",
default: "something",
})}
<div class="mb-4 flex items-center gap-2 border-l-2 border-solid border-gray-5 pl-2">
Rated
{#if action}
<Anchor on:click={action}>{display}</Anchor>
{:else}
{display}
{/if}
<div class="text-sm">
<Rating inert value={rating} />
</div>
</div>
{/if}
{#each shortContent as { type, value }, i}
{#if type === NEWLINE}
<NoteContentNewline {value} />
{:else if type === TOPIC}
<NoteContentTopic {value} />
{:else if type === INVOICE}
<div on:click|stopPropagation>
<QRCode fullWidth onClick="copy" code={value} />
</div>
{:else if type === LINK}
<NoteContentLink {value} showMedia={showMedia && isStandalone(i)} />
{:else if type.match(/^nostr:np(rofile|ub)$/)}
<NoteContentPerson {value} />
{:else if type.startsWith("nostr:") && showMedia && isStandalone(i) && value.id !== anchorId}
<NoteContentQuote {note} {value}>
<div slot="note-content" let:quote>
<svelte:self note={quote} />
</div>
</NoteContentQuote>
{:else if type.startsWith("nostr:")}
<NoteContentEntity {value} />
{:else}
{value}
{/if}
{" "}
{/each}
</p>
{#if showMedia && extraLinks.length > 0}
<MediaSet links={extraLinks} />
{/if}
</div>
{#if note.kind === 1985}
<NoteContentKind1985 {note} {anchorId} {maxLength} {showEntire} />
{:else if note.kind === 9802}
<NoteContentKind9802 {note} {anchorId} {maxLength} {showEntire} {showMedia} />
{:else}
<NoteContentKind1 {note} {anchorId} {maxLength} {showEntire} {showMedia} />
{/if}

View File

@ -0,0 +1,67 @@
<script lang="ts">
import {without} from "ramda"
import {
parseContent,
getLinks,
truncateContent,
LINK,
INVOICE,
NEWLINE,
TOPIC,
} from "src/util/notes"
import MediaSet from "src/partials/MediaSet.svelte"
import QRCode from "src/partials/QRCode.svelte"
import NoteContentNewline from "src/app/shared/NoteContentNewline.svelte"
import NoteContentTopic from "src/app/shared/NoteContentTopic.svelte"
import NoteContentLink from "src/app/shared/NoteContentLink.svelte"
import NoteContentPerson from "src/app/shared/NoteContentPerson.svelte"
import NoteContentQuote from "src/app/shared/NoteContentQuote.svelte"
import NoteContentEntity from "src/app/shared/NoteContentEntity.svelte"
export let note, anchorId, maxLength, showEntire
export let showMedia = false
const fullContent = parseContent(note)
const shortContent = truncateContent(fullContent, {maxLength, showEntire, showMedia})
const links = getLinks(shortContent)
const extraLinks = without(links, getLinks(fullContent))
export const isNewline = i =>
!shortContent[i] || shortContent[i].type === NEWLINE
export const isStartOrEnd = i => isNewline(i - 1) || isNewline(i + 1)
</script>
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
<p>
{#each shortContent as { type, value }, i}
{#if type === NEWLINE}
<NoteContentNewline {value} />
{:else if type === TOPIC}
<NoteContentTopic {value} />
{:else if type === INVOICE}
<div on:click|stopPropagation>
<QRCode fullWidth onClick="copy" code={value} />
</div>
{:else if type === LINK}
<NoteContentLink {value} showMedia={showMedia && isStartOrEnd(i) && value.url.includes('/')} />
{:else if type.match(/^nostr:np(rofile|ub)$/)}
<NoteContentPerson {value} />
{:else if type.startsWith("nostr:") && showMedia && isStartOrEnd(i) && value.id !== anchorId}
<NoteContentQuote {note} {value}>
<div slot="note-content" let:quote>
<svelte:self note={quote} />
</div>
</NoteContentQuote>
{:else if type.startsWith("nostr:")}
<NoteContentEntity {value} />
{:else}
{value}
{/if}
{" "}
{/each}
</p>
{#if showMedia && extraLinks.length > 0}
<MediaSet links={extraLinks} />
{/if}
</div>

View File

@ -0,0 +1,16 @@
<script lang="ts">
import {getLabelQuality} from "src/util/nostr"
import NoteContentRating from "src/app/shared/NoteContentRating.svelte"
import NoteContentKind1 from "src/app/shared/NoteContentKind1.svelte"
export let note, anchorId, maxLength, showEntire
const rating = getLabelQuality("review/relay", note)
</script>
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
{#if rating}
<NoteContentRating {note} {rating} />
{/if}
<NoteContentKind1 {note} {anchorId} {maxLength} {showEntire} />
</div>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import {Tags} from "src/util/nostr"
import {canDisplayUrl} from "src/util/notes"
import NoteContentKind1 from "src/app/shared/NoteContentKind1.svelte"
import NoteContentLink from "src/app/shared/NoteContentLink.svelte"
export let note, anchorId, maxLength, showEntire, showMedia
const ref = Tags.from(note).getMeta("r")
</script>
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
<div class="border-l-2 border-solid border-gray-5 pl-4">
<NoteContentKind1 {note} {anchorId} {maxLength} {showEntire} />
</div>
</div>
{#if ref}
<NoteContentLink {showMedia} value={{url: ref, canDisplay: canDisplayUrl(ref)}} />
{/if}

View File

@ -10,8 +10,6 @@
hidden = true
}
console.log(value)
let hidden = false
</script>

View File

@ -0,0 +1,39 @@
<script lang="ts">
import {switcher, switcherFn} from "hurdak/lib/hurdak"
import {displayPerson, displayRelay, Tags} from "src/util/nostr"
import {modal} from "src/partials/state"
import Anchor from "src/partials/Anchor.svelte"
import Rating from "src/partials/Rating.svelte"
import {getPersonWithFallback} from "src/agent/db"
export let note, rating
const [type, value] = Tags.from(note)
.reject(t => ["l", "L"].includes(t[0]))
.first()
const action = switcher(type, {
r: () => modal.push({type: "relay/detail", url: value}),
p: () => modal.push({type: "person/feed", pubkey: value}),
e: () => modal.push({type: "note/detail", note: {id: value}}),
})
const display = switcherFn(type, {
r: () => displayRelay({url: value}),
p: () => displayPerson(getPersonWithFallback(value)),
e: () => "a note",
default: "something",
})
</script>
<div class="mb-4 flex items-center gap-2 border-l-2 border-solid border-gray-5 pl-2">
Rated
{#if action}
<Anchor on:click={action}>{display}</Anchor>
{:else}
{display}
{/if}
<div class="text-sm">
<Rating inert value={rating} />
</div>
</div>

View File

@ -6,7 +6,6 @@ import {tryJson, avg} from "src/util/misc"
import {invoiceAmount} from "src/util/lightning"
export const noteKinds = [1, 1985, 30023, 30018, 10001, 1063, 9802]
// export const noteKinds = [9802]
export const personKinds = [0, 2, 3, 10001, 10002]
export const userKinds = personKinds.concat([10000, 30001, 30078])
export const appDataKeys = [

View File

@ -1,4 +1,4 @@
import {last, identity} from "ramda"
import {last, pluck, identity} from "ramda"
import {nip19} from "nostr-tools"
import {first} from "hurdak/lib/hurdak"
import {fromNostrURI} from "src/util/nostr"
@ -14,7 +14,7 @@ export const NOSTR_NPUB = "nostr:npub"
export const NOSTR_NPROFILE = "nostr:nprofile"
export const NOSTR_NADDR = "nostr:naddr"
const canDisplayUrl = url => !url.match(/\.(apk|docx|xlsx|csv|dmg)/)
export const canDisplayUrl = url => !url.match(/\.(apk|docx|xlsx|csv|dmg)/)
export const parseContent = ({content, tags = []}) => {
const result = []
@ -160,7 +160,7 @@ export const parseContent = ({content, tags = []}) => {
return result
}
export const truncateContent = (content, {showEntire, maxLength, showMedia}) => {
export const truncateContent = (content, {showEntire, maxLength, showMedia = false}) => {
if (showEntire) {
return content
}
@ -194,3 +194,15 @@ export const truncateContent = (content, {showEntire, maxLength, showMedia}) =>
return result
}
export const getLinks = parts =>
pluck(
"value",
parts.filter(x => x.type === LINK && x.canDisplay)
)
export const isStandalone = (content, i) =>
!content[i - 1] ||
content[i - 1].type === NEWLINE ||
!content[i + 1] ||
content[i + 1].type === NEWLINE