mirror of
git://jb55.com/damus
synced 2024-09-30 00:40:45 +00:00
artifacts: allow unseparated note artifacts
This is needed for longform events. Right now we treat unseparated note artifacts as a list of blocks, but we will likely need to render these blocks into lists of attributed texts with image blocks inbetween.
This commit is contained in:
parent
4d995fd04c
commit
374610a21a
@ -287,6 +287,7 @@
|
|||||||
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
||||||
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
||||||
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
||||||
|
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; };
|
||||||
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
||||||
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
||||||
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
||||||
@ -770,6 +771,7 @@
|
|||||||
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
||||||
|
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
||||||
@ -1596,6 +1598,7 @@
|
|||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
||||||
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
||||||
|
4CFD502E2A2DA45800A229DB /* MediaView.swift */,
|
||||||
);
|
);
|
||||||
path = Images;
|
path = Images;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1850,6 +1853,7 @@
|
|||||||
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */,
|
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */,
|
||||||
4CC6193A29DC777C006A86D1 /* RelayBootstrap.swift in Sources */,
|
4CC6193A29DC777C006A86D1 /* RelayBootstrap.swift in Sources */,
|
||||||
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
||||||
|
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */,
|
||||||
4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */,
|
4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */,
|
||||||
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */,
|
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */,
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
||||||
|
@ -10,7 +10,7 @@ import NaturalLanguage
|
|||||||
|
|
||||||
|
|
||||||
struct Translated: Equatable {
|
struct Translated: Equatable {
|
||||||
let artifacts: NoteArtifacts
|
let artifacts: NoteArtifactsSeparated
|
||||||
let language: String
|
let language: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ struct TranslateView: View {
|
|||||||
.translate_button_style()
|
.translate_button_style()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TranslatedView(lang: String?, artifacts: NoteArtifacts) -> some View {
|
func TranslatedView(lang: String?, artifacts: NoteArtifactsSeparated) -> some View {
|
||||||
return VStack(alignment: .leading) {
|
return VStack(alignment: .leading) {
|
||||||
let translatedFromLanguageString = String(format: NSLocalizedString("Translated from %@", comment: "Button to indicate that the note has been translated from a different language."), lang ?? "ja")
|
let translatedFromLanguageString = String(format: NSLocalizedString("Translated from %@", comment: "Button to indicate that the note has been translated from a different language."), lang ?? "ja")
|
||||||
Text(translatedFromLanguageString)
|
Text(translatedFromLanguageString)
|
||||||
|
@ -1147,6 +1147,27 @@ func create_in_app_event_zap_notification(profiles: Profiles, zap: Zap, locale:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func render_notification_content_preview(cache: EventCache, ev: NostrEvent, profiles: Profiles, privkey: String?) -> String {
|
||||||
|
|
||||||
|
let prefix_len = 50
|
||||||
|
let artifacts = cache.get_cache_data(ev.id).artifacts.artifacts ?? render_note_content(ev: ev, profiles: profiles, privkey: privkey)
|
||||||
|
|
||||||
|
// special case for longform events
|
||||||
|
if ev.known_kind == .longform {
|
||||||
|
let longform = LongformEvent(event: ev)
|
||||||
|
return longform.title ?? longform.summary ?? "Longform Event"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch artifacts {
|
||||||
|
case .parts:
|
||||||
|
// we should never hit this until we have more note types built out of parts
|
||||||
|
// since we handle this case above in known_kind == .longform
|
||||||
|
return String(ev.content.prefix(prefix_len))
|
||||||
|
|
||||||
|
case .separated(let artifacts):
|
||||||
|
return String(NSAttributedString(artifacts.content.attributed).string.prefix(prefix_len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
||||||
guard let type = ev.known_kind else {
|
guard let type = ev.known_kind else {
|
||||||
@ -1172,21 +1193,20 @@ func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
|||||||
if type == .text && damus_state.settings.mention_notification {
|
if type == .text && damus_state.settings.mention_notification {
|
||||||
let blocks = ev.blocks(damus_state.keypair.privkey).blocks
|
let blocks = ev.blocks(damus_state.keypair.privkey).blocks
|
||||||
for case .mention(let mention) in blocks where mention.ref.ref_id == damus_state.keypair.pubkey {
|
for case .mention(let mention) in blocks where mention.ref.ref_id == damus_state.keypair.pubkey {
|
||||||
let content = NSAttributedString(render_note_content(ev: ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
||||||
|
let notify = LocalNotification(type: .mention, event: ev, target: ev, content: content_preview)
|
||||||
let notify = LocalNotification(type: .mention, event: ev, target: ev, content: content)
|
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
||||||
}
|
}
|
||||||
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
||||||
let content = NSAttributedString(render_note_content(ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
||||||
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content)
|
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content_preview)
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
} else if type == .like && damus_state.settings.like_notification,
|
} else if type == .like && damus_state.settings.like_notification,
|
||||||
let evid = ev.referenced_ids.last?.ref_id,
|
let evid = ev.referenced_ids.last?.ref_id,
|
||||||
let liked_event = damus_state.events.lookup(evid)
|
let liked_event = damus_state.events.lookup(evid)
|
||||||
{
|
{
|
||||||
let content = NSAttributedString(render_note_content(ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
||||||
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content)
|
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content_preview)
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ struct PreloadPlan {
|
|||||||
let load_preview: Bool
|
let load_preview: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_preview(artifacts: NoteArtifacts) async -> Preview? {
|
func load_preview(artifacts: NoteArtifactsSeparated) async -> Preview? {
|
||||||
guard let link = artifacts.links.first else {
|
guard let link = artifacts.links.first else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -442,14 +442,18 @@ func preload_event(plan: PreloadPlan, state: DamusState) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if plan.load_preview {
|
if plan.load_preview, note_artifact_is_separated(kind: plan.event.known_kind) {
|
||||||
let arts = artifacts ?? render_note_content(ev: plan.event, profiles: profiles, privkey: our_keypair.privkey)
|
let arts = artifacts ?? render_note_content(ev: plan.event, profiles: profiles, privkey: our_keypair.privkey)
|
||||||
let preview = await load_preview(artifacts: arts)
|
|
||||||
DispatchQueue.main.async {
|
// only separated artifacts have previews
|
||||||
if let preview {
|
if case .separated(let sep) = arts {
|
||||||
plan.data.preview_model.state = .loaded(preview)
|
let preview = await load_preview(artifacts: sep)
|
||||||
} else {
|
DispatchQueue.main.async {
|
||||||
plan.data.preview_model.state = .loaded(.failed)
|
if let preview {
|
||||||
|
plan.data.preview_model.state = .loaded(preview)
|
||||||
|
} else {
|
||||||
|
plan.data.preview_model.state = .loaded(.failed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,32 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
func count_leading_hashes(_ str: String) -> Int {
|
||||||
|
var count = 0
|
||||||
|
for c in str {
|
||||||
|
if c == "#" {
|
||||||
|
count += 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_heading_title_size(count: Int) -> SwiftUI.Font {
|
||||||
|
if count >= 3 {
|
||||||
|
return Font.title3
|
||||||
|
} else if count >= 2 {
|
||||||
|
return Font.title2
|
||||||
|
} else if count >= 1 {
|
||||||
|
return Font.title
|
||||||
|
}
|
||||||
|
|
||||||
|
return Font.body
|
||||||
|
}
|
||||||
|
|
||||||
public struct Markdown {
|
public struct Markdown {
|
||||||
private var detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
|
private var detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
|
||||||
@ -19,6 +45,18 @@ public struct Markdown {
|
|||||||
public static func parse(content: String) -> AttributedString {
|
public static func parse(content: String) -> AttributedString {
|
||||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||||
|
|
||||||
|
guard content.utf8.count > 0 else {
|
||||||
|
return AttributedString(stringLiteral: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
let leading_hashes = count_leading_hashes(content)
|
||||||
|
if leading_hashes > 0 {
|
||||||
|
if var str = try? AttributedString(markdown: content) {
|
||||||
|
str.font = get_heading_title_size(count: leading_hashes)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: escape unintentional markdown
|
// TODO: escape unintentional markdown
|
||||||
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
||||||
|
@ -12,6 +12,7 @@ enum EventViewKind {
|
|||||||
case small
|
case small
|
||||||
case normal
|
case normal
|
||||||
case selected
|
case selected
|
||||||
|
case title
|
||||||
case subheadline
|
case subheadline
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +110,8 @@ func eventviewsize_to_font(_ size: EventViewKind) -> Font {
|
|||||||
return .body
|
return .body
|
||||||
case .selected:
|
case .selected:
|
||||||
return .custom("selected", size: 21.0)
|
return .custom("selected", size: 21.0)
|
||||||
|
case .title:
|
||||||
|
return .title
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return .subheadline
|
return .subheadline
|
||||||
}
|
}
|
||||||
@ -124,6 +127,8 @@ func eventviewsize_to_uifont(_ size: EventViewKind) -> UIFont {
|
|||||||
return .preferredFont(forTextStyle: .title2)
|
return .preferredFont(forTextStyle: .title2)
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return .preferredFont(forTextStyle: .subheadline)
|
return .preferredFont(forTextStyle: .subheadline)
|
||||||
|
case .title:
|
||||||
|
return .preferredFont(forTextStyle: .title1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,14 @@ struct EventBody: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
if event.known_kind == .longform {
|
||||||
|
let longform = LongformEvent.parse(from: event)
|
||||||
|
|
||||||
|
Text(longform.title ?? "Untitled")
|
||||||
|
.font(.title)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, options: options)
|
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, options: options)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ func eventview_pfp_size(_ size: EventViewKind) -> CGFloat {
|
|||||||
return PFP_SIZE
|
return PFP_SIZE
|
||||||
case .selected:
|
case .selected:
|
||||||
return PFP_SIZE
|
return PFP_SIZE
|
||||||
|
case .title:
|
||||||
|
return PFP_SIZE
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return PFP_SIZE * 0.5
|
return PFP_SIZE * 0.5
|
||||||
}
|
}
|
||||||
|
@ -24,22 +24,20 @@ struct LongformPreview: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
EventShell(state: state, event: event.event, options: [.no_mentions]) {
|
EventShell(state: state, event: event.event, options: [.no_mentions]) {
|
||||||
NavigationLink(destination: LongformView(event: event)) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
Text(event.title ?? "Untitled")
|
||||||
Text(event.title ?? "Untitled")
|
.font(.title)
|
||||||
.font(.title)
|
|
||||||
|
Text(event.summary ?? "")
|
||||||
Text(event.summary ?? "")
|
.foregroundColor(.gray)
|
||||||
.foregroundColor(.gray)
|
|
||||||
|
if case .loaded(let arts) = artifacts.state,
|
||||||
if case .loaded(let arts) = artifacts.state {
|
case .parts(let parts) = arts
|
||||||
Words(arts.words)
|
{
|
||||||
.font(.footnote)
|
Words(parts.words).font(.footnote)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,10 @@ struct LongformView: View {
|
|||||||
let event: LongformEvent
|
let event: LongformEvent
|
||||||
@ObservedObject var artifacts: NoteArtifactsModel
|
@ObservedObject var artifacts: NoteArtifactsModel
|
||||||
|
|
||||||
init(state: DamusState, event: LongformEvent) {
|
init(state: DamusState, event: LongformEvent, artifacts: NoteArtifactsModel? = nil) {
|
||||||
self.state = state
|
self.state = state
|
||||||
self.event = event
|
self.event = event
|
||||||
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(event.event.id).artifacts_model)
|
self._artifacts = ObservedObject(wrappedValue: artifacts ?? state.events.get_cache_data(event.event.id).artifacts_model)
|
||||||
}
|
}
|
||||||
|
|
||||||
var options: EventViewOptions {
|
var options: EventViewOptions {
|
||||||
@ -53,17 +53,10 @@ struct LongformView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
EventShell(state: state, event: event.event, options: options) {
|
EventShell(state: state, event: event.event, options: options) {
|
||||||
|
|
||||||
Content
|
VStack {
|
||||||
}
|
SelectableText(attributedString: AttributedString(stringLiteral: event.title ?? "Untitled"), size: .title)
|
||||||
}
|
|
||||||
|
NoteContentView(damus_state: state, event: event.event, show_images: true, size: .selected, options: options)
|
||||||
var Content: some View {
|
|
||||||
Group {
|
|
||||||
if case .loaded(let artifacts) = artifacts.state {
|
|
||||||
SelectableText(attributedString: artifacts.content.attributed, size: .selected)
|
|
||||||
.padding(.horizontal)
|
|
||||||
} else {
|
|
||||||
Text("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,6 +78,10 @@ let test_longform_event = LongformEvent.parse(from:
|
|||||||
|
|
||||||
struct LongformView_Previews: PreviewProvider {
|
struct LongformView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
LongformView(state: test_damus_state(), event: test_longform_event)
|
let st = test_damus_state()
|
||||||
|
let artifacts = render_note_content(ev: test_longform_event.event, profiles: st.profiles, privkey: nil)
|
||||||
|
|
||||||
|
let model = NoteArtifactsModel(state: .loaded(artifacts))
|
||||||
|
LongformView(state: st, event: test_longform_event, artifacts: model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
damus/Views/Images/MediaView.swift
Normal file
50
damus/Views/Images/MediaView.swift
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// MediaView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-06-05.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct MediaView: View {
|
||||||
|
let geo: GeometryProxy
|
||||||
|
let url: MediaUrl
|
||||||
|
let index: Int
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
switch url {
|
||||||
|
case .image(let url):
|
||||||
|
Img(geo: geo, url: url, index: index)
|
||||||
|
.onTapGesture {
|
||||||
|
open_sheet = true
|
||||||
|
}
|
||||||
|
case .video(let url):
|
||||||
|
DamusVideoPlayer(url: url, model: video_model(url), video_size: $video_size)
|
||||||
|
.onChange(of: video_size) { size in
|
||||||
|
guard let size else { return }
|
||||||
|
|
||||||
|
let fill = ImageFill.calculate_image_fill(geo_size: geo.size, img_size: size, maxHeight: maxHeight, fillHeight: fillHeight)
|
||||||
|
|
||||||
|
print("video_size changed \(size)")
|
||||||
|
if self.image_fill == nil {
|
||||||
|
print("video_size firstImageHeight \(fill.height)")
|
||||||
|
firstImageHeight = fill.height
|
||||||
|
state.events.get_cache_data(evid).media_metadata_model.fill = fill
|
||||||
|
}
|
||||||
|
|
||||||
|
self.image_fill = fill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MediaView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MediaView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
@ -33,8 +33,8 @@ struct NoteContentView: View {
|
|||||||
@ObservedObject var artifacts_model: NoteArtifactsModel
|
@ObservedObject var artifacts_model: NoteArtifactsModel
|
||||||
@ObservedObject var preview_model: PreviewModel
|
@ObservedObject var preview_model: PreviewModel
|
||||||
|
|
||||||
var artifacts: NoteArtifacts {
|
var note_artifacts: NoteArtifacts {
|
||||||
return self.artifacts_model.state.artifacts ?? .just_content(event.get_content(damus_state.keypair.privkey))
|
return self.artifacts_model.state.artifacts ?? .separated(.just_content(event.get_content(damus_state.keypair.privkey)))
|
||||||
}
|
}
|
||||||
|
|
||||||
init(damus_state: DamusState, event: NostrEvent, show_images: Bool, size: EventViewKind, options: EventViewOptions) {
|
init(damus_state: DamusState, event: NostrEvent, show_images: Bool, size: EventViewKind, options: EventViewOptions) {
|
||||||
@ -67,27 +67,27 @@ struct NoteContentView: View {
|
|||||||
return LinkViewRepresentable(meta: .linkmeta(cached))
|
return LinkViewRepresentable(meta: .linkmeta(cached))
|
||||||
}
|
}
|
||||||
|
|
||||||
var truncatedText: some View {
|
func truncatedText(content: CompatibleText) -> some View {
|
||||||
Group {
|
Group {
|
||||||
if truncate {
|
if truncate {
|
||||||
TruncatedText(text: artifacts.content)
|
TruncatedText(text: content)
|
||||||
.font(eventviewsize_to_font(size))
|
.font(eventviewsize_to_font(size))
|
||||||
} else {
|
} else {
|
||||||
artifacts.content.text
|
content.text
|
||||||
.font(eventviewsize_to_font(size))
|
.font(eventviewsize_to_font(size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var invoicesView: some View {
|
func invoicesView(invoices: [Invoice]) -> some View {
|
||||||
InvoicesView(our_pubkey: damus_state.keypair.pubkey, invoices: artifacts.invoices, settings: damus_state.settings)
|
InvoicesView(our_pubkey: damus_state.keypair.pubkey, invoices: invoices, settings: damus_state.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
var translateView: some View {
|
var translateView: some View {
|
||||||
TranslateView(damus_state: damus_state, event: event, size: self.size)
|
TranslateView(damus_state: damus_state, event: event, size: self.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
var previewView: some View {
|
func previewView(links: [URL]) -> some View {
|
||||||
Group {
|
Group {
|
||||||
if let preview = self.preview, show_images {
|
if let preview = self.preview, show_images {
|
||||||
if let preview_height {
|
if let preview_height {
|
||||||
@ -96,14 +96,14 @@ struct NoteContentView: View {
|
|||||||
} else {
|
} else {
|
||||||
preview
|
preview
|
||||||
}
|
}
|
||||||
} else if let link = artifacts.links.first {
|
} else if let link = links.first {
|
||||||
LinkViewRepresentable(meta: .url(link))
|
LinkViewRepresentable(meta: .url(link))
|
||||||
.frame(height: 50)
|
.frame(height: 50)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var MainContent: some View {
|
func MainContent(artifacts: NoteArtifactsSeparated) -> some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if size == .selected {
|
if size == .selected {
|
||||||
if with_padding {
|
if with_padding {
|
||||||
@ -114,10 +114,10 @@ struct NoteContentView: View {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if with_padding {
|
if with_padding {
|
||||||
truncatedText
|
truncatedText(content: artifacts.content)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
} else {
|
} else {
|
||||||
truncatedText
|
truncatedText(content: artifacts.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,17 +143,17 @@ struct NoteContentView: View {
|
|||||||
|
|
||||||
if artifacts.invoices.count > 0 {
|
if artifacts.invoices.count > 0 {
|
||||||
if with_padding {
|
if with_padding {
|
||||||
invoicesView
|
invoicesView(invoices: artifacts.invoices)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
} else {
|
} else {
|
||||||
invoicesView
|
invoicesView(invoices: artifacts.invoices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if with_padding {
|
if with_padding {
|
||||||
previewView.padding(.horizontal)
|
previewView(links: artifacts.links).padding(.horizontal)
|
||||||
} else {
|
} else {
|
||||||
previewView
|
previewView(links: artifacts.links)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -183,8 +183,38 @@ struct NoteContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func artifactPartsView(_ parts: [ArtifactPart]) -> some View {
|
||||||
|
|
||||||
|
LazyVStack {
|
||||||
|
ForEach(parts.indices, id: \.self) { ind in
|
||||||
|
let part = parts[ind]
|
||||||
|
switch part {
|
||||||
|
case .text(let txt):
|
||||||
|
txt
|
||||||
|
.padding(.horizontal)
|
||||||
|
case .invoice(let inv):
|
||||||
|
InvoiceView(our_pubkey: damus_state.pubkey, invoice: inv, settings: damus_state.settings)
|
||||||
|
.padding(.horizontal)
|
||||||
|
case .media(let media):
|
||||||
|
Text("media \(media.url.absoluteString)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ArtifactContent: some View {
|
||||||
|
Group {
|
||||||
|
switch self.note_artifacts {
|
||||||
|
case .parts(let parts):
|
||||||
|
artifactPartsView(parts.parts)
|
||||||
|
case .separated(let separated):
|
||||||
|
MainContent(artifacts: separated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
MainContent
|
ArtifactContent
|
||||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||||
let profile = notif.object as! ProfileUpdate
|
let profile = notif.object as! ProfileUpdate
|
||||||
let blocks = event.blocks(damus_state.keypair.privkey)
|
let blocks = event.blocks(damus_state.keypair.privkey)
|
||||||
@ -255,8 +285,53 @@ struct NoteContentView_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NoteArtifacts: Equatable {
|
|
||||||
static func == (lhs: NoteArtifacts, rhs: NoteArtifacts) -> Bool {
|
enum NoteArtifacts {
|
||||||
|
case separated(NoteArtifactsSeparated)
|
||||||
|
case parts(NoteArtifactsParts)
|
||||||
|
|
||||||
|
var images: [URL] {
|
||||||
|
switch self {
|
||||||
|
case .separated(let arts):
|
||||||
|
return arts.images
|
||||||
|
case .parts(let parts):
|
||||||
|
return parts.parts.reduce(into: [URL]()) { acc, part in
|
||||||
|
guard case .media(let m) = part,
|
||||||
|
case .image(let url) = m
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
acc.append(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ArtifactPart {
|
||||||
|
case text(Text)
|
||||||
|
case media(MediaUrl)
|
||||||
|
case invoice(Invoice)
|
||||||
|
|
||||||
|
var is_text: Bool {
|
||||||
|
switch self {
|
||||||
|
case .text: return true
|
||||||
|
case .media: return false
|
||||||
|
case .invoice: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoteArtifactsParts {
|
||||||
|
var parts: [ArtifactPart]
|
||||||
|
var words: Int
|
||||||
|
|
||||||
|
init(parts: [ArtifactPart], words: Int) {
|
||||||
|
self.parts = parts
|
||||||
|
self.words = words
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NoteArtifactsSeparated: Equatable {
|
||||||
|
static func == (lhs: NoteArtifactsSeparated, rhs: NoteArtifactsSeparated) -> Bool {
|
||||||
return lhs.content == rhs.content
|
return lhs.content == rhs.content
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,9 +352,9 @@ struct NoteArtifacts: Equatable {
|
|||||||
return urls.compactMap { url in url.is_link }
|
return urls.compactMap { url in url.is_link }
|
||||||
}
|
}
|
||||||
|
|
||||||
static func just_content(_ content: String) -> NoteArtifacts {
|
static func just_content(_ content: String) -> NoteArtifactsSeparated {
|
||||||
let txt = CompatibleText(attributed: AttributedString(stringLiteral: content))
|
let txt = CompatibleText(attributed: AttributedString(stringLiteral: content))
|
||||||
return NoteArtifacts(content: txt, words: 0, urls: [], invoices: [])
|
return NoteArtifactsSeparated(content: txt, words: 0, urls: [], invoices: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,49 +383,93 @@ enum NoteArtifactState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func note_artifact_is_separated(kind: NostrKind?) -> Bool {
|
||||||
|
return kind != .longform
|
||||||
|
}
|
||||||
|
|
||||||
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
|
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
|
||||||
let blocks = ev.blocks(privkey)
|
let blocks = ev.blocks(privkey)
|
||||||
|
|
||||||
return render_blocks(blocks: blocks, profiles: profiles)
|
if ev.known_kind == .longform {
|
||||||
|
return .parts(render_blocks_parted(blocks: blocks, profiles: profiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .separated(render_blocks(blocks: blocks, profiles: profiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
func render_blocks_longform(blocks bs: Blocks) -> NoteArtifacts {
|
fileprivate func artifact_part_last_text_ind(parts: [ArtifactPart]) -> (Int, Text)? {
|
||||||
var invoices: [Invoice] = []
|
let ind = parts.count - 1
|
||||||
var urls: [UrlType] = []
|
if ind < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard case .text(let txt) = parts[safe: ind] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ind, txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func render_blocks_parted(blocks bs: Blocks, profiles: Profiles) -> NoteArtifactsParts {
|
||||||
let blocks = bs.blocks
|
let blocks = bs.blocks
|
||||||
|
|
||||||
var ind: Int = -1
|
let new_parts = NoteArtifactsParts(parts: [], words: bs.words)
|
||||||
let txt: CompatibleText = blocks.reduce(CompatibleText()) { str, block in
|
|
||||||
ind = ind + 1
|
return blocks.reduce(into: new_parts) { parts, block in
|
||||||
|
|
||||||
switch block {
|
switch block {
|
||||||
case .mention(let m):
|
case .mention(let m):
|
||||||
return str + mention_str(m, profiles: profiles)
|
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||||
case .text(let txt):
|
parts.parts.append(.text(mention_str(m, profiles: profiles).text))
|
||||||
return str + reduce_text_block(blocks: blocks, ind: ind, txt: txt, one_note_ref: false)
|
return
|
||||||
|
}
|
||||||
|
parts.parts[last_ind] = .text(txt + mention_str(m, profiles: profiles).text)
|
||||||
|
|
||||||
|
case .text(let str):
|
||||||
|
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||||
|
// TODO: (jb55) md is longform specific
|
||||||
|
let md = Markdown.parse(content: str)
|
||||||
|
parts.parts.append(.text(Text(md)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.parts[last_ind] = .text(txt + Text(str))
|
||||||
|
|
||||||
case .relay(let relay):
|
case .relay(let relay):
|
||||||
return str + CompatibleText(stringLiteral: relay)
|
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||||
|
parts.parts.append(.text(Text(relay)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.parts[last_ind] = .text(txt + Text(relay))
|
||||||
|
|
||||||
case .hashtag(let htag):
|
case .hashtag(let htag):
|
||||||
return str + hashtag_str(htag)
|
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||||
|
parts.parts.append(.text(hashtag_str(htag).text))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.parts[last_ind] = .text(txt + hashtag_str(htag).text)
|
||||||
|
|
||||||
case .invoice(let invoice):
|
case .invoice(let invoice):
|
||||||
invoices.append(invoice)
|
parts.parts.append(.invoice(invoice))
|
||||||
return str
|
return
|
||||||
|
|
||||||
case .url(let url):
|
case .url(let url):
|
||||||
let url_type = classify_url(url)
|
let url_type = classify_url(url)
|
||||||
switch url_type {
|
switch url_type {
|
||||||
case .media:
|
case .media(let media_url):
|
||||||
urls.append(url_type)
|
parts.parts.append(.media(media_url))
|
||||||
return str
|
|
||||||
case .link(let url):
|
case .link(let url):
|
||||||
urls.append(url_type)
|
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||||
return str + url_str(url)
|
parts.parts.append(.text(url_str(url).text))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.parts[last_ind] = .text(txt + url_str(url).text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func reduce_text_block(blocks: [Block], ind: Int, txt: String, one_note_ref: Bool) -> CompatibleText {
|
||||||
@ -373,7 +492,7 @@ func reduce_text_block(blocks: [Block], ind: Int, txt: String, one_note_ref: Boo
|
|||||||
return CompatibleText(stringLiteral: trimmed)
|
return CompatibleText(stringLiteral: trimmed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
|
func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifactsSeparated {
|
||||||
var invoices: [Invoice] = []
|
var invoices: [Invoice] = []
|
||||||
var urls: [UrlType] = []
|
var urls: [UrlType] = []
|
||||||
let blocks = bs.blocks
|
let blocks = bs.blocks
|
||||||
@ -416,7 +535,7 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NoteArtifacts(content: txt, words: bs.words, urls: urls, invoices: invoices)
|
return NoteArtifactsSeparated(content: txt, words: bs.words, urls: urls, invoices: invoices)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUrl {
|
enum MediaUrl {
|
||||||
|
@ -20,7 +20,7 @@ final class InvoiceTests: XCTestCase {
|
|||||||
|
|
||||||
func testParseAnyAmountInvoice() throws {
|
func testParseAnyAmountInvoice() throws {
|
||||||
let invstr = "LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C"
|
let invstr = "LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C"
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
@ -38,7 +38,7 @@ final class InvoiceTests: XCTestCase {
|
|||||||
let invstr = """
|
let invstr = """
|
||||||
LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C hi there
|
LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C hi there
|
||||||
"""
|
"""
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 2)
|
XCTAssertEqual(parsed.count, 2)
|
||||||
@ -54,7 +54,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
|
|||||||
|
|
||||||
func testParseInvoiceUpper() throws {
|
func testParseInvoiceUpper() throws {
|
||||||
let invstr = "LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
|
let invstr = "LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
@ -70,7 +70,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
|
|||||||
|
|
||||||
func testParseInvoiceWithPrefix() throws {
|
func testParseInvoiceWithPrefix() throws {
|
||||||
let invstr = "lightning:lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
|
let invstr = "lightning:lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
@ -79,7 +79,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
|
|||||||
|
|
||||||
func testParseInvoiceWithPrefixCapitalized() throws {
|
func testParseInvoiceWithPrefixCapitalized() throws {
|
||||||
let invstr = "LIGHTNING:LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
|
let invstr = "LIGHTNING:LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
@ -88,7 +88,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
|
|||||||
|
|
||||||
func testParseInvoice() throws {
|
func testParseInvoice() throws {
|
||||||
let invstr = "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
|
let invstr = "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
|
||||||
let parsed = parse_mentions(content: invstr, tags: [])
|
let parsed = parse_mentions(content: invstr, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
|
@ -33,6 +33,18 @@ class MarkdownTests: XCTestCase {
|
|||||||
let expected = try AttributedString(markdown: "prologue [damus.io](https://damus.io) epilogue", options: md_opts)
|
let expected = try AttributedString(markdown: "prologue [damus.io](https://damus.io) epilogue", options: md_opts)
|
||||||
XCTAssertEqual(md, expected)
|
XCTAssertEqual(md, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_longform_rendering() throws {
|
||||||
|
let st = test_damus_state()
|
||||||
|
let artifacts = render_note_content(ev: test_longform_event.event, profiles: st.profiles, privkey: st.keypair.privkey)
|
||||||
|
|
||||||
|
switch artifacts {
|
||||||
|
case .separated:
|
||||||
|
XCTAssert(false)
|
||||||
|
case .parts(let parts):
|
||||||
|
XCTAssertEqual(parts.parts.count, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func test_convert_links() throws {
|
func test_convert_links() throws {
|
||||||
let helper = Markdown()
|
let helper = Markdown()
|
||||||
|
@ -19,7 +19,7 @@ final class NIP19Tests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_parse_nprofile() throws {
|
func test_parse_nprofile() throws {
|
||||||
let res = parse_mentions(content: "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", tags: [])
|
let res = parse_mentions(content: "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", tags: []).blocks
|
||||||
XCTAssertEqual(res.count, 1)
|
XCTAssertEqual(res.count, 1)
|
||||||
let expected_ref = ReferencedId(ref_id: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", relay_id: "wss://r.x.com", key: "p")
|
let expected_ref = ReferencedId(ref_id: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", relay_id: "wss://r.x.com", key: "p")
|
||||||
let expected_mention = Mention(index: nil, type: .pubkey, ref: expected_ref)
|
let expected_mention = Mention(index: nil, type: .pubkey, ref: expected_ref)
|
||||||
@ -27,7 +27,7 @@ final class NIP19Tests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_parse_npub() throws {
|
func test_parse_npub() throws {
|
||||||
let res = parse_mentions(content: "nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg ", tags: [])
|
let res = parse_mentions(content: "nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg ", tags: []).blocks
|
||||||
XCTAssertEqual(res.count, 2)
|
XCTAssertEqual(res.count, 2)
|
||||||
let expected_ref = ReferencedId(ref_id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", relay_id: nil, key: "p")
|
let expected_ref = ReferencedId(ref_id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", relay_id: nil, key: "p")
|
||||||
let expected_mention = Mention(index: nil, type: .pubkey, ref: expected_ref)
|
let expected_mention = Mention(index: nil, type: .pubkey, ref: expected_ref)
|
||||||
@ -35,7 +35,7 @@ final class NIP19Tests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_parse_note() throws {
|
func test_parse_note() throws {
|
||||||
let res = parse_mentions(content: " nostr:note1s4p70596lv50x0zftuses32t6ck8x6wgd4edwacyetfxwns2jtysux7vep", tags: [])
|
let res = parse_mentions(content: " nostr:note1s4p70596lv50x0zftuses32t6ck8x6wgd4edwacyetfxwns2jtysux7vep", tags: []).blocks
|
||||||
XCTAssertEqual(res.count, 2)
|
XCTAssertEqual(res.count, 2)
|
||||||
let expected_ref = ReferencedId(ref_id: "8543e7d0bafb28f33c495f2198454bd62c7369c86d72d77704cad2674e0a92c9", relay_id: nil, key: "e")
|
let expected_ref = ReferencedId(ref_id: "8543e7d0bafb28f33c495f2198454bd62c7369c86d72d77704cad2674e0a92c9", relay_id: nil, key: "e")
|
||||||
let expected_mention = Mention(index: nil, type: .event, ref: expected_ref)
|
let expected_mention = Mention(index: nil, type: .event, ref: expected_ref)
|
||||||
@ -43,7 +43,7 @@ final class NIP19Tests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_mention_with_adjacent() throws {
|
func test_mention_with_adjacent() throws {
|
||||||
let res = parse_mentions(content: " nostr:note1s4p70596lv50x0zftuses32t6ck8x6wgd4edwacyetfxwns2jtysux7vep?", tags: [])
|
let res = parse_mentions(content: " nostr:note1s4p70596lv50x0zftuses32t6ck8x6wgd4edwacyetfxwns2jtysux7vep?", tags: []).blocks
|
||||||
XCTAssertEqual(res.count, 3)
|
XCTAssertEqual(res.count, 3)
|
||||||
let expected_ref = ReferencedId(ref_id: "8543e7d0bafb28f33c495f2198454bd62c7369c86d72d77704cad2674e0a92c9", relay_id: nil, key: "e")
|
let expected_ref = ReferencedId(ref_id: "8543e7d0bafb28f33c495f2198454bd62c7369c86d72d77704cad2674e0a92c9", relay_id: nil, key: "e")
|
||||||
let expected_mention = Mention(index: nil, type: .event, ref: expected_ref)
|
let expected_mention = Mention(index: nil, type: .event, ref: expected_ref)
|
||||||
|
@ -21,7 +21,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testMentionIsntReply() throws {
|
func testMentionIsntReply() throws {
|
||||||
let content = "this is #[0] a mention"
|
let content = "this is #[0] a mention"
|
||||||
let tags = [["e", "event_id"]]
|
let tags = [["e", "event_id"]]
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
||||||
|
|
||||||
XCTAssertEqual(event_refs.count, 1)
|
XCTAssertEqual(event_refs.count, 1)
|
||||||
@ -96,7 +96,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testRootReplyWithMention() throws {
|
func testRootReplyWithMention() throws {
|
||||||
let content = "this is #[1] a mention"
|
let content = "this is #[1] a mention"
|
||||||
let tags = [["e", "thread_id"], ["e", "mentioned_id"]]
|
let tags = [["e", "thread_id"], ["e", "mentioned_id"]]
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
||||||
|
|
||||||
XCTAssertEqual(event_refs.count, 2)
|
XCTAssertEqual(event_refs.count, 2)
|
||||||
@ -114,7 +114,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testEmptyMention() throws {
|
func testEmptyMention() throws {
|
||||||
let content = "this is some & content"
|
let content = "this is some & content"
|
||||||
let tags: [[String]] = []
|
let tags: [[String]] = []
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let post_blocks = parse_post_blocks(content: content)
|
let post_blocks = parse_post_blocks(content: content)
|
||||||
let post_tags = make_post_tags(post_blocks: post_blocks, tags: tags, silent_mentions: false)
|
let post_tags = make_post_tags(post_blocks: post_blocks, tags: tags, silent_mentions: false)
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
||||||
@ -148,7 +148,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testManyMentions() throws {
|
func testManyMentions() throws {
|
||||||
let content = "#[10]"
|
let content = "#[10]"
|
||||||
let tags: [[String]] = [[],[],[],[],[],[],[],[],[],[],["p", "3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692"]]
|
let tags: [[String]] = [[],[],[],[],[],[],[],[],[],[],["p", "3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692"]]
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let mentions = blocks.filter { $0.is_mention }
|
let mentions = blocks.filter { $0.is_mention }
|
||||||
XCTAssertEqual(mentions.count, 1)
|
XCTAssertEqual(mentions.count, 1)
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testThreadedReply() throws {
|
func testThreadedReply() throws {
|
||||||
let content = "this is some content"
|
let content = "this is some content"
|
||||||
let tags = [["e", "thread_id"], ["e", "reply_id"]]
|
let tags = [["e", "thread_id"], ["e", "reply_id"]]
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
||||||
|
|
||||||
XCTAssertEqual(event_refs.count, 2)
|
XCTAssertEqual(event_refs.count, 2)
|
||||||
@ -172,7 +172,7 @@ class ReplyTests: XCTestCase {
|
|||||||
func testRootReply() throws {
|
func testRootReply() throws {
|
||||||
let content = "this is a reply"
|
let content = "this is a reply"
|
||||||
let tags = [["e", "thread_id"]]
|
let tags = [["e", "thread_id"]]
|
||||||
let blocks = parse_mentions(content: content, tags: tags)
|
let blocks = parse_mentions(content: content, tags: tags).blocks
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
let event_refs = interpret_event_refs(blocks: blocks, tags: tags)
|
||||||
|
|
||||||
XCTAssertEqual(event_refs.count, 1)
|
XCTAssertEqual(event_refs.count, 1)
|
||||||
@ -186,14 +186,14 @@ class ReplyTests: XCTestCase {
|
|||||||
|
|
||||||
func testNoReply() throws {
|
func testNoReply() throws {
|
||||||
let content = "this is a #[0] reply"
|
let content = "this is a #[0] reply"
|
||||||
let blocks = parse_mentions(content: content, tags: [])
|
let blocks = parse_mentions(content: content, tags: []).blocks
|
||||||
let event_refs = interpret_event_refs(blocks: blocks, tags: [])
|
let event_refs = interpret_event_refs(blocks: blocks, tags: [])
|
||||||
|
|
||||||
XCTAssertEqual(event_refs.count, 0)
|
XCTAssertEqual(event_refs.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testParseMention() throws {
|
func testParseMention() throws {
|
||||||
let parsed = parse_mentions(content: "this is #[0] a mention", tags: [["e", "event_id"]])
|
let parsed = parse_mentions(content: "this is #[0] a mention", tags: [["e", "event_id"]]).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -522,7 +522,7 @@ class ReplyTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseInvalidMention() throws {
|
func testParseInvalidMention() throws {
|
||||||
let parsed = parse_mentions(content: "this is #[0] a mention", tags: [])
|
let parsed = parse_mentions(content: "this is #[0] a mention", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
|
@ -71,7 +71,7 @@ class damusTests: XCTestCase {
|
|||||||
[my website](https://jb55.com)
|
[my website](https://jb55.com)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let parsed = parse_mentions(content: md, tags: [])
|
let parsed = parse_mentions(content: md, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
@ -79,7 +79,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseUrlUpper() {
|
func testParseUrlUpper() {
|
||||||
let parsed = parse_mentions(content: "a HTTPS://jb55.COM b", tags: [])
|
let parsed = parse_mentions(content: "a HTTPS://jb55.COM b", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -114,7 +114,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseUrl() {
|
func testParseUrl() {
|
||||||
let parsed = parse_mentions(content: "a https://jb55.com b", tags: [])
|
let parsed = parse_mentions(content: "a https://jb55.com b", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -122,7 +122,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseUrlEnd() {
|
func testParseUrlEnd() {
|
||||||
let parsed = parse_mentions(content: "a https://jb55.com", tags: [])
|
let parsed = parse_mentions(content: "a https://jb55.com", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 2)
|
XCTAssertEqual(parsed.count, 2)
|
||||||
@ -131,7 +131,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseUrlStart() {
|
func testParseUrlStart() {
|
||||||
let parsed = parse_mentions(content: "https://jb55.com br", tags: [])
|
let parsed = parse_mentions(content: "https://jb55.com br", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 2)
|
XCTAssertEqual(parsed.count, 2)
|
||||||
@ -141,7 +141,7 @@ class damusTests: XCTestCase {
|
|||||||
|
|
||||||
func testNoParseUrlWithOnlyWhitespace() {
|
func testNoParseUrlWithOnlyWhitespace() {
|
||||||
let testString = "https:// "
|
let testString = "https:// "
|
||||||
let parsed = parse_mentions(content: testString, tags: [])
|
let parsed = parse_mentions(content: testString, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed[0].is_text, testString)
|
XCTAssertEqual(parsed[0].is_text, testString)
|
||||||
@ -149,14 +149,14 @@ class damusTests: XCTestCase {
|
|||||||
|
|
||||||
func testNoParseUrlTrailingCharacters() {
|
func testNoParseUrlTrailingCharacters() {
|
||||||
let testString = "https://foo.bar, "
|
let testString = "https://foo.bar, "
|
||||||
let parsed = parse_mentions(content: testString, tags: [])
|
let parsed = parse_mentions(content: testString, tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed[0].is_url?.absoluteString, "https://foo.bar")
|
XCTAssertEqual(parsed[0].is_url?.absoluteString, "https://foo.bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testParseMentionBlank() {
|
func testParseMentionBlank() {
|
||||||
let parsed = parse_mentions(content: "", tags: [["e", "event_id"]])
|
let parsed = parse_mentions(content: "", tags: [["e", "event_id"]]).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 0)
|
XCTAssertEqual(parsed.count, 0)
|
||||||
@ -178,7 +178,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseHashtag() {
|
func testParseHashtag() {
|
||||||
let parsed = parse_mentions(content: "some hashtag #bitcoin derp", tags: [])
|
let parsed = parse_mentions(content: "some hashtag #bitcoin derp", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -188,7 +188,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testHashtagWithComma() {
|
func testHashtagWithComma() {
|
||||||
let parsed = parse_mentions(content: "some hashtag #bitcoin, cool", tags: [])
|
let parsed = parse_mentions(content: "some hashtag #bitcoin, cool", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -198,7 +198,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testHashtagWithEmoji() {
|
func testHashtagWithEmoji() {
|
||||||
let parsed = parse_mentions(content: "some hashtag #bitcoin☕️ cool", tags: [])
|
let parsed = parse_mentions(content: "some hashtag #bitcoin☕️ cool", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 3)
|
XCTAssertEqual(parsed.count, 3)
|
||||||
@ -208,7 +208,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseHashtagEnd() {
|
func testParseHashtagEnd() {
|
||||||
let parsed = parse_mentions(content: "some hashtag #bitcoin", tags: [])
|
let parsed = parse_mentions(content: "some hashtag #bitcoin", tags: []).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 2)
|
XCTAssertEqual(parsed.count, 2)
|
||||||
@ -217,7 +217,7 @@ class damusTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testParseMentionOnlyText() {
|
func testParseMentionOnlyText() {
|
||||||
let parsed = parse_mentions(content: "there is no mention here", tags: [["e", "event_id"]])
|
let parsed = parse_mentions(content: "there is no mention here", tags: [["e", "event_id"]]).blocks
|
||||||
|
|
||||||
XCTAssertNotNil(parsed)
|
XCTAssertNotNil(parsed)
|
||||||
XCTAssertEqual(parsed.count, 1)
|
XCTAssertEqual(parsed.count, 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user