damus

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

FollowPackTimeline.swift (5042B)


      1 //
      2 //  FollowPackTimeline.swift
      3 //  damus
      4 //
      5 //  Created by eric on 5/6/25.
      6 //
      7 
      8 import SwiftUI
      9 
     10 struct FollowPackTimelineView<Content: View>: View {
     11     @ObservedObject var events: EventHolder
     12     @Binding var loading: Bool
     13 
     14     let damus: DamusState
     15     let show_friend_icon: Bool
     16     let filter: (NostrEvent) -> Bool
     17     let content: Content?
     18     let apply_mute_rules: Bool
     19 
     20     init(events: EventHolder, loading: Binding<Bool>, headerHeight: Binding<CGFloat>, headerOffset: Binding<CGFloat>, damus: DamusState, show_friend_icon: Bool, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true, content: (() -> Content)? = nil) {
     21         self.events = events
     22         self._loading = loading
     23         self.damus = damus
     24         self.show_friend_icon = show_friend_icon
     25         self.filter = filter
     26         self.apply_mute_rules = apply_mute_rules
     27         self.content = content?()
     28     }
     29     
     30     init(events: EventHolder, loading: Binding<Bool>, damus: DamusState, show_friend_icon: Bool, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true, content: (() -> Content)? = nil) {
     31         self.events = events
     32         self._loading = loading
     33         self.damus = damus
     34         self.show_friend_icon = show_friend_icon
     35         self.filter = filter
     36         self.apply_mute_rules = apply_mute_rules
     37         self.content = content?()
     38     }
     39 
     40     var body: some View {
     41         MainContent
     42     }
     43     
     44     var MainContent: some View {
     45         ScrollViewReader { scroller in
     46             ScrollView(.horizontal) {
     47                 if let content {
     48                     content
     49                 }
     50 
     51                 Color.clear
     52                     .id("startblock")
     53                     .frame(height: 0)
     54 
     55                 FollowPackInnerView(events: events, damus: damus, filter: loading ? { _ in true } : filter, apply_mute_rules: self.apply_mute_rules)
     56                     .redacted(reason: loading ? .placeholder : [])
     57                     .shimmer(loading)
     58                     .disabled(loading)
     59                     .background {
     60                         GeometryReader { proxy -> Color in
     61                             handle_scroll_queue(proxy, queue: self.events)
     62                             return Color.clear
     63                         }
     64                     }
     65             }
     66             .coordinateSpace(name: "scroll")
     67             .onReceive(handle_notify(.scroll_to_top)) { () in
     68                 events.flush()
     69                 self.events.should_queue = false
     70                 scroll_to_event(scroller: scroller, id: "startblock", delay: 0.0, animate: true, anchor: .top)
     71             }
     72         }
     73         .onAppear {
     74             events.flush()
     75         }
     76     }
     77 }
     78 
     79 struct FollowPackInnerView: View {
     80     @ObservedObject var events: EventHolder
     81     let state: DamusState
     82     let filter: (NostrEvent) -> Bool
     83 
     84     init(events: EventHolder, damus: DamusState, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true) {
     85         self.events = events
     86         self.state = damus
     87         self.filter = apply_mute_rules ? { filter($0) && !damus.mutelist_manager.is_event_muted($0) } : filter
     88     }
     89     
     90     var event_options: EventViewOptions {
     91         if self.state.settings.truncate_timeline_text {
     92             return [.wide, .truncate_content]
     93         }
     94         
     95         return [.wide]
     96     }
     97     
     98     var body: some View {
     99         LazyHStack(spacing: 0) {
    100             let events = self.events.events
    101             if events.isEmpty {
    102                 EmptyTimelineView()
    103             } else {
    104                 let evs = events.filter(filter)
    105                 let indexed = Array(zip(evs, 0...))
    106                 ForEach(indexed, id: \.0.id) { tup in
    107                     let ev = tup.0
    108                     let ind = tup.1
    109                     let blur_imgs = should_blur_images(settings: state.settings, contacts: state.contacts, ev: ev, our_pubkey: state.pubkey)
    110                     if ev.kind == NostrKind.follow_list.rawValue {
    111                         FollowPackPreview(state: state, ev: ev, options: event_options, blur_imgs: blur_imgs)
    112                             .onTapGesture {
    113                                 state.nav.push(route: Route.FollowPack(followPack: ev, model: FollowPackModel(damus_state: state), blur_imgs: blur_imgs))
    114                             }
    115                             .padding(.top, 7)
    116                             .onAppear {
    117                                 let to_preload =
    118                                 Array([indexed[safe: ind+1]?.0,
    119                                        indexed[safe: ind+2]?.0,
    120                                        indexed[safe: ind+3]?.0,
    121                                        indexed[safe: ind+4]?.0,
    122                                        indexed[safe: ind+5]?.0
    123                                       ].compactMap({ $0 }))
    124                                 
    125                                 preload_events(state: state, events: to_preload)
    126                             }
    127                     }
    128                 }
    129             }
    130         }
    131         .padding(.bottom)
    132         
    133     }
    134 }
    135