commit b2b790a969560971fd99f524d2749d75ee27a861
parent 907f0d236f93eae27b896ed8459806969bf9ca51
Author: William Casarin <jb55@jb55.com>
Date: Tue, 10 Jan 2023 08:12:04 -0800
Reactions View
Changelog-Added: Added Reactions View
Diffstat:
13 files changed, 450 insertions(+), 221 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -116,8 +116,12 @@
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */; };
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838529656C8B00DC99E7 /* NIP05.swift */; };
+ 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */; };
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */; };
4CB8838D296F710400DC99E7 /* Reposted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838C296F710400DC99E7 /* Reposted.swift */; };
+ 4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838E296F781C00DC99E7 /* ReactionsView.swift */; };
+ 4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88392296F798300DC99E7 /* ReactionsModel.swift */; };
+ 4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */; };
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
@@ -310,8 +314,12 @@
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = "<group>"; };
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRelaysView.swift; sourceTree = "<group>"; };
4CB8838529656C8B00DC99E7 /* NIP05.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05.swift; sourceTree = "<group>"; };
+ 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailBar.swift; sourceTree = "<group>"; };
4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05Badge.swift; sourceTree = "<group>"; };
4CB8838C296F710400DC99E7 /* Reposted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reposted.swift; sourceTree = "<group>"; };
+ 4CB8838E296F781C00DC99E7 /* ReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsView.swift; sourceTree = "<group>"; };
+ 4CB88392296F798300DC99E7 /* ReactionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsModel.swift; sourceTree = "<group>"; };
+ 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = "<group>"; };
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
@@ -469,6 +477,7 @@
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */,
BA693073295D649800ADDB87 /* UserSettingsStore.swift */,
4FE60CDC295E1C5E00105A1F /* Wallet.swift */,
+ 4CB88392296F798300DC99E7 /* ReactionsModel.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -476,6 +485,8 @@
4C75EFA227FA576C0006080F /* Views */ = {
isa = PBXGroup;
children = (
+ 4CB88394296F7F8100DC99E7 /* Reactions */,
+ 4CB88387296AF97C00DC99E7 /* ActionBar */,
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
4C363A8728236948006E126D /* BlocksView.swift */,
4C285C8128385570008A31F1 /* CarouselView.swift */,
@@ -488,7 +499,6 @@
4C216F33286F5ACD00040376 /* DMView.swift */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
3169CAE4294E699400EE4006 /* Empty Views */,
- 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
4C75EFB82804A2740006080F /* EventView.swift */,
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
@@ -520,6 +530,7 @@
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
647D9A8C2968520300A295DE /* SideMenuView.swift */,
+ 4CB8838E296F781C00DC99E7 /* ReactionsView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -565,6 +576,23 @@
path = Util;
sourceTree = "<group>";
};
+ 4CB88387296AF97C00DC99E7 /* ActionBar */ = {
+ isa = PBXGroup;
+ children = (
+ 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
+ 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */,
+ );
+ path = ActionBar;
+ sourceTree = "<group>";
+ };
+ 4CB88394296F7F8100DC99E7 /* Reactions */ = {
+ isa = PBXGroup;
+ children = (
+ 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */,
+ );
+ path = Reactions;
+ sourceTree = "<group>";
+ };
4CE4F9DF285287A000C00DD9 /* Components */ = {
isa = PBXGroup;
children = (
@@ -840,6 +868,7 @@
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
+ 4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */,
4C649844285A952100EAE2B3 /* LocalUserConfig.swift in Sources */,
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
@@ -888,6 +917,8 @@
4C3EA66528FF5F6800C48A62 /* mem.c in Sources */,
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
+ 4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
+ 4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */,
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
@@ -910,6 +941,7 @@
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
+ 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -216,7 +216,7 @@ struct ContentView: View {
}
}
}
-
+
var body: some View {
VStack(alignment: .leading, spacing: 0) {
if let damus = self.damus_state {
@@ -229,9 +229,6 @@ struct ContentView: View {
Button {
isSideBarOpened.toggle()
} label: {
- let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!)
- let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey)
-
if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture {
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture)
} else {
diff --git a/damus/Models/ActionBarModel.swift b/damus/Models/ActionBarModel.swift
@@ -16,6 +16,10 @@ class ActionBarModel: ObservableObject {
@Published var boosts: Int
@Published var tips: Int64
+ static func empty() -> ActionBarModel {
+ return ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
+ }
+
init(likes: Int, boosts: Int, tips: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_tip: NostrEvent?) {
self.likes = likes
self.boosts = boosts
diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift
@@ -73,31 +73,30 @@ class FollowersModel: ObservableObject {
}
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
- switch ev {
- case .ws_event:
- break
- case .nostr_event(let nev):
- switch nev {
- case .event(let sub_id, let ev):
- guard sub_id == self.sub_id || sub_id == self.profiles_id else {
- return
- }
-
- if ev.known_kind == .contacts {
- handle_contact_event(ev)
- } else if ev.known_kind == .metadata {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
- }
-
- case .notice(let msg):
- print("followingmodel notice: \(msg)")
-
- case .eose(let sub_id):
- if sub_id == self.sub_id {
- load_profiles(relay_id: relay_id)
- } else if sub_id == self.profiles_id {
- damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id])
- }
+ guard case .nostr_event(let nev) = ev else {
+ return
+ }
+
+ switch nev {
+ case .event(let sub_id, let ev):
+ guard sub_id == self.sub_id || sub_id == self.profiles_id else {
+ return
+ }
+
+ if ev.known_kind == .contacts {
+ handle_contact_event(ev)
+ } else if ev.known_kind == .metadata {
+ process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ }
+
+ case .notice(let msg):
+ print("followingmodel notice: \(msg)")
+
+ case .eose(let sub_id):
+ if sub_id == self.sub_id {
+ load_profiles(relay_id: relay_id)
+ } else if sub_id == self.profiles_id {
+ damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id])
}
}
}
diff --git a/damus/Models/ReactionsModel.swift b/damus/Models/ReactionsModel.swift
@@ -0,0 +1,75 @@
+//
+// LikesModel.swift
+// damus
+//
+// Created by William Casarin on 2023-01-11.
+//
+
+import Foundation
+
+
+class ReactionsModel: ObservableObject {
+ let state: DamusState
+ let target: String
+ let sub_id: String
+ @Published var reactions: [NostrEvent]
+
+ init (state: DamusState, target: String) {
+ self.state = state
+ self.target = target
+ self.sub_id = UUID().description
+ self.reactions = []
+ }
+
+ func get_filter() -> NostrFilter {
+ var filter = NostrFilter.filter_kinds([7])
+ filter.referenced_ids = [target]
+ filter.limit = 500
+ return filter
+ }
+
+ func subscribe() {
+ let filter = get_filter()
+ let filters = [filter]
+ self.state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_nostr_event)
+ }
+
+ func unsubscribe() {
+ self.state.pool.unsubscribe(sub_id: sub_id)
+ }
+
+ func handle_event(relay_id: String, ev: NostrEvent) {
+ guard ev.kind == 7 else {
+ return
+ }
+
+ guard let reacted_to = last_etag(tags: ev.tags) else {
+ return
+ }
+
+ guard reacted_to == self.target else {
+ return
+ }
+
+ if insert_uniq_sorted_event(events: &self.reactions, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) {
+ objectWillChange.send()
+ }
+ }
+
+ func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) {
+ guard case .nostr_event(let nev) = ev else {
+ return
+ }
+
+ switch nev {
+ case .event(_, let ev):
+ handle_event(relay_id: relay_id, ev: ev)
+
+ case .notice(_):
+ break
+ case .eose(_):
+ load_profiles(profiles_subid: UUID().description, relay_id: relay_id, events: reactions, damus_state: state)
+ break
+ }
+ }
+}
diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift
@@ -772,6 +772,15 @@ func validate_event(ev: NostrEvent) -> ValidationResult {
return ok ? .ok : .bad_sig
}
+func last_etag(tags: [[String]]) -> String? {
+ var e: String? = nil
+ for tag in tags {
+ if tag.count >= 2 && tag[0] == "e" {
+ e = tag[1]
+ }
+ }
+ return e
+}
func inner_event_or_self(ev: NostrEvent) -> NostrEvent {
guard let inner_ev = ev.inner_event else {
diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift
@@ -0,0 +1,183 @@
+//
+// EventActionBar.swift
+// damus
+//
+// Created by William Casarin on 2022-04-16.
+//
+
+import SwiftUI
+import UIKit
+
+enum ActionBarSheet: Identifiable {
+ case reply
+
+ var id: String {
+ switch self {
+ case .reply: return "reply"
+ }
+ }
+}
+
+struct EventActionBar: View {
+ let damus_state: DamusState
+ let event: NostrEvent
+ let generator = UIImpactFeedbackGenerator(style: .medium)
+ @State var sheet: ActionBarSheet? = nil
+ @State var confirm_boost: Bool = false
+ @State var show_share_sheet: Bool = false
+ @StateObject var bar: ActionBarModel
+
+ var body: some View {
+ HStack {
+ if damus_state.keypair.privkey != nil {
+ EventActionButton(img: "bubble.left", col: nil) {
+ notify(.reply, event)
+ }
+ .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
+ }
+
+ HStack(alignment: .bottom) {
+ Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
+ .font(.footnote.weight(.medium))
+ .foregroundColor(bar.boosted ? Color.green : Color.gray)
+
+ EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
+ if bar.boosted {
+ notify(.delete, bar.our_boost)
+ } else {
+ self.confirm_boost = true
+ }
+ }
+ }
+ .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
+
+ HStack(alignment: .bottom) {
+ Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
+ .font(.footnote.weight(.medium))
+ .foregroundColor(bar.liked ? Color.orange : Color.gray)
+
+ LikeButton(liked: bar.liked) {
+ if bar.liked {
+ notify(.delete, bar.our_like)
+ } else {
+ send_like()
+ }
+ }
+ }
+ .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
+
+ EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
+ show_share_sheet = true
+ }
+ .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
+
+ /*
+ HStack(alignment: .bottom) {
+ Text("\(bar.tips > 0 ? "\(bar.tips)" : "")")
+ .font(.footnote)
+ .foregroundColor(bar.tipped ? Color.orange : Color.gray)
+
+ EventActionButton(img: bar.tipped ? "bitcoinsign.circle.fill" : "bitcoinsign.circle", col: bar.tipped ? Color.orange : nil) {
+ if bar.tipped {
+ //notify(.delete, bar.our_tip)
+ } else {
+ //notify(.boost, event)
+ }
+ }
+ }
+ */
+ }
+ .sheet(isPresented: $show_share_sheet) {
+ if let note_id = bech32_note_id(event.id) {
+ if let url = URL(string: "https://damus.io/" + note_id) {
+ ShareSheet(activityItems: [url])
+ }
+ }
+ }
+ .alert(NSLocalizedString("Boost", comment: "Title of alert for confirming to boost a post."), isPresented: $confirm_boost) {
+ Button("Cancel") {
+ confirm_boost = false
+ }
+ Button(NSLocalizedString("Boost", comment: "Button to confirm boosting a post.")) {
+ send_boost()
+ }
+ } message: {
+ Text("Are you sure you want to boost this post?", comment: "Alert message to ask if user wants to boost a post.")
+ }
+ .onReceive(handle_notify(.liked)) { n in
+ let liked = n.object as! Counted
+ if liked.id != event.id {
+ return
+ }
+ self.bar.likes = liked.total
+ if liked.event.pubkey == damus_state.keypair.pubkey {
+ self.bar.our_like = liked.event
+ }
+ }
+ }
+
+ func send_boost() {
+ guard let privkey = self.damus_state.keypair.privkey else {
+ return
+ }
+
+ let boost = make_boost_event(pubkey: damus_state.keypair.pubkey, privkey: privkey, boosted: self.event)
+
+ self.bar.our_boost = boost
+
+ damus_state.pool.send(.event(boost))
+ }
+
+ func send_like() {
+ guard let privkey = damus_state.keypair.privkey else {
+ return
+ }
+
+ let like_ev = make_like_event(pubkey: damus_state.pubkey, privkey: privkey, liked: event)
+
+ self.bar.our_like = like_ev
+
+ generator.impactOccurred()
+
+ damus_state.pool.send(.event(like_ev))
+ }
+}
+
+
+func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View {
+ Button(action: action) {
+ Label(" ", systemImage: img)
+ .font(.footnote.weight(.medium))
+ .foregroundColor(col == nil ? Color.gray : col!)
+ }
+}
+
+struct LikeButton: View {
+ let liked: Bool
+ let action: () -> ()
+
+ @Environment(\.colorScheme) var colorScheme
+
+ var body: some View {
+ Button(action: action) {
+ if liked {
+ Text("🤙", comment: "Button with emoji to like an event.")
+ } else {
+ Image("shaka")
+ .renderingMode(.template)
+ .foregroundColor(.gray)
+ }
+ }
+ }
+}
+
+
+struct EventActionBar_Previews: PreviewProvider {
+ static var previews: some View {
+ let pk = "pubkey"
+ let ds = test_damus_state()
+ let bar = ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
+ let ev = NostrEvent(content: "hi", pubkey: pk)
+ EventActionBar(damus_state: ds, event: ev, bar: bar)
+ }
+}
diff --git a/damus/Views/ActionBar/EventDetailBar.swift b/damus/Views/ActionBar/EventDetailBar.swift
@@ -0,0 +1,39 @@
+//
+// EventDetailBar.swift
+// damus
+//
+// Created by William Casarin on 2023-01-08.
+//
+
+import SwiftUI
+
+struct EventDetailBar: View {
+ let state: DamusState
+ let target: String
+ @StateObject var bar: ActionBarModel
+
+ var body: some View {
+ HStack {
+ Text("\(bar.boosts)")
+ .font(.body.bold())
+ Text("Reposts")
+
+ NavigationLink(destination: ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: target))) {
+ Text("\(bar.likes)")
+ .font(.body.bold())
+ Text("Reactions")
+ }
+ .buttonStyle(PlainButtonStyle())
+
+ Text("\(bar.tips)")
+ .font(.body.bold())
+ Text("Tips")
+ }
+ }
+}
+
+struct EventDetailBar_Previews: PreviewProvider {
+ static var previews: some View {
+ EventDetailBar(state: test_damus_state(), target: "", bar: ActionBarModel.empty())
+ }
+}
diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift
@@ -1,191 +0,0 @@
-//
-// EventActionBar.swift
-// damus
-//
-// Created by William Casarin on 2022-04-16.
-//
-
-import SwiftUI
-import UIKit
-
-enum ActionBarSheet: Identifiable {
- case reply
-
- var id: String {
- switch self {
- case .reply: return "reply"
- }
- }
-}
-
-struct EventActionBar: View {
- let damus_state: DamusState
- let event: NostrEvent
- let generator = UIImpactFeedbackGenerator(style: .medium)
- @State var sheet: ActionBarSheet? = nil
- @State var confirm_boost: Bool = false
- @State var show_share_sheet: Bool = false
- @StateObject var bar: ActionBarModel
-
- var body: some View {
- HStack {
- /*
- EventActionButton(img: "square.and.arrow.up") {
- print("share")
- }
-
- Spacer()
-
- */
- if damus_state.keypair.privkey != nil {
- EventActionButton(img: "bubble.left", col: nil) {
- notify(.reply, event)
- }
- .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
- }
-
- HStack(alignment: .bottom) {
- Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
- .font(.footnote.weight(.medium))
- .foregroundColor(bar.boosted ? Color.green : Color.gray)
-
- EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
- if bar.boosted {
- notify(.delete, bar.our_boost)
- } else {
- self.confirm_boost = true
- }
- }
- }
- .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
-
- HStack(alignment: .bottom) {
- Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
- .font(.footnote.weight(.medium))
- .foregroundColor(bar.liked ? Color.orange : Color.gray)
-
- LikeButton(liked: bar.liked) {
- if bar.liked {
- notify(.delete, bar.our_like)
- } else {
- send_like()
- }
- }
- }
- .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
-
- EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
- show_share_sheet = true
- }
- .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
-
- /*
- HStack(alignment: .bottom) {
- Text("\(bar.tips > 0 ? "\(bar.tips)" : "")")
- .font(.footnote)
- .foregroundColor(bar.tipped ? Color.orange : Color.gray)
-
- EventActionButton(img: bar.tipped ? "bitcoinsign.circle.fill" : "bitcoinsign.circle", col: bar.tipped ? Color.orange : nil) {
- if bar.tipped {
- //notify(.delete, bar.our_tip)
- } else {
- //notify(.boost, event)
- }
- }
- }
- */
- }
- .sheet(isPresented: $show_share_sheet) {
- if let note_id = bech32_note_id(event.id) {
- if let url = URL(string: "https://damus.io/" + note_id) {
- ShareSheet(activityItems: [url])
- }
- }
- }
- .alert(NSLocalizedString("Boost", comment: "Title of alert for confirming to boost a post."), isPresented: $confirm_boost) {
- Button("Cancel") {
- confirm_boost = false
- }
- Button(NSLocalizedString("Boost", comment: "Button to confirm boosting a post.")) {
- send_boost()
- }
- } message: {
- Text("Are you sure you want to boost this post?", comment: "Alert message to ask if user wants to boost a post.")
- }
- .onReceive(handle_notify(.liked)) { n in
- let liked = n.object as! Counted
- if liked.id != event.id {
- return
- }
- self.bar.likes = liked.total
- if liked.event.pubkey == damus_state.keypair.pubkey {
- self.bar.our_like = liked.event
- }
- }
- }
-
- func send_boost() {
- guard let privkey = self.damus_state.keypair.privkey else {
- return
- }
-
- let boost = make_boost_event(pubkey: damus_state.keypair.pubkey, privkey: privkey, boosted: self.event)
-
- self.bar.our_boost = boost
-
- damus_state.pool.send(.event(boost))
- }
-
- func send_like() {
- guard let privkey = damus_state.keypair.privkey else {
- return
- }
-
- let like_ev = make_like_event(pubkey: damus_state.pubkey, privkey: privkey, liked: event)
-
- self.bar.our_like = like_ev
-
- generator.impactOccurred()
-
- damus_state.pool.send(.event(like_ev))
- }
-}
-
-
-func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View {
- Button(action: action) {
- Label(" ", systemImage: img)
- .font(.footnote.weight(.medium))
- .foregroundColor(col == nil ? Color.gray : col!)
- }
-}
-
-struct LikeButton: View {
- let liked: Bool
- let action: () -> ()
-
- @Environment(\.colorScheme) var colorScheme
-
- var body: some View {
- Button(action: action) {
- if liked {
- Text("🤙", comment: "Button with emoji to like an event.")
- } else {
- Image("shaka")
- .renderingMode(.template)
- .foregroundColor(.gray)
- }
- }
- }
-}
-
-
-struct EventActionBar_Previews: PreviewProvider {
- static var previews: some View {
- let pk = "pubkey"
- let ds = test_damus_state()
- let bar = ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
- let ev = NostrEvent(content: "hi", pubkey: pk)
- EventActionBar(damus_state: ds, event: ev, bar: bar)
- }
-}
diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift
@@ -251,6 +251,13 @@ struct EventView: View {
}
let bar = make_actionbar_model(ev: event, damus: damus)
+
+ if size == .selected {
+ EventDetailBar(state: damus, target: event.id, bar: bar)
+ Divider()
+ .padding([.bottom], 4)
+ }
+
EventActionBar(damus_state: damus, event: event, bar: bar)
}
diff --git a/damus/Views/Reactions/ReactionView.swift b/damus/Views/Reactions/ReactionView.swift
@@ -0,0 +1,36 @@
+//
+// ReactionView.swift
+// damus
+//
+// Created by William Casarin on 2023-01-11.
+//
+
+import SwiftUI
+
+struct ReactionView: View {
+ let damus_state: DamusState
+ let reaction: NostrEvent
+
+ var content: String {
+ if reaction.content == "" || reaction.content == "+" {
+ return "❤️"
+ }
+ return reaction.content
+ }
+
+ var body: some View {
+ HStack {
+ Text(content)
+ .font(Font.headline)
+ .frame(width: 50, height: 50)
+
+ FollowUserView(target: .pubkey(reaction.pubkey), damus_state: damus_state)
+ }
+ }
+}
+
+struct ReactionView_Previews: PreviewProvider {
+ static var previews: some View {
+ ReactionView(damus_state: test_damus_state(), reaction: NostrEvent(id: "", content: "🤙🏼", pubkey: ""))
+ }
+}
diff --git a/damus/Views/ReactionsView.swift b/damus/Views/ReactionsView.swift
@@ -0,0 +1,38 @@
+//
+// ReactionsView.swift
+// damus
+//
+// Created by William Casarin on 2023-01-11.
+//
+
+import SwiftUI
+
+struct ReactionsView: View {
+ let damus_state: DamusState
+ @StateObject var model: ReactionsModel
+
+ var body: some View {
+ ScrollView {
+ LazyVStack {
+ ForEach(model.reactions, id: \.id) { ev in
+ ReactionView(damus_state: damus_state, reaction: ev)
+ }
+ }
+ .padding()
+ }
+ .navigationBarTitle("Reactions")
+ .onAppear {
+ model.subscribe()
+ }
+ .onDisappear {
+ model.unsubscribe()
+ }
+ }
+}
+
+struct ReactionsView_Previews: PreviewProvider {
+ static var previews: some View {
+ let state = test_damus_state()
+ ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: "pubkey"))
+ }
+}
diff --git a/damus/Views/SearchView.swift b/damus/Views/SearchView.swift
@@ -42,6 +42,7 @@ struct SearchView_Previews: PreviewProvider {
let test_state = test_damus_state()
let filter = NostrFilter.filter_hashtag(["bitcoin"])
let pool = test_state.pool
+
let model = SearchModel(pool: pool, search: filter)
SearchView(appstate: test_state, search: model)