Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix note rendering for those that contain previewable items or leading and trailing whitespaces #2890

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion damus/Components/TranslateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func translate_note(profiles: Profiles, keypair: Keypair, event: NostrEvent, set

// Render translated note
let translated_blocks = parse_note_content(content: .content(translated_note, event.tags))
let artifacts = render_blocks(blocks: translated_blocks, profiles: profiles)
let artifacts = render_blocks(blocks: translated_blocks, profiles: profiles, can_hide_last_previewable_refs: true)

// and cache it
return .translated(Translated(artifacts: artifacts, language: note_lang))
Expand Down
116 changes: 66 additions & 50 deletions damus/Models/NoteContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,85 +73,101 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, keypair: Keypair) -
return .longform(LongformContent(ev.content))
}

return .separated(render_blocks(blocks: blocks, profiles: profiles))
return .separated(render_blocks(blocks: blocks, profiles: profiles, can_hide_last_previewable_refs: true))
}

func render_blocks(blocks bs: Blocks, profiles: Profiles) -> NoteArtifactsSeparated {
func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewable_refs: Bool = false) -> NoteArtifactsSeparated {
var invoices: [Invoice] = []
var urls: [UrlType] = []
let blocks = bs.blocks

let one_note_ref = blocks
.filter({
if case .mention(let mention) = $0,
case .note = mention.ref {
return true
}
else {
return false

// Search backwards until we find the beginning index of the chain of previewables that reach the end of the content.
var hide_text_index = blocks.endIndex
if can_hide_last_previewable_refs {
for (i, block) in blocks.enumerated().reversed() {
if block.is_previewable {
hide_text_index = i
} else if case .text(let txt) = block, txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
hide_text_index = i
} else {
break
}
})
.count == 1
}
}

var ind: Int = -1
let txt: CompatibleText = blocks.reduce(CompatibleText()) { str, block in
ind = ind + 1


// Add the rendered previewable blocks to their type-specific lists.
switch block {
case .mention(let m):
if case .note = m.ref, one_note_ref {
case .invoice(let invoice):
invoices.append(invoice)
case .url(let url):
let url_type = classify_url(url)
urls.append(url_type)
default:
break
}

if can_hide_last_previewable_refs {
// If there are previewable blocks that occur before the consecutive sequence of them at the end of the content,
// we should not hide the text representation of any previewable block to avoid altering the format of the note.
if block.is_previewable && ind < hide_text_index {
hide_text_index = blocks.endIndex
}

// No need to show the text representation of the block if the only previewables are the sequence of them
// found at the end of the content.
// This is to save unnecessary use of screen space.
if ind >= hide_text_index {
return str
}
}

switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + CompatibleText(stringLiteral: reduce_text_block(blocks: blocks, ind: ind, txt: txt, one_note_ref: one_note_ref))

return str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: hide_text_index, txt: txt))
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
return str + invoice_str(invoice)
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 str + url_str(url)
}
}

return NoteArtifactsSeparated(content: txt, words: bs.words, urls: urls, invoices: invoices)
}

