damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 495859e07f605846a2e2fef79c0476984489e904
parent d96ea593a5b2ea3bbebceb82710a6e06c6c6ae37
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 11 Jul 2023 12:17:59 -0700

Fix various padding issues related to longform posts

1. Make a proper threaded EventShell variant
2. Fix padding everywhere

Changelog-Fixed: Fix padding on longform events

Diffstat:
Mdamus/Views/EventView.swift | 4++++
Mdamus/Views/Events/EventShell.swift | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mdamus/Views/Events/Longform/LongformPreview.swift | 43++++++++++++++++++++++++++++---------------
Mdamus/Views/Events/Longform/LongformView.swift | 12+++++-------
Mdamus/Views/Events/SelectedEventView.swift | 5++---
Mdamus/Views/Events/TextEvent.swift | 109++++++-------------------------------------------------------------------------
Mdamus/Views/NoteContentView.swift | 44+++++++++++++++++++++++++++++---------------
Mdamus/Views/ReplyView.swift | 35+++++++++++++++++++++++------------
Mdamus/Views/Reposts/RepostedEvent.swift | 2+-
9 files changed, 175 insertions(+), 165 deletions(-)

diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -142,7 +142,10 @@ struct EventView_Previews: PreviewProvider { EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .big) */ + EventView( damus: test_damus_state(), event: test_event ) + + EventView( damus: test_damus_state(), event: test_longform_event.event, options: [.wide] ) } .padding() } @@ -154,3 +157,4 @@ let test_event = pubkey: "pk", createdAt: Int64(Date().timeIntervalSince1970 - 100) ) + diff --git a/damus/Views/Events/EventShell.swift b/damus/Views/Events/EventShell.swift @@ -25,20 +25,64 @@ struct EventShell<Content: View>: View { } func get_mention() -> Mention? { - if self.options.contains(.nested) { + if self.options.contains(.nested) || self.options.contains(.no_mentions) { return nil } return first_eref_mention(ev: event, privkey: state.keypair.privkey) } + + func Mention(_ mention: Mention) -> some View { + return BuilderEventView(damus: state, event_id: mention.ref.id) + } - var body: some View { + var ActionBar: some View { + return EventActionBar(damus_state: state, event: event) + .padding([.top], 4) + } + + func Pfp(is_anon: Bool) -> some View { + return MaybeAnonPfpView(state: state, is_anon: is_anon, pubkey: event.pubkey, size: options.contains(.small_pfp) ? eventview_pfp_size(.small) : PFP_SIZE ) + } + + var Threaded: some View { + HStack(alignment: .top) { + + let is_anon = event_is_anonymous(ev: event) + VStack { + Pfp(is_anon: is_anon) + + Spacer() + } + + VStack(alignment: .leading) { + EventTop(state: state, event: event, is_anon: is_anon) + + if !options.contains(.no_replying_to) { + ReplyPart(event: event, privkey: state.keypair.privkey, profiles: state.profiles) + } + + content + + if let mention = get_mention() { + Mention(mention) + } + + if has_action_bar { + ActionBar + } + } + .padding([.leading], 2) + } + } + + var Wide: some View { VStack(alignment: .leading) { let is_anon = event_is_anonymous(ev: event) HStack(spacing: 10) { - MaybeAnonPfpView(state: state, is_anon: is_anon, pubkey: event.pubkey, size: options.contains(.small_pfp) ? eventview_pfp_size(.small) : PFP_SIZE ) - + Pfp(is_anon: is_anon) + VStack { EventTop(state: state, event: event, is_anon: is_anon) ReplyPart(event: event, privkey: state.keypair.privkey, profiles: state.profiles) @@ -49,24 +93,44 @@ struct EventShell<Content: View>: View { content if !options.contains(.no_mentions), let mention = get_mention() { - - BuilderEventView(damus: state, event_id: mention.ref.id) + Mention(mention) .padding(.horizontal) } if has_action_bar { - //EmptyRect - EventActionBar(damus_state: state, event: event) - .padding([.leading, .trailing, .top]) + ActionBar + .padding(.horizontal) } } } + + var body: some View { + Group { + if options.contains(.wide) { + Wide + } else { + Threaded + } + } + .contentShape(Rectangle()) + .id(event.id) + .frame(maxWidth: .infinity, minHeight: PFP_SIZE) + .padding([.bottom], 2) + } } struct EventShell_Previews: PreviewProvider { + static var previews: some View { - EventShell(state: test_damus_state(), event: test_event, options: [.no_action_bar]) { - Text("Hello") + VStack { + EventShell(state: test_damus_state(), event: test_event, options: [.no_action_bar]) { + Text("Hello") + } + + EventShell(state: test_damus_state(), event: test_event, options: [.no_action_bar, .wide]) { + Text("Hello") + } } + .frame(height: 300) } } diff --git a/damus/Views/Events/Longform/LongformPreview.swift b/damus/Views/Events/Longform/LongformPreview.swift @@ -24,29 +24,42 @@ struct LongformPreview: View { func Words(_ words: Int) -> Text { Text(verbatim: words.description) + Text(verbatim: " ") + Text("Words") } - + + var Main: some View { + VStack(alignment: .leading, spacing: 10) { + Text(event.title ?? "Untitled") + .font(.title) + + Text(event.summary ?? "") + .foregroundColor(.gray) + + if case .loaded(let arts) = artifacts.state, + case .parts(let parts) = arts + { + Words(parts.words).font(.footnote) + } + } + } + var body: some View { EventShell(state: state, event: event.event, options: options.union(.no_mentions)) { - VStack(alignment: .leading, spacing: 10) { - Text(event.title ?? "Untitled") - .font(.title) - - Text(event.summary ?? "") - .foregroundColor(.gray) - - if case .loaded(let arts) = artifacts.state, - case .parts(let parts) = arts - { - Words(parts.words).font(.footnote) - } + + if options.contains(.wide) { + Main.padding(.horizontal) + } else { + Main } - .padding() } } } struct LongformPreview_Previews: PreviewProvider { static var previews: some View { - LongformPreview(state: test_damus_state(), ev: test_longform_event.event, options: []) + VStack { + LongformPreview(state: test_damus_state(), ev: test_longform_event.event, options: []) + + LongformPreview(state: test_damus_state(), ev: test_longform_event.event, options: [.wide]) + } + .frame(height: 400) } } diff --git a/damus/Views/Events/Longform/LongformView.swift b/damus/Views/Events/Longform/LongformView.swift @@ -52,18 +52,16 @@ struct LongformView: View { var body: some View { EventShell(state: state, event: event.event, options: options) { - - VStack { - SelectableText(attributedString: AttributedString(stringLiteral: event.title ?? "Untitled"), size: .title) - - NoteContentView(damus_state: state, event: event.event, show_images: true, size: .selected, options: options) - } + SelectableText(attributedString: AttributedString(stringLiteral: event.title ?? "Untitled"), size: .title) + + NoteContentView(damus_state: state, event: event.event, show_images: true, size: .selected, options: options) } } } let test_longform_event = LongformEvent.parse(from: - .init(content: "## Let me tell you why coffee is awesome\n**IT JUST IS**", + .init(id: "longform_id", + content: "## Let me tell you why coffee is awesome\n**IT JUST IS**", pubkey: "pk", kind: NostrKind.longform.rawValue, tags: [ diff --git a/damus/Views/Events/SelectedEventView.swift b/damus/Views/Events/SelectedEventView.swift @@ -47,8 +47,8 @@ struct SelectedEventView: View { .padding(.horizontal) } - EventBody(damus_state: damus, event: event, size: size, options: [.pad_content]) - + EventBody(damus_state: damus, event: event, size: size, options: [.wide]) + if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) { BuilderEventView(damus: damus, event_id: mention.ref.id) .padding(.horizontal) @@ -88,6 +88,5 @@ struct SelectedEventView: View { struct SelectedEventView_Previews: PreviewProvider { static var previews: some View { SelectedEventView(damus: test_damus_state(), event: test_event, size: .selected) - .padding() } } diff --git a/damus/Views/Events/TextEvent.swift b/damus/Views/Events/TextEvent.swift @@ -14,13 +14,12 @@ struct EventViewOptions: OptionSet { static let no_replying_to = EventViewOptions(rawValue: 1 << 1) static let wide = EventViewOptions(rawValue: 1 << 3) static let truncate_content = EventViewOptions(rawValue: 1 << 4) - static let pad_content = EventViewOptions(rawValue: 1 << 5) - static let no_translate = EventViewOptions(rawValue: 1 << 6) - static let small_pfp = EventViewOptions(rawValue: 1 << 7) - static let nested = EventViewOptions(rawValue: 1 << 8) - static let top_zap = EventViewOptions(rawValue: 1 << 9) - static let no_mentions = EventViewOptions(rawValue: 1 << 10) - + static let no_translate = EventViewOptions(rawValue: 1 << 5) + static let small_pfp = EventViewOptions(rawValue: 1 << 6) + static let nested = EventViewOptions(rawValue: 1 << 7) + static let top_zap = EventViewOptions(rawValue: 1 << 8) + static let no_mentions = EventViewOptions(rawValue: 1 << 9) + static let embedded: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested] } @@ -39,51 +38,12 @@ struct TextEvent: View { self.evdata = damus.events.get_cache_data(event.id) } - var has_action_bar: Bool { - !options.contains(.no_action_bar) - } - var body: some View { - Group { - if options.contains(.wide) { - WideStyle - } else { - ThreadedStyle - } - } - .contentShape(Rectangle()) - .id(event.id) - .frame(maxWidth: .infinity, minHeight: PFP_SIZE) - .padding([.bottom], 2) - } - - func Pfp(is_anon: Bool) -> some View { - MaybeAnonPfpView(state: damus, is_anon: is_anon, pubkey: pubkey, size: options.contains(.small_pfp) ? eventview_pfp_size(.small) : PFP_SIZE ) - } - - func TopPart(is_anon: Bool) -> some View { - HStack(alignment: .center, spacing: 0) { - ProfileName(is_anon: is_anon) - TimeDot() - RelativeTime(time: self.evdata.relative_time) - Spacer() - EventMenuContext(damus: damus, event: event) - } - .lineLimit(1) - } - - var WideStyle: some View { EventShell(state: damus, event: event, options: options) { - EvBody(options: self.options.union(.pad_content)) + EvBody(options: options) } } - - func ProfileName(is_anon: Bool) -> some View { - let profile = damus.profiles.lookup(id: pubkey) - let pk = is_anon ? ANON_PUBKEY : pubkey - return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal) - } - + func EvBody(options: EventViewOptions) -> some View { let show_imgs = should_show_images(settings: damus.settings, contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey) return NoteContentView( @@ -93,61 +53,8 @@ struct TextEvent: View { size: .normal, options: options ) - .fixedSize(horizontal: false, vertical: true) - } - - func Mention(_ mention: Mention) -> some View { - return BuilderEventView(damus: damus, event_id: mention.ref.id) - } - - var ActionBar: some View { - return EventActionBar(damus_state: damus, event: event) - .padding([.top], 4) - } - - var EmptyRect: some View { - return Rectangle().frame(height: 2).opacity(0) - } - - func get_mention() -> Mention? { - if self.options.contains(.nested) { - return nil - } - - return first_eref_mention(ev: event, privkey: damus.keypair.privkey) } - - var ThreadedStyle: some View { - HStack(alignment: .top) { - - let is_anon = event_is_anonymous(ev: event) - VStack { - Pfp(is_anon: is_anon) - - Spacer() - } - VStack(alignment: .leading) { - TopPart(is_anon: is_anon) - - if !options.contains(.no_replying_to) { - ReplyPart(event: event, privkey: damus.keypair.privkey, profiles: damus.profiles) - } - - EvBody(options: self.options) - - if let mention = get_mention() { - Mention(mention) - } - - if has_action_bar { - EmptyRect - ActionBar - } - } - .padding([.leading], 2) - } - } } func event_has_tag(ev: NostrEvent, tag: String) -> Bool { diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -54,7 +54,7 @@ struct NoteContentView: View { } var with_padding: Bool { - return options.contains(.pad_content) + return options.contains(.wide) } var preview: LinkViewRepresentable? { @@ -185,16 +185,23 @@ struct NoteContentView: View { func artifactPartsView(_ parts: [ArtifactPart]) -> some View { - LazyVStack { + LazyVStack(alignment: .leading) { ForEach(parts.indices, id: \.self) { ind in let part = parts[ind] switch part { case .text(let txt): - txt - .padding(.horizontal) + if with_padding { + txt.padding(.horizontal) + } else { + txt + } case .invoice(let inv): - InvoiceView(our_pubkey: damus_state.pubkey, invoice: inv, settings: damus_state.settings) - .padding(.horizontal) + if with_padding { + InvoiceView(our_pubkey: damus_state.pubkey, invoice: inv, settings: damus_state.settings) + .padding(.horizontal) + } else { + InvoiceView(our_pubkey: damus_state.pubkey, invoice: inv, settings: damus_state.settings) + } case .media(let media): Text("media \(media.url.absoluteString)") } @@ -211,6 +218,7 @@ struct NoteContentView: View { MainContent(artifacts: separated) } } + .fixedSize(horizontal: false, vertical: true) } var body: some View { @@ -277,15 +285,6 @@ func mention_str(_ m: Mention, profiles: Profiles) -> CompatibleText { } } -struct NoteContentView_Previews: PreviewProvider { - static var previews: some View { - let state = test_damus_state() - let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg" - NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, size: .normal, options: []) - } -} - - enum NoteArtifacts { case separated(NoteArtifactsSeparated) case parts(NoteArtifactsParts) @@ -652,3 +651,18 @@ func trim_suffix(_ str: String) -> String { func trim_prefix(_ str: String) -> String { return str.replacingOccurrences(of: "^\\s+", with: "", options: .regularExpression) } + +struct NoteContentView_Previews: PreviewProvider { + static var previews: some View { + let state = test_damus_state() + + VStack { + NoteContentView(damus_state: state, event: test_event, show_images: true, size: .normal, options: []) + + NoteContentView(damus_state: state, event: test_longform_event.event, show_images: true, size: .normal, options: [.wide]) + .border(Color.red) + } + } +} + + diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift @@ -51,30 +51,41 @@ struct ReplyView: View { Spacer() } } - + + func line(height: CGFloat) -> some View { + return Rectangle() + .fill(Color.gray.opacity(0.25)) + .frame(width: 2, height: height) + .offset(x: 25, y: 40) + .padding(.leading) + } + var body: some View { VStack(alignment: .leading) { - EventView(damus: damus, event: replying_to, options: [.no_action_bar]) .padding() .background(GeometryReader { geometry in let eventHeight = geometry.frame(in: .global).height - Rectangle() - .fill(Color.gray.opacity(0.25)) - .frame(width: 2, height: eventHeight + 7) - .offset(x: 25, y: 40) - .padding(.leading) + line(height: eventHeight) }) ReplyingToSection .background(GeometryReader { geometry in let replyingToHeight = geometry.frame(in: .global).height - Rectangle() - .fill(Color.gray.opacity(0.25)) - .frame(width: 2, height: replyingToHeight) - .offset(x: 25, y: 40) - .padding(.leading) + line(height: replyingToHeight) }) } } } + +struct ReplyView_Previews: PreviewProvider { + static var previews: some View { + VStack { + ReplyView(replying_to: test_event, damus: test_damus_state(), originalReferences: .constant([]), references: .constant([])) + .frame(height: 300) + + ReplyView(replying_to: test_longform_event.event, damus: test_damus_state(), originalReferences: .constant([]), references: .constant([])) + .frame(height: 300) + } + } +} diff --git a/damus/Views/Reposts/RepostedEvent.swift b/damus/Views/Reposts/RepostedEvent.swift @@ -24,7 +24,7 @@ struct RepostedEvent: View { .buttonStyle(PlainButtonStyle()) //SelectedEventView(damus: damus, event: inner_ev, size: .normal) - EventView(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options) + EventView(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options.union(.wide)) } } }