Longform Notes
This commit is contained in:
@@ -192,6 +192,8 @@
|
|||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
||||||
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */; };
|
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */; };
|
||||||
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5588229F33F5B00DC6A45 /* StringCodable.swift */; };
|
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5588229F33F5B00DC6A45 /* StringCodable.swift */; };
|
||||||
|
4CA9275D2A28FF630098A105 /* LongformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9275C2A28FF630098A105 /* LongformView.swift */; };
|
||||||
|
4CA9275F2A2902B20098A105 /* LongformPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9275E2A2902B20098A105 /* LongformPreview.swift */; };
|
||||||
4CA927612A290E340098A105 /* EventShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927602A290E340098A105 /* EventShell.swift */; };
|
4CA927612A290E340098A105 /* EventShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927602A290E340098A105 /* EventShell.swift */; };
|
||||||
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
|
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
|
||||||
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
|
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
|
||||||
@@ -670,6 +672,8 @@
|
|||||||
4CA927712A2A5D480098A105 /* error.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; };
|
4CA927712A2A5D480098A105 /* error.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; };
|
||||||
4CA927742A2A5E2F0098A105 /* varint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = varint.h; sourceTree = "<group>"; };
|
4CA927742A2A5E2F0098A105 /* varint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = varint.h; sourceTree = "<group>"; };
|
||||||
4CA927752A2A5E2F0098A105 /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = "<group>"; };
|
4CA927752A2A5E2F0098A105 /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = "<group>"; };
|
||||||
|
4CA9275C2A28FF630098A105 /* LongformView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformView.swift; sourceTree = "<group>"; };
|
||||||
|
4CA9275E2A2902B20098A105 /* LongformPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformPreview.swift; sourceTree = "<group>"; };
|
||||||
4CA927602A290E340098A105 /* EventShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventShell.swift; sourceTree = "<group>"; };
|
4CA927602A290E340098A105 /* EventShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventShell.swift; sourceTree = "<group>"; };
|
||||||
4CA927622A290EB10098A105 /* EventTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTop.swift; sourceTree = "<group>"; };
|
4CA927622A290EB10098A105 /* EventTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTop.swift; sourceTree = "<group>"; };
|
||||||
4CA927642A290F1A0098A105 /* TimeDot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeDot.swift; sourceTree = "<group>"; };
|
4CA927642A290F1A0098A105 /* TimeDot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeDot.swift; sourceTree = "<group>"; };
|
||||||
@@ -1270,6 +1274,15 @@
|
|||||||
);
|
);
|
||||||
path = nostrscript;
|
path = nostrscript;
|
||||||
};
|
};
|
||||||
|
4CA9275B2A28FF570098A105 /* Longform */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CA9275C2A28FF630098A105 /* LongformView.swift */,
|
||||||
|
4CA9275E2A2902B20098A105 /* LongformPreview.swift */,
|
||||||
|
);
|
||||||
|
path = Longform;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CA927682A290F8F0098A105 /* Components */ = {
|
4CA927682A290F8F0098A105 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1362,6 +1375,7 @@
|
|||||||
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
|
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
|
||||||
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
|
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
|
||||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
|
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
|
||||||
|
4CA9275B2A28FF570098A105 /* Longform */,
|
||||||
4CA927602A290E340098A105 /* EventShell.swift */,
|
4CA927602A290E340098A105 /* EventShell.swift */,
|
||||||
);
|
);
|
||||||
path = Events;
|
path = Events;
|
||||||
@@ -1826,6 +1840,7 @@
|
|||||||
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
|
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
|
||||||
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
||||||
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
||||||
|
4CA9275D2A28FF630098A105 /* LongformView.swift in Sources */,
|
||||||
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
||||||
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
|
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
|
||||||
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
||||||
@@ -2023,6 +2038,7 @@
|
|||||||
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
||||||
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
||||||
4C363A962827096D006E126D /* PostBlock.swift in Sources */,
|
4C363A962827096D006E126D /* PostBlock.swift in Sources */,
|
||||||
|
4CA9275F2A2902B20098A105 /* LongformPreview.swift in Sources */,
|
||||||
4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */,
|
4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */,
|
||||||
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
||||||
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ struct EventView: View {
|
|||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
} else if event.known_kind == .longform {
|
||||||
|
LongformPreview(state: damus, ev: event)
|
||||||
} else {
|
} else {
|
||||||
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
||||||
//.padding([.top], 6)
|
//.padding([.top], 6)
|
||||||
|
|||||||
51
damus/Views/Events/Longform/LongformPreview.swift
Normal file
51
damus/Views/Events/Longform/LongformPreview.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
90
damus/Views/Events/Longform/LongformView.swift
Normal file
90
damus/Views/Events/Longform/LongformView.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -314,6 +314,65 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
|||||||
return render_blocks(blocks: blocks, profiles: profiles)
|
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 {
|
func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
|
||||||
var invoices: [Invoice] = []
|
var invoices: [Invoice] = []
|
||||||
var urls: [UrlType] = []
|
var urls: [UrlType] = []
|
||||||
@@ -334,22 +393,8 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
|
|||||||
}
|
}
|
||||||
return str + mention_str(m, profiles: profiles)
|
return str + mention_str(m, profiles: profiles)
|
||||||
case .text(let txt):
|
case .text(let txt):
|
||||||
var trimmed = txt
|
return str + reduce_text_block(blocks: blocks, ind: ind, txt: txt, one_note_ref: one_note_ref)
|
||||||
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 str + CompatibleText(stringLiteral: trimmed)
|
|
||||||
case .relay(let relay):
|
case .relay(let relay):
|
||||||
return str + CompatibleText(stringLiteral: relay)
|
return str + CompatibleText(stringLiteral: relay)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user