func reduce_text_block(blocks: [Block], ind: Int, txt: String, one_note_ref: Bool) -> String {
func reduce_text_block(ind: Int, hide_text_index: Int, txt: String) -> String {
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)

// Trim leading whitespaces.
if ind == 0 {
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,
case .note = m.ref,
one_note_ref {
trimmed = trim_suffix(trimmed)
}

// Trim trailing whitespaces if the following blocks will be hidden or if this is the last block.
if ind == hide_text_index - 1 {
trimmed = trim_suffix(trimmed)
}

return trimmed
}

func invoice_str(_ invoice: Invoice) -> CompatibleText {
var attributedString = AttributedString(stringLiteral: abbrev_identifier(invoice.string))
attributedString.link = URL(string: "damus:lightning:\(invoice.string)")
attributedString.foregroundColor = DamusColors.purple

return CompatibleText(attributed: attributedString)
}

func url_str(_ url: URL) -> CompatibleText {
var attributedString = AttributedString(stringLiteral: url.absoluteString)
attributedString.link = url
Expand Down Expand Up @@ -194,11 +210,11 @@ func mention_str(_ m: Mention<MentionRef>, profiles: Profiles) -> CompatibleText
let display_str: String = {
switch m.ref {
case .pubkey(let pk): return getDisplayName(pk: pk, profiles: profiles)
case .note: return abbrev_pubkey(bech32String)
case .nevent: return abbrev_pubkey(bech32String)
case .note: return abbrev_identifier(bech32String)
case .nevent: return abbrev_identifier(bech32String)
case .nprofile(let nprofile): return getDisplayName(pk: nprofile.author, profiles: profiles)
case .nrelay(let url): return url
case .naddr: return abbrev_pubkey(bech32String)
case .naddr: return abbrev_identifier(bech32String)
}
}()

Expand Down
18 changes: 18 additions & 0 deletions damus/Models/URLHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ struct DamusURLHandler {
return .route(.Script(script: model))
case .purple(let purple_url):
return await damus_state.purple.handle(purple_url: purple_url)
case .invoice(let invoice):
if damus_state.settings.show_wallet_selector {
return .sheet(.select_wallet(invoice: invoice.string))
} else {
do {
try open_with_wallet(wallet: damus_state.settings.default_wallet.model, invoice: invoice.string)
return .no_action
}
catch {
return .sheet(.select_wallet(invoice: invoice.string))
}
}
case nil:
break
}
Expand Down Expand Up @@ -91,6 +103,11 @@ struct DamusURLHandler {
return .filter(filt)
case .script(let script):
return .script(script)
case .invoice(let bolt11):
if let invoice = decode_bolt11(bolt11) {
return .invoice(invoice)
}
return nil
}
return nil
}
Expand All @@ -103,5 +120,6 @@ struct DamusURLHandler {
case wallet_connect(WalletConnectURL)
case script([UInt8])
case purple(DamusPurpleURL)
case invoice(Invoice)
}
}
12 changes: 10 additions & 2 deletions damus/Nostr/NostrLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum NostrLink: Equatable {
case ref(RefId)
case filter(NostrFilter)
case script([UInt8])
case invoice(String)
}

func encode_pubkey_uri(_ pubkey: Pubkey) -> String {
Expand Down Expand Up @@ -93,8 +94,15 @@ func decode_nostr_uri(_ s: String) -> NostrLink? {
return
}

if parts.count >= 2 && parts[0] == "t" {
return .filter(NostrFilter(hashtag: [parts[1].lowercased()]))
if parts.count >= 2 {
switch parts[0] {
case "t":
return .filter(NostrFilter(hashtag: [parts[1].lowercased()]))
case "lightning":
return .invoice(parts[1])
default:
break
}
}

guard parts.count == 1 else {
Expand Down
18 changes: 17 additions & 1 deletion damus/Types/Block.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,23 @@ enum Block: Equatable {
return false
}
}


var is_previewable: Bool {
switch self {
case .mention(let m):
switch m.ref {
case .note, .nevent: return true
default: return false
}
case .invoice:
return true
case .url:
return true
default:
return false
}
}

case text(String)
case mention(Mention<MentionRef>)
case hashtag(String)
Expand Down
4 changes: 2 additions & 2 deletions damus/Util/DisplayName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ func parse_display_name(name: String?, display_name: String?, pubkey: Pubkey) ->
}

func abbrev_bech32_pubkey(pubkey: Pubkey) -> String {
return abbrev_pubkey(String(pubkey.npub.dropFirst(4)))
return abbrev_identifier(String(pubkey.npub.dropFirst(4)))
}

func abbrev_pubkey(_ pubkey: String, amount: Int = 8) -> String {
func abbrev_identifier(_ pubkey: String, amount: Int = 8) -> String {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed because it was already abbreviating more than only pubkeys.

return pubkey.prefix(amount) + ":" + pubkey.suffix(amount)
}
2 changes: 1 addition & 1 deletion damus/Views/PubkeyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct PubkeyView: View {
let bech32 = pubkey.npub

HStack {
Text(verbatim: "\(abbrev_pubkey(bech32, amount: sidemenu ? 12 : 16))")
Text(verbatim: "\(abbrev_identifier(bech32, amount: sidemenu ? 12 : 16))")
.font(sidemenu ? .system(size: 10) : .footnote)
.foregroundColor(keyColor())
.padding(5)
Expand Down
3 changes: 1 addition & 2 deletions damusTests/damusTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ class damusTests: XCTestCase {
XCTAssertEqual(bytes.count, 32)
}

func testTrimmingFunctions() {
func testTrimSuffix() {
let txt = " bobs "

XCTAssertEqual(trim_prefix(txt), "bobs ")
XCTAssertEqual(trim_suffix(txt), " bobs")
}

Expand Down