1
0
mirror of git://jb55.com/damus synced 2024-09-19 19:46:51 +00:00

New Timeline

Switch to a new timeline style that has higher information density and
better image display
This commit is contained in:
William Casarin 2023-03-23 17:29:37 -06:00
parent 2e34230119
commit f84d4516db
15 changed files with 253 additions and 92 deletions

View File

@ -225,6 +225,8 @@
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
@ -601,6 +603,8 @@
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>"; };
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
@ -672,6 +676,7 @@
isa = PBXGroup;
children = (
3AA24801297E3DC20090C62D /* RepostView.swift */,
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */,
);
path = Reposts;
sourceTree = "<group>";
@ -987,6 +992,7 @@
4CF0ABE6298444FC00D66079 /* MutedEventView.swift */,
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
);
path = Events;
sourceTree = "<group>";
@ -1502,6 +1508,7 @@
4CE879582996C45300F758CC /* ZapsView.swift in Sources */,
4C30AC7429A5680900E2BD5A /* EventGroupView.swift in Sources */,
4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */,
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */,
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */,
4C363A94282704FA006E126D /* Post.swift in Sources */,
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
@ -1530,6 +1537,7 @@
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */,
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */,
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
4CF0ABD629817F5B00D66079 /* ReportView.swift in Sources */,

View File

@ -33,8 +33,6 @@ struct ShareSheet: UIViewControllerRepresentable {
struct ImageCarousel: View {
var urls: [URL]
@ -53,8 +51,8 @@ struct ImageCarousel: View {
.configure { view in
view.framePreloadCount = 3
}
.aspectRatio(contentMode: .fit)
.cornerRadius(10)
.aspectRatio(contentMode: .fill)
//.cornerRadius(10)
.tabItem {
Text(url.absoluteString)
}
@ -70,8 +68,7 @@ struct ImageCarousel: View {
.fullScreenCover(isPresented: $open_sheet) {
ImageView(urls: urls)
}
.frame(height: 200)
.clipped()
.frame(height: 350)
.onTapGesture {
open_sheet = true
}

View File

@ -15,12 +15,10 @@ struct Reposted: View {
var body: some View {
HStack(alignment: .center) {
Image(systemName: "arrow.2.squarepath")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(Color.gray)
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true, show_nip5_domain: false)
.foregroundColor(Color.gray)
Text("Reposted", comment: "Text indicating that the post was reposted (i.e. re-shared).")
.font(.system(size: 14, weight: .heavy))
.foregroundColor(Color.gray)
}
}

View File

@ -114,7 +114,7 @@ struct ChatView: View {
show_images: show_images,
size: .normal,
artifacts: .just_content(event.content),
truncate: false)
options: [])
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
let bar = make_actionbar_model(ev: event.id, damus: damus_state)

View File

@ -23,7 +23,7 @@ struct DMView: View {
let should_show_img = should_show_images(settings: damus_state.settings, contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: .normal, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), truncate: false)
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: .normal, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), options: [])
.padding([.top, .leading, .trailing], 10)
.padding([.bottom], 25)
.background(VisualEffectView(effect: UIBlurEffect(style: .prominent))

View File

@ -60,17 +60,7 @@ struct EventView: View {
VStack {
if event.known_kind == .boost {
if let inner_ev = event.inner_event {
VStack(alignment: .leading) {
let prof = damus.profiles.lookup(id: event.pubkey)
let booster_profile = ProfileView(damus_state: damus, pubkey: event.pubkey)
NavigationLink(destination: booster_profile) {
Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
}
.buttonStyle(PlainButtonStyle())
TextEvent(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options)
.padding([.top], 1)
}
RepostedEvent(damus: damus, event: event, inner_ev: inner_ev, options: options)
} else {
EmptyView()
}
@ -82,7 +72,7 @@ struct EventView: View {
}
} else {
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
.padding([.top], 6)
//.padding([.top], 6)
}
}
}

View File

@ -30,7 +30,7 @@ struct EmbeddedEventView: View {
.minimumScaleFactor(0.75)
.lineLimit(1)
EventBody(damus_state: damus_state, event: event, size: .small)
EventBody(damus_state: damus_state, event: event, size: .small, options: [.truncate_content])
}
}
}

