damus

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

LongformPreview.swift (7297B)


      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                         Blur()
    126                             .onTapGesture {
    127                                 blur_images = false
    128                             }
    129                     }
    130                 }
    131             }
    132             
    133             Text(event.title ?? NSLocalizedString("Untitled", comment: "Title of longform event if it is untitled."))
    134                 .font(header ? .title : .headline)
    135                 .padding(.horizontal, 10)
    136                 .padding(.top, 5)
    137             
    138             if let summary = event.summary {
    139                 truncatedText(content: CompatibleText(stringLiteral: summary))
    140             }
    141             
    142             if let labels = event.labels {
    143                 ScrollView(.horizontal) {
    144                     HStack {
    145                         ForEach(labels, id: \.self) { label in
    146                             Text(label)
    147                                 .font(.caption)
    148                                 .foregroundColor(.gray)
    149                                 .padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
    150                                 .background(DamusColors.neutral1)
    151                                 .cornerRadius(20)
    152                                 .overlay(
    153                                     RoundedRectangle(cornerRadius: 20)
    154                                         .stroke(DamusColors.neutral3, lineWidth: 1)
    155                                 )
    156                         }
    157                     }
    158                 }
    159                 .scrollIndicators(.hidden)
    160                 .padding(10)
    161             }
    162 
    163             
    164             if case .loaded(let arts) = artifacts.state,
    165                case .longform(let longform) = arts
    166             {
    167                 Words(longform.words).font(.footnote)
    168                     .padding([.horizontal, .bottom], 10)
    169             }
    170         }
    171         .frame(maxWidth: .infinity, alignment: .leading)
    172         .background(DamusColors.neutral3)
    173         .cornerRadius(10)
    174         .overlay(
    175             RoundedRectangle(cornerRadius: 10)
    176                 .stroke(DamusColors.neutral1, lineWidth: 1)
    177         )
    178         .padding(.top, 10)
    179         .onAppear {
    180             blur_images = should_blur_images(settings: state.settings, contacts: state.contacts, ev: event.event, our_pubkey: state.pubkey)
    181         }
    182     }
    183 }
    184 
    185 struct LongformPreview: View {
    186     let state: DamusState
    187     let event: LongformEvent
    188     let options: EventViewOptions
    189 
    190     init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
    191         self.state = state
    192         self.event = LongformEvent.parse(from: ev)
    193         self.options = options.union(.no_mentions)
    194     }
    195 
    196     var body: some View {
    197         EventShell(state: state, event: event.event, options: options) {
    198             LongformPreviewBody(state: state, ev: event, options: options, header: false)
    199         }
    200     }
    201 }
    202 
    203 struct LongformPreview_Previews: PreviewProvider {
    204     static var previews: some View {
    205         VStack {
    206             LongformPreview(state: test_damus_state, ev: test_longform_event.event, options: [])
    207 
    208             LongformPreview(state: test_damus_state, ev: test_longform_event.event, options: [.wide])
    209         }
    210         .frame(height: 400)
    211     }
    212 }