Longform Notes

This commit is contained in:
William Casarin
2023-06-01 11:53:59 -07:00
parent 518886912c
commit 4d995fd04c
5 changed files with 219 additions and 15 deletions

View File

@@ -42,6 +42,8 @@ struct EventView: View {
} else {
EmptyView()
}
} else if event.known_kind == .longform {
LongformPreview(state: damus, ev: event)
} else {
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
//.padding([.top], 6)

View File

@@ -0,0 +1,51 @@
//
// LongformPreview.swift
// damus
//
// Created by William Casarin on 2023-06-01.
//
import SwiftUI
struct LongformPreview: View {
let state: DamusState
let event: LongformEvent
@ObservedObject var artifacts: NoteArtifactsModel
init(state: DamusState, ev: NostrEvent) {
self.state = state
self.event = LongformEvent.parse(from: ev)
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
}
func Words(_ words: Int) -> Text {
Text(verbatim: words.description) + Text(verbatim: " ") + Text("Words")
}
var body: some View {
EventShell(state: state, event: event.event, options: [.no_mentions]) {
NavigationLink(destination: LongformView(event: event)) {
VStack(alignment: .leading, spacing: 10) {
Text(event.title ?? "Untitled")
.font(.title)
Text(event.summary ?? "")
.foregroundColor(.gray)
if case .loaded(let arts) = artifacts.state {
Words(arts.words)
.font(.footnote)
}
}
.padding()
}
.buttonStyle(.plain)
}
}
}
struct LongformPreview_Previews: PreviewProvider {
static var previews: some View {
LongformPreview(state: test_damus_state(), ev: test_longform_event.event)
}
}

View File

@@ -0,0 +1,90 @@
//
// LongformEvent.swift
// damus
//
// Created by William Casarin on 2023-06-01.
//
import SwiftUI
struct LongformEvent {
let event: NostrEvent
var title: String? = nil
var image: URL? = nil
var summary: String? = nil
var published_at: Date? = nil
static func parse(from ev: NostrEvent) -> LongformEvent {
var longform = LongformEvent(event: ev)
for tag in ev.tags {
guard tag.count >= 2 else { continue }
switch tag[0] {
case "title": longform.title = tag[1]
case "image": longform.image = URL(string: tag[1])
case "summary": longform.summary = tag[1]
case "published_at":
longform.published_at = Double(tag[1]).map { d in Date(timeIntervalSince1970: d) }
default:
break
}
}
return longform
}
}
struct LongformView: View {
let state: DamusState
let event: LongformEvent
@ObservedObject var artifacts: NoteArtifactsModel
init(state: DamusState, event: LongformEvent) {
self.state = state
self.event = event
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(event.event.id).artifacts_model)
}
var options: EventViewOptions {
return [.wide, .no_mentions, .no_replying_to]
}
var body: some View {
EventShell(state: state, event: event.event, options: options) {
Content
}
}
var Content: some View {
Group {
if case .loaded(let artifacts) = artifacts.state {
SelectableText(attributedString: artifacts.content.attributed, size: .selected)
.padding(.horizontal)
} else {
Text("")
}
}
}
}
let test_longform_event = LongformEvent.parse(from:
.init(content: "## Let me tell you why coffee is awesome\n**IT JUST IS**",
pubkey: "pk",
kind: NostrKind.longform.rawValue,
tags: [
["title", "Coffee is awesome"],
["summary", "Did you know coffee is awesome?"],
["published_at", "1685638715"],
["t", "coffee"],
["t", "coffeechain"],
["image", "https://cdn.jb55.com/s/038fe8f558153b52.jpg"],
])
)
struct LongformView_Previews: PreviewProvider {
static var previews: some View {
LongformView(state: test_damus_state(), event: test_longform_event)
}
}

View File

@@ -314,6 +314,65 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
return render_blocks(blocks: blocks, profiles: profiles)
}
func render_blocks_longform(blocks bs: Blocks) -> NoteArtifacts {
var invoices: [Invoice] = []
var urls: [UrlType] = []
let blocks = bs.blocks
var ind: Int = -1
let txt: CompatibleText = blocks.reduce(CompatibleText()) { str, block in
ind = ind + 1
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + reduce_text_block(blocks: blocks, ind: ind, txt: txt, one_note_ref: false)
case .relay(let relay):
return str + CompatibleText(stringLiteral: relay)
case .hashtag(let htag):
return str + hashtag_str(htag)
case .invoice(let invoice):
invoices.append(invoice)
return str
case .url(let url):
let url_type = classify_url(url)
switch url_type {
case .media:
urls.append(url_type)
return str
case .link(let url):
urls.append(url_type)
return str + url_str(url)
}
}
}
return NoteArtifacts(content: txt, words: bs.words, urls: urls, invoices: invoices)
}
func reduce_text_block(blocks: [Block], ind: Int, txt: String, one_note_ref: Bool) -> CompatibleText {
var trimmed = txt
if let prev = blocks[safe: ind-1],
case .url(let u) = prev,
classify_url(u).is_media != nil {
trimmed = " " + trim_prefix(trimmed)
}
if let next = blocks[safe: ind+1] {
if case .url(let u) = next, classify_url(u).is_media != nil {
trimmed = trim_suffix(trimmed)
} else if case .mention(let m) = next, m.type == .event, one_note_ref {
trimmed = trim_suffix(trimmed)
}
}
return CompatibleText(stringLiteral: trimmed)
}
func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
var invoices: [Invoice] = []
var urls: [UrlType] = []
@@ -334,22 +393,8 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
}
return str + mention_str(m, profiles: profiles)
case .text(let txt):
var trimmed = txt
if let prev = blocks[safe: ind-1],
case .url(let u) = prev,
classify_url(u).is_media != nil {
trimmed = " " + trim_prefix(trimmed)
}
return str + reduce_text_block(blocks: blocks, ind: ind, txt: txt, one_note_ref: one_note_ref)
if let next = blocks[safe: ind+1] {
if case .url(let u) = next, classify_url(u).is_media != nil {
trimmed = trim_suffix(trimmed)
} else if case .mention(let m) = next, m.type == .event, one_note_ref {
trimmed = trim_suffix(trimmed)
}
}
return str + CompatibleText(stringLiteral: trimmed)
case .relay(let relay):
return str + CompatibleText(stringLiteral: relay)