View File

@ -12,11 +12,13 @@ struct EventBody: View {
let event: NostrEvent
let size: EventViewKind
let should_show_img: Bool
let options: EventViewOptions
init(damus_state: DamusState, event: NostrEvent, size: EventViewKind, should_show_img: Bool? = nil) {
init(damus_state: DamusState, event: NostrEvent, size: EventViewKind, should_show_img: Bool? = nil, options: EventViewOptions) {
self.damus_state = damus_state
self.event = event
self.size = size
self.options = options
self.should_show_img = should_show_img ?? should_show_images(settings: damus_state.settings, contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
}
@ -25,17 +27,13 @@ struct EventBody: View {
}
var body: some View {
if event_is_reply(event, privkey: damus_state.keypair.privkey) {
ReplyDescription(event: event, profiles: damus_state.profiles)
}
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, artifacts: .just_content(content), truncate: true)
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, artifacts: .just_content(content), options: options)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct EventBody_Previews: PreviewProvider {
static var previews: some View {
EventBody(damus_state: test_damus_state(), event: test_event, size: .normal)
EventBody(damus_state: test_damus_state(), event: test_event, size: .normal, options: [])
}
}

View File

@ -51,7 +51,7 @@ struct MutedEventView: View {
var Event: some View {
Group {
if selected {
SelectedEventView(damus: damus_state, event: event)
SelectedEventView(damus: damus_state, event: event, size: .selected)
} else {
EventView(damus: damus_state, event: event)
}

View File

@ -10,6 +10,7 @@ import SwiftUI
struct SelectedEventView: View {
let damus: DamusState
let event: NostrEvent
let size: EventViewKind
var pubkey: String {
event.pubkey
@ -17,9 +18,10 @@ struct SelectedEventView: View {
@StateObject var bar: ActionBarModel
init(damus: DamusState, event: NostrEvent) {
init(damus: DamusState, event: NostrEvent, size: EventViewKind) {
self.damus = damus
self.event = event
self.size = size
self._bar = StateObject(wrappedValue: make_actionbar_model(ev: event.id, damus: damus))
}
@ -40,7 +42,7 @@ struct SelectedEventView: View {
.minimumScaleFactor(0.75)
.lineLimit(1)
EventBody(damus_state: damus, event: event, size: .selected)
EventBody(damus_state: damus, event: event, size: size, options: [])
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
BuilderEventView(damus: damus, event_id: mention.ref.id)
@ -78,7 +80,7 @@ struct SelectedEventView: View {
struct SelectedEventView_Previews: PreviewProvider {
static var previews: some View {
SelectedEventView(damus: test_damus_state(), event: test_event)
SelectedEventView(damus: test_damus_state(), event: test_event, size: .selected)
.padding()
}
}

View File

@ -12,6 +12,9 @@ struct EventViewOptions: OptionSet {
static let no_action_bar = EventViewOptions(rawValue: 1 << 0)
static let no_replying_to = EventViewOptions(rawValue: 1 << 1)
static let no_images = EventViewOptions(rawValue: 1 << 2)
static let wide = EventViewOptions(rawValue: 1 << 3)
static let truncate_content = EventViewOptions(rawValue: 1 << 4)
static let pad_content = EventViewOptions(rawValue: 1 << 5)
}
struct TextEvent: View {
@ -25,51 +28,12 @@ struct TextEvent: View {
}
var body: some View {
HStack(alignment: .top) {
let profile = damus.profiles.lookup(id: pubkey)
let is_anon = event_is_anonymous(ev: event)
VStack {
MaybeAnonPfpView(state: damus, is_anon: is_anon, pubkey: pubkey)
Spacer()
Group {
if options.contains(.wide) {
WideStyle
} else {
ThreadedStyle
}
VStack(alignment: .leading) {
HStack(alignment: .center, spacing: 0) {
let pk = is_anon ? "anon" : pubkey
EventProfileName(pubkey: pk, profile: profile, damus: damus, show_friend_confirmed: true, size: .normal)
Text(verbatim: "")
.font(.footnote)
.foregroundColor(.gray)
Text(verbatim: "\(format_relative_time(event.created_at))")
.font(.system(size: 16))
.foregroundColor(.gray)
Spacer()
EventMenuContext(event: event, keypair: damus.keypair, target_pubkey: event.pubkey, bookmarks: damus.bookmarks)
.padding([.bottom], 4)
}
.minimumScaleFactor(0.75)
.lineLimit(1)
EventBody(damus_state: damus, event: event, size: .normal)
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
BuilderEventView(damus: damus, event_id: mention.ref.id)
}
if has_action_bar {
Rectangle().frame(height: 2).opacity(0)
EventActionBar(damus_state: damus, event: event)
.padding([.top], 4)
}
}
.padding([.leading], 2)
}
.contentShape(Rectangle())
.background(event_validity_color(event.validity))
@ -77,11 +41,127 @@ struct TextEvent: View {
.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)
}
struct TextEvent_Previews: PreviewProvider {
static var previews: some View {
TextEvent(damus: test_damus_state(), event: test_event, pubkey: "pk", options: [])
func TopPart(is_anon: Bool) -> some View {
HStack(alignment: .center, spacing: 0) {
ProfileName(is_anon: is_anon)
TimeDot
Time
Spacer()
ContextButton
}
.lineLimit(1)
}
var ReplyPart: some View {
Group {
if event_is_reply(event, privkey: damus.keypair.privkey) {
ReplyDescription(event: event, profiles: damus.profiles)
} else {
EmptyView()
}
}
}
var WideStyle: some View {
VStack(alignment: .leading) {
let is_anon = event_is_anonymous(ev: event)
HStack(spacing: 10) {
Pfp(is_anon: is_anon)
VStack {
TopPart(is_anon: is_anon)
ReplyPart
}
}
.padding(.horizontal)
EvBody(options: [.truncate_content, .pad_content])
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
Mention(mention)
.padding(.horizontal)
}
if has_action_bar {
//EmptyRect
ActionBar
.padding(.horizontal)
}
}
}
var TimeDot: some View {
Text(verbatim: "")
.font(.footnote)
.foregroundColor(.gray)
}
var Time: some View {
Text(verbatim: "\(format_relative_time(event.created_at))")
.font(.system(size: 16))
.foregroundColor(.gray)
}
var ContextButton: some View {
EventMenuContext(event: event, keypair: damus.keypair, target_pubkey: event.pubkey, bookmarks: damus.bookmarks)
.padding([.bottom], 4)
}
func ProfileName(is_anon: Bool) -> some View {
let profile = damus.profiles.lookup(id: pubkey)
let pk = is_anon ? "anon" : pubkey
return EventProfileName(pubkey: pk, profile: profile, damus: damus, show_friend_confirmed: true, size: .normal)
}
func EvBody(options: EventViewOptions) -> some View {
return EventBody(damus_state: damus, event: event, size: .normal, options: options)
}
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)
}
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)
ReplyPart
EvBody(options: [])
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
Mention(mention)
}
if has_action_bar {
EmptyRect
ActionBar
}
}
.padding([.leading], 2)
}
}
}
@ -99,3 +179,16 @@ func event_has_tag(ev: NostrEvent, tag: String) -> Bool {
func event_is_anonymous(ev: NostrEvent) -> Bool {
return ev.known_kind == .zap_request && event_has_tag(ev: ev, tag: "anon")
}
struct TextEvent_Previews: PreviewProvider {
static var previews: some View {
VStack(spacing: 20) {
TextEvent(damus: test_damus_state(), event: test_event, pubkey: "pk", options: [])
.frame(height: 400)
TextEvent(damus: test_damus_state(), event: test_event, pubkey: "pk", options: [.wide])
.frame(height: 400)
}
}
}

View File

@ -0,0 +1,22 @@
//
// WideEventView.swift
// damus
//
// Created by William Casarin on 2023-03-23.
//
import SwiftUI
struct WideEventView: View {
let event: NostrEvent
var body: some View {
EmptyView()
}
}
struct WideEventView_Previews: PreviewProvider {
static var previews: some View {
WideEventView(event: test_event)
}
}

View File

@ -28,32 +28,48 @@ struct NoteContentView: View {
let show_images: Bool
let size: EventViewKind
let preview_height: CGFloat?
let truncate: Bool
let options: EventViewOptions
@State var artifacts: NoteArtifacts
@State var preview: LinkViewRepresentable?
init(damus_state: DamusState, event: NostrEvent, show_images: Bool, size: EventViewKind, artifacts: NoteArtifacts, truncate: Bool) {
init(damus_state: DamusState, event: NostrEvent, show_images: Bool, size: EventViewKind, artifacts: NoteArtifacts, options: EventViewOptions) {
self.damus_state = damus_state
self.event = event
self.show_images = show_images
self.size = size
self.options = options
self._artifacts = State(initialValue: artifacts)
self.preview_height = lookup_cached_preview_size(previews: damus_state.previews, evid: event.id)
self._preview = State(initialValue: load_cached_preview(previews: damus_state.previews, evid: event.id))
self._artifacts = State(initialValue: render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey))
self.truncate = truncate
}
var truncate: Bool {
return options.contains(.truncate_content)
}
var with_padding: Bool {
return options.contains(.pad_content)
}
var truncatedText: some View {
TruncatedText(text: artifacts.content, maxChars: (truncate ? 280 : nil))
.font(eventviewsize_to_font(size))
}
func MainContent() -> some View {
return VStack(alignment: .leading) {
if size == .selected {
SelectableText(attributedString: artifacts.content)
TranslateView(damus_state: damus_state, event: event)
} else {
TruncatedText(text: artifacts.content, maxChars: (truncate ? 280 : nil))
.font(eventviewsize_to_font(size))
if with_padding {
truncatedText
.padding(.horizontal)
} else {
truncatedText
}
}
if show_images && artifacts.images.count > 0 {
@ -64,7 +80,7 @@ struct NoteContentView: View {
Blur()
.disabled(true)
}
.cornerRadius(10)
//.cornerRadius(10)
}
if artifacts.invoices.count > 0 {
@ -177,7 +193,7 @@ struct NoteContentView_Previews: PreviewProvider {
let state = test_damus_state()
let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
let artifacts = NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, size: .normal, artifacts: artifacts, truncate: false)
NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, size: .normal, artifacts: artifacts, options: [])
}
}

View File

@ -0,0 +1,37 @@
//
// RepostedEvent.swift
// damus
//
// Created by William Casarin on 2023-03-23.
//
import SwiftUI
struct RepostedEvent: View {
let damus: DamusState
let event: NostrEvent
let inner_ev: NostrEvent
let options: EventViewOptions
var body: some View {
VStack(alignment: .leading) {
let prof = damus.profiles.lookup(id: event.pubkey)
let booster_profile = ProfileView(damus_state: damus, pubkey: event.pubkey)
NavigationLink(destination: booster_profile) {
Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
.padding(.horizontal)
}
.buttonStyle(PlainButtonStyle())
//SelectedEventView(damus: damus, event: inner_ev, size: .normal)
TextEvent(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, options: options)
}
}
}
struct RepostedEvent_Previews: PreviewProvider {
static var previews: some View {
RepostedEvent(damus: test_damus_state(), event: test_event, inner_ev: test_event, options: [])
}
}

View File

@ -37,7 +37,7 @@ struct InnerTimelineView: View {
EmptyTimelineView()
} else {
ForEach(events.filter(filter), id: \.id) { (ev: NostrEvent) in
EventView(damus: damus, event: ev)
EventView(damus: damus, event: ev, options: [.wide])
.onTapGesture {
nav_target = ev.inner_event ?? ev
navigating = true
@ -49,7 +49,7 @@ struct InnerTimelineView: View {
}
}
}
.padding(.horizontal)
//.padding(.horizontal)
}
}