diff --git a/damus/Components/TranslateView.swift b/damus/Components/TranslateView.swift index fc9b8530..b4af37f0 100644 --- a/damus/Components/TranslateView.swift +++ b/damus/Components/TranslateView.swift @@ -128,7 +128,7 @@ struct TranslateView: View { if let translated = translated_note { // Render translated note. let translatedBlocks = event.get_blocks(content: translated) - translated_artifacts = render_blocks(blocks: translatedBlocks, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey) + translated_artifacts = render_blocks(blocks: translatedBlocks, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey, ev: event) } checkingTranslationStatus = false diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift index 5a607a32..ff6f6c5e 100644 --- a/damus/Views/ChatView.swift +++ b/damus/Views/ChatView.swift @@ -113,6 +113,7 @@ struct ChatView: View { event: event, show_images: show_images, size: .normal, + show_time: false, artifacts: .just_content(event.content)) if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { diff --git a/damus/Views/DMChatView.swift b/damus/Views/DMChatView.swift index c2a11a5e..0fbc838a 100644 --- a/damus/Views/DMChatView.swift +++ b/damus/Views/DMChatView.swift @@ -15,45 +15,56 @@ struct DMChatView: View { @State var eventsInView = Set() @State var minDate: String = "" - var Messages: some View { - VStack { - Text(minDate) - .contentShape(RoundedRectangle(cornerRadius: 4.0)) - .foregroundColor(.primary) - .padding(16) + func fillColor() -> Color { + colorScheme == .light ? Color("DamusLightGrey") : Color("DamusDarkGrey") + } - ScrollViewReader { scroller in - ScrollView { - LazyVStack(alignment: .leading) { - ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in - let date = Date.init(timeIntervalSince1970: Double(ev.created_at)) - DMView(event: ev, damus_state: damus_state) - .event_context_menu(ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey) - .onAppear { - eventsInView.insert(ev) - } - .onDisappear { - eventsInView.remove(ev) + func foregroundColor() -> Color { + colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite") + } + + var Messages: some View { + ScrollViewReader { scroller in + ScrollView { + LazyVStack() { + var dates = Set() + ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in + if let date = Calendar.current.startOfDay(for: Date.init(timeIntervalSince1970: Double(ev.created_at))), dates.insert(date).inserted { + Text(date.formatted(date: .abbreviated, time: .omitted)) + .padding([.leading, .trailing], 6.0) + .padding([.top, .bottom], 2.0) + .foregroundColor(.gray) + .background { + RoundedRectangle(cornerRadius: 5.0) + .foregroundColor(fillColor()) } + .font(.callout) } - EndBlock(height: 80) + DMView(event: ev, damus_state: damus_state) + .event_context_menu(ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey) + .onAppear { + eventsInView.insert(ev) + } + .onDisappear { + eventsInView.remove(ev) + } } - .padding(.horizontal) + EndBlock(height: 80) } - .onAppear { + .padding(.horizontal) + } + .onAppear { + scroller.scrollTo("endblock") + }.onChange(of: dms.events.count) { _ in + withAnimation { scroller.scrollTo("endblock") - }.onChange(of: dms.events.count) { _ in - withAnimation { - scroller.scrollTo("endblock") - } - }.onChange(of: eventsInView) { _ in - print("eventsInView \(eventsInView.count)") - let timestamps = eventsInView.map { $0.created_at } - guard let minTimestamp = timestamps.min() else { - return - } - minDate = Date.init(timeIntervalSince1970: Double(minTimestamp)).formatted(date: .abbreviated, time: .omitted) } + }.onChange(of: eventsInView) { _ in + let timestamps = eventsInView.map { $0.created_at } + guard let minTimestamp = timestamps.min() else { + return + } + minDate = Date.init(timeIntervalSince1970: Double(minTimestamp)).formatted(date: .abbreviated, time: .omitted) } } } @@ -165,6 +176,16 @@ struct DMChatView: View { .dismissKeyboardOnTap() VStack { + Text(minDate) + .padding([.leading, .trailing], 6.0) + .padding([.top, .bottom], 2.0) + .foregroundColor(.gray) + .background { + RoundedRectangle(cornerRadius: 5.0) + .foregroundColor(fillColor()) + } + .font(.callout) + Spacer() Footer diff --git a/damus/Views/DMView.swift b/damus/Views/DMView.swift index 691266df..2d43efdd 100644 --- a/damus/Views/DMView.swift +++ b/damus/Views/DMView.swift @@ -23,19 +23,13 @@ struct DMView: View { let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey) - VStack { - NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: .normal, artifacts: .just_content(event.get_content(damus_state.keypair.privkey))) - .foregroundColor(is_ours ? Color.white : Color.primary) - .padding(10) - .background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15)) - .cornerRadius(8.0) - .tint(is_ours ? Color.white : Color.accentColor) - - Text(verbatim: Date.init(timeIntervalSince1970: Double(event.created_at)).formatted(date: .abbreviated, time: .shortened)) - .font(.footnote) - .foregroundColor(.gray) - .frame(maxWidth: .infinity, alignment: is_ours ? .trailing : .leading) - } + NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: .normal, show_time: true, artifacts: .just_content(event.get_content(damus_state.keypair.privkey))) + .foregroundColor(is_ours ? Color.white : Color.primary) + .padding(10) + .background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15)) + .cornerRadius(8.0) + .tint(is_ours ? Color.white : Color.accentColor) + .frame(maxWidth: .infinity, alignment: is_ours ? .trailing : .leading) if !is_ours { Spacer(minLength: UIScreen.main.bounds.width * 0.2) diff --git a/damus/Views/Events/EventBody.swift b/damus/Views/Events/EventBody.swift index 65c5ead6..c0acedcc 100644 --- a/damus/Views/Events/EventBody.swift +++ b/damus/Views/Events/EventBody.swift @@ -23,7 +23,7 @@ struct EventBody: View { let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey, booster_pubkey: nil) - NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, artifacts: .just_content(content)) + NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, show_time: false, artifacts: .just_content(content)) .frame(maxWidth: .infinity, alignment: .leading) } } diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift index cbdc9fd8..96b171df 100644 --- a/damus/Views/NoteContentView.swift +++ b/damus/Views/NoteContentView.swift @@ -27,6 +27,7 @@ struct NoteContentView: View { let event: NostrEvent let show_images: Bool let size: EventViewKind + let show_time: Bool @State var artifacts: NoteArtifacts @State var preview: LinkViewRepresentable? = nil @@ -67,6 +68,14 @@ struct NoteContentView: View { } } } + + if show_time { + Text(verbatim: Date.init(timeIntervalSince1970: Double(event.created_at)).formatted(date: .omitted, time: .shortened)) + .font(.footnote) + .foregroundColor(.gray) + .frame(alignment: .trailing) + .padding([.top], 2) + } } } @@ -129,37 +138,54 @@ struct NoteContentView: View { } } -func hashtag_str(_ htag: String) -> AttributedString { - var attributedString = AttributedString(stringLiteral: "#\(htag)") - attributedString.link = URL(string: "nostr:t:\(htag)") - attributedString.foregroundColor = .purple - return attributedString - } +func hashtag_str(_ htag: String, ev: NostrEvent) -> AttributedString { + var attributedString = AttributedString(stringLiteral: "#\(htag)") + attributedString.link = URL(string: "nostr:t:\(htag)") + + if ev.known_kind == .dm { + attributedString.underlineStyle = .single + } else { + attributedString.foregroundColor = .purple + } -func url_str(_ url: URL) -> AttributedString { - var attributedString = AttributedString(stringLiteral: url.absoluteString) - attributedString.link = url - attributedString.foregroundColor = .purple return attributedString } -func mention_str(_ m: Mention, profiles: Profiles) -> AttributedString { +func url_str(_ url: URL, ev: NostrEvent) -> AttributedString { + var attributedString = AttributedString(stringLiteral: url.absoluteString) + attributedString.link = url + + if ev.known_kind == .dm { + attributedString.underlineStyle = .single + } else { + attributedString.foregroundColor = .purple + } + + return attributedString + } + +func mention_str(_ m: Mention, profiles: Profiles, ev: NostrEvent) -> AttributedString { + var attributedString: AttributedString switch m.type { case .pubkey: let pk = m.ref.ref_id let profile = profiles.lookup(id: pk) let disp = Profile.displayName(profile: profile, pubkey: pk) - var attributedString = AttributedString(stringLiteral: "@\(disp)") + attributedString = AttributedString(stringLiteral: "@\(disp)") attributedString.link = URL(string: "nostr:\(encode_pubkey_uri(m.ref))") - attributedString.foregroundColor = .purple - return attributedString case .event: let bevid = bech32_note_id(m.ref.ref_id) ?? m.ref.ref_id - var attributedString = AttributedString(stringLiteral: "@\(abbrev_pubkey(bevid))") + attributedString = AttributedString(stringLiteral: "@\(abbrev_pubkey(bevid))") attributedString.link = URL(string: "nostr:\(encode_event_id_uri(m.ref))") - attributedString.foregroundColor = .purple - return attributedString } + + if ev.known_kind == .dm { + attributedString.underlineStyle = .single + } else { + attributedString.foregroundColor = .purple + } + + return attributedString } struct NoteContentView_Previews: PreviewProvider { @@ -167,11 +193,10 @@ 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) + NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, size: .normal, show_time: false, artifacts: artifacts) } } - extension View { func translate_button_style() -> some View { return self @@ -194,21 +219,21 @@ struct NoteArtifacts { func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts { let blocks = ev.blocks(privkey) - return render_blocks(blocks: blocks, profiles: profiles, privkey: privkey) + return render_blocks(blocks: blocks, profiles: profiles, privkey: privkey, ev: ev) } -func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?) -> NoteArtifacts { +func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?, ev: NostrEvent) -> NoteArtifacts { var invoices: [Invoice] = [] var img_urls: [URL] = [] var link_urls: [URL] = [] let txt: AttributedString = blocks.reduce("") { str, block in switch block { case .mention(let m): - return str + mention_str(m, profiles: profiles) + return str + mention_str(m, profiles: profiles, ev: ev) case .text(let txt): return str + AttributedString(stringLiteral: txt) case .hashtag(let htag): - return str + hashtag_str(htag) + return str + hashtag_str(htag, ev: ev) case .invoice(let invoice): invoices.append(invoice) return str @@ -220,7 +245,7 @@ func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?) -> Not return str } else { link_urls.append(url) - return str + url_str(url) + return str + url_str(url, ev: ev) } } }