damus

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

LongformPreview.swift (7279B)


      1 //
      2 //  LongformPreview.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2023-06-01.
      6 //
      7 
      8 import SwiftUI
      9 import Kingfisher
     10 
     11 struct LongformPreviewBody: View {
     12     let state: DamusState
     13     let event: LongformEvent
     14     let options: EventViewOptions
     15     let header: Bool
     16     @State var blur_images: Bool = true
     17     
     18     @ObservedObject var artifacts: NoteArtifactsModel
     19 
     20     init(state: DamusState, ev: LongformEvent, options: EventViewOptions, header: Bool) {
     21         self.state = state
     22         self.event = ev
     23         self.options = options
     24         self.header = header
     25 
     26         self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.event.id).artifacts_model)
     27     }
     28 
     29     init(state: DamusState, ev: NostrEvent, options: EventViewOptions, header: Bool) {
     30         self.state = state
     31         self.event = LongformEvent.parse(from: ev)
     32         self.options = options
     33         self.header = header
     34 
     35         self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
     36     }
     37 
     38     func Words(_ words: Int) -> Text {
     39         let wordCount = pluralizedString(key: "word_count", count: words)
     40         return Text(wordCount)
     41     }
     42     
     43     var truncate: Bool {
     44         return options.contains(.truncate_content)
     45     }
     46     
     47     var truncate_very_short: Bool {
     48         return options.contains(.truncate_content_very_short)
     49     }
     50     
     51     func truncatedText(content: CompatibleText) -> some View {
     52         Group {
     53             if truncate_very_short {
     54                 TruncatedText(text: content, maxChars: 140, show_show_more_button: !options.contains(.no_show_more))
     55                     .font(header ? .body : .caption)
     56                     .foregroundColor(.gray)
     57                     .padding(.horizontal, 10)
     58             }
     59             else if truncate {
     60                 TruncatedText(text: content, show_show_more_button: !options.contains(.no_show_more))
     61                     .font(header ? .body : .caption)
     62                     .foregroundColor(.gray)
     63                     .padding(.horizontal, 10)
     64             } else {
     65                 content.text
     66                     .font(header ? .body : .caption)
     67                     .foregroundColor(.gray)
     68                     .padding(.horizontal, 10)
     69             }
     70         }
     71     }
     72     
     73     func Placeholder(url: URL) -> some View {
     74         Group {
     75             if let meta = state.events.lookup_img_metadata(url: url),
     76                case .processed(let blurhash) = meta.state {
     77                 Image(uiImage: blurhash)
     78                     .resizable()
     79                     .frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150)
     80             } else {
     81                 DamusColors.adaptableWhite
     82             }
     83         }
     84     }
     85     
     86     func titleImage(url: URL) -> some View {
     87         KFAnimatedImage(url)
     88             .callbackQueue(.dispatch(.global(qos:.background)))
     89             .backgroundDecode(true)
     90             .imageContext(.note, disable_animation: state.settings.disable_animation)
     91             .image_fade(duration: 0.25)
     92             .cancelOnDisappear(true)
     93             .configure { view in
     94                 view.framePreloadCount = 3
     95             }
     96             .background {
     97                 Placeholder(url: url)
     98             }
     99             .aspectRatio(contentMode: .fill)
    100             .frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150)
    101             .kfClickable()
    102             .cornerRadius(1)
    103     }
    104 
    105     var body: some View {
    106         Group {
    107             if options.contains(.wide) {
    108                 Main.padding(.horizontal)
    109             } else {
    110                 Main
    111             }
    112         }
    113     }
    114 
    115     var Main: some View {
    116         VStack(alignment: .leading, spacing: 10) {
    117             if let url = event.image {
    118                 if (self.options.contains(.no_media)) {
    119                     EmptyView()
    120                 } else if !blur_images || (!blur_images && !state.settings.media_previews) {
    121                     titleImage(url: url)
    122                 } else if blur_images || (blur_images && !state.settings.media_previews) {
    123                     ZStack {
    124                         titleImage(url: url)
    125                         BlurOverlayView(blur_images: $blur_images, artifacts: nil, size: nil, damus_state: nil, parentView: .longFormView)
    126                     }
    127                 }
    128             }
    129             
    130             Text(event.title ?? NSLocalizedString("Untitled", comment: "Title of longform event if it is untitled."))
    131                 .font(header ? .title : .headline)
    132                 .padding(.horizontal, 10)
    133                 .padding(.top, 5)
    134             
    135             if let summary = event.summary {
    136                 truncatedText(content: CompatibleText(stringLiteral: summary))
    137             }
    138             
    139             if let labels = event.labels {
    140                 ScrollView(.horizontal) {
    141                     HStack {
    142                         ForEach(labels, id: \.self) { label in
    143                             Text(label)
    144                                 .font(.caption)
    145                                 .foregroundColor(.gray)
    146                                 .padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
    147                                 .background(DamusColors.neutral1)
    148                                 .cornerRadius(20)
    149                                 .overlay(
    150                                     RoundedRectangle(cornerRadius: 20)
    151                                         .stroke(DamusColors.neutral3, lineWidth: 1)
    152                                 )
    153                         }
    154                     }
    155                 }
    156                 .scrollIndicators(.hidden)
    157                 .padding(10)
    158             }
    159 
    160             
    161             if case .loaded(let arts) = artifacts.state,
    162                case .longform(let longform) = arts
    163             {
    164                 Words(longform.words).font(.footnote)
    165                     .padding([.horizontal, .bottom], 10)
    166             }
    167         }
    168         .frame(maxWidth: .infinity, alignment: .leading)
    169         .background(DamusColors.neutral3)
    170         .cornerRadius(10)
    171         .overlay(
    172             RoundedRectangle(cornerRadius: 10)
    173                 .stroke(DamusColors.neutral1, lineWidth: 1)
    174         )
    175         .padding(.top, 10)
    176         .onAppear {
    177             blur_images = should_blur_images(settings: state.settings, contacts: state.contacts, ev: event.event, our_pubkey: state.pubkey)
    178         }
    179     }
    180 }
    181 
    182 struct LongformPreview: View {
    183     let state: DamusState
    184     let event: LongformEvent
    185     let options: EventViewOptions
    186 
    187     init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
    188         self.state = state
    189         self.event = LongformEvent.parse(from: ev)
    190         self.options = options.union(.no_mentions)
    191     }
    192 
    193     var body: some View {
    194         EventShell(state: state, event: event.event, options: options) {
    195             LongformPreviewBody(state: state, ev: event, options: options, header: false)
    196         }
    197     }
    198 }
    199 
    200 struct LongformPreview_Previews: PreviewProvider {
    201     static var previews: some View {
    202         VStack {
    203             LongformPreview(state: test_damus_state, ev: test_longform_event.event, options: [])
    204 
    205             LongformPreview(state: test_damus_state, ev: test_longform_event.event, options: [.wide])
    206         }
    207         .frame(height: 400)
    208     }
    209 }