Compare commits
6 Commits
tyiu/find-
...
tyiu/refac
| Author | SHA1 | Date | |
|---|---|---|---|
|
6cfb9f7c75
|
|||
|
|
1b161fefd0 | ||
|
|
0b9a274e67 | ||
|
|
2bbbb5db65 | ||
|
|
bffa42a13a | ||
|
|
8097cfdfb8 |
@@ -53,6 +53,8 @@
|
|||||||
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
|
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
|
||||||
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
|
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
|
||||||
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F372871EDE300040376 /* DirectMessageModel.swift */; };
|
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F372871EDE300040376 /* DirectMessageModel.swift */; };
|
||||||
|
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */; };
|
||||||
|
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2859612A12A7F0004746F7 /* GoldSupportGradient.swift */; };
|
||||||
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8128385570008A31F1 /* CarouselView.swift */; };
|
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8128385570008A31F1 /* CarouselView.swift */; };
|
||||||
4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8328385690008A31F1 /* CreateAccountView.swift */; };
|
4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8328385690008A31F1 /* CreateAccountView.swift */; };
|
||||||
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C85283892E7008A31F1 /* CreateAccountModel.swift */; };
|
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C85283892E7008A31F1 /* CreateAccountModel.swift */; };
|
||||||
@@ -444,6 +446,8 @@
|
|||||||
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
|
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
|
||||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
|
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
|
||||||
4C216F372871EDE300040376 /* DirectMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessageModel.swift; sourceTree = "<group>"; };
|
4C216F372871EDE300040376 /* DirectMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessageModel.swift; sourceTree = "<group>"; };
|
||||||
|
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupporterBadge.swift; sourceTree = "<group>"; };
|
||||||
|
4C2859612A12A7F0004746F7 /* GoldSupportGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoldSupportGradient.swift; sourceTree = "<group>"; };
|
||||||
4C285C8128385570008A31F1 /* CarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselView.swift; sourceTree = "<group>"; };
|
4C285C8128385570008A31F1 /* CarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselView.swift; sourceTree = "<group>"; };
|
||||||
4C285C8328385690008A31F1 /* CreateAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountView.swift; sourceTree = "<group>"; };
|
4C285C8328385690008A31F1 /* CreateAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountView.swift; sourceTree = "<group>"; };
|
||||||
4C285C85283892E7008A31F1 /* CreateAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountModel.swift; sourceTree = "<group>"; };
|
4C285C85283892E7008A31F1 /* CreateAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountModel.swift; sourceTree = "<group>"; };
|
||||||
@@ -1041,6 +1045,7 @@
|
|||||||
children = (
|
children = (
|
||||||
4C7D09712A0AEF5E00943473 /* DamusGradient.swift */,
|
4C7D09712A0AEF5E00943473 /* DamusGradient.swift */,
|
||||||
4C7D09732A0AEF9000943473 /* AlbyGradient.swift */,
|
4C7D09732A0AEF9000943473 /* AlbyGradient.swift */,
|
||||||
|
4C2859612A12A7F0004746F7 /* GoldSupportGradient.swift */,
|
||||||
);
|
);
|
||||||
path = Gradients;
|
path = Gradients;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1218,6 +1223,7 @@
|
|||||||
4CE4F0F729DB7399005914DB /* ThiccDivider.swift */,
|
4CE4F0F729DB7399005914DB /* ThiccDivider.swift */,
|
||||||
4C1A9A2229DDDB8100516EAC /* IconLabel.swift */,
|
4C1A9A2229DDDB8100516EAC /* IconLabel.swift */,
|
||||||
4C8D00C929DF80350036AF10 /* TruncatedText.swift */,
|
4C8D00C929DF80350036AF10 /* TruncatedText.swift */,
|
||||||
|
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1730,6 +1736,7 @@
|
|||||||
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
||||||
F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */,
|
F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */,
|
||||||
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
|
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
|
||||||
|
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
||||||
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */,
|
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */,
|
||||||
@@ -1837,6 +1844,7 @@
|
|||||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
||||||
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
||||||
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
||||||
|
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
29
damus/Components/Gradients/GoldSupportGradient.swift
Normal file
29
damus/Components/Gradients/GoldSupportGradient.swift
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// GoldSupportGradient.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-05-15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
fileprivate let gold_grad_c1 = hex_col(r: 226, g: 168, b: 0)
|
||||||
|
fileprivate let gold_grad_c2 = hex_col(r: 249, g: 243, b: 100)
|
||||||
|
|
||||||
|
fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1]
|
||||||
|
|
||||||
|
let GoldGradient: LinearGradient =
|
||||||
|
LinearGradient(colors: gold_grad, startPoint: .bottomLeading, endPoint: .topTrailing)
|
||||||
|
|
||||||
|
struct GoldGradientView: View {
|
||||||
|
var body: some View {
|
||||||
|
GoldGradient
|
||||||
|
.edgesIgnoringSafeArea([.top,.bottom])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GoldGradientView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
GoldGradientView()
|
||||||
|
}
|
||||||
|
}
|
||||||
73
damus/Components/SupporterBadge.swift
Normal file
73
damus/Components/SupporterBadge.swift
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// SupporterBadge.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-05-15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SupporterBadge: View {
|
||||||
|
let percent: Int
|
||||||
|
|
||||||
|
let size: CGFloat = 17
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if percent < 100 {
|
||||||
|
Image("star.fill")
|
||||||
|
.resizable()
|
||||||
|
.frame(width:size, height:size)
|
||||||
|
.foregroundColor(support_level_color(percent))
|
||||||
|
} else {
|
||||||
|
Image("star.fill")
|
||||||
|
.resizable()
|
||||||
|
.frame(width:size, height:size)
|
||||||
|
.foregroundStyle(GoldGradient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func support_level_color(_ percent: Int) -> Color {
|
||||||
|
if percent == 0 {
|
||||||
|
return .gray
|
||||||
|
}
|
||||||
|
|
||||||
|
let percent_f = Double(percent) / 100.0
|
||||||
|
let cutoff = 0.5
|
||||||
|
let h = cutoff + (percent_f * cutoff); // Hue (note 0.2 = Green, see huge chart below)
|
||||||
|
let s = 0.9; // Saturation
|
||||||
|
let b = 0.9; // Brightness
|
||||||
|
|
||||||
|
return Color(hue: h, saturation: s, brightness: b)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SupporterBadge_Previews: PreviewProvider {
|
||||||
|
static func Level(_ p: Int) -> some View {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
SupporterBadge(percent: p)
|
||||||
|
.frame(width: 50)
|
||||||
|
Text("\(p)")
|
||||||
|
.frame(width: 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Level(1)
|
||||||
|
Level(10)
|
||||||
|
Level(20)
|
||||||
|
Level(30)
|
||||||
|
Level(40)
|
||||||
|
Level(50)
|
||||||
|
}
|
||||||
|
Level(60)
|
||||||
|
Level(70)
|
||||||
|
Level(80)
|
||||||
|
Level(90)
|
||||||
|
Level(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -258,14 +258,18 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let nwc_req = nwc_pay(url: nwc_state.url, pool: damus_state.pool, post: damus_state.postbox, invoice: inv, on_flush: .once({ pe in
|
var flusher: OnFlush? = nil
|
||||||
|
// Don't donate on custom zaps
|
||||||
// send donation zap when the pending zap is flushed, this allows user to cancel and not send a donation
|
if !is_custom && damus_state.settings.donation_percent > 0 {
|
||||||
Task.init { @MainActor in
|
flusher = .once({ pe in
|
||||||
await send_donation_zap(pool: damus_state.pool, postbox: damus_state.postbox, nwc: nwc_state.url, percent: damus_state.settings.donation_percent, base_msats: amount_msat)
|
// send donation zap when the pending zap is flushed, this allows user to cancel and not send a donation
|
||||||
}
|
Task.init { @MainActor in
|
||||||
|
await send_donation_zap(pool: damus_state.pool, postbox: damus_state.postbox, nwc: nwc_state.url, percent: damus_state.settings.donation_percent, base_msats: amount_msat)
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let nwc_req = nwc_pay(url: nwc_state.url, pool: damus_state.pool, post: damus_state.postbox, invoice: inv, on_flush: flusher)
|
||||||
|
|
||||||
guard let nwc_req, case .nwc(let pzap_state) = pending_zap_state else {
|
guard let nwc_req, case .nwc(let pzap_state) = pending_zap_state else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ struct ContentView: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
ds.postbox.send(ev)
|
ds.postbox.send(ev)
|
||||||
if let profile = ds.profiles.profiles[ev.pubkey] {
|
if let profile = ds.profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||||
ds.postbox.send(profile.event)
|
ds.postbox.send(profile.event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ func process_metadata_profile(our_pubkey: String, profiles: Profiles, profile: P
|
|||||||
var old_nip05: String? = nil
|
var old_nip05: String? = nil
|
||||||
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||||
old_nip05 = mprof.profile.nip05
|
old_nip05 = mprof.profile.nip05
|
||||||
if mprof.timestamp > ev.created_at {
|
if mprof.event.created_at > ev.created_at {
|
||||||
// skip if we already have an newer profile
|
// skip if we already have an newer profile
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -752,7 +752,7 @@ func process_metadata_profile(our_pubkey: String, profiles: Profiles, profile: P
|
|||||||
print("validated nip05 for '\(nip05)'")
|
print("validated nip05 for '\(nip05)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
Task { @MainActor in
|
||||||
profiles.validated[ev.pubkey] = validated
|
profiles.validated[ev.pubkey] = validated
|
||||||
profiles.nip05_pubkey[nip05] = ev.pubkey
|
profiles.nip05_pubkey[nip05] = ev.pubkey
|
||||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||||
@@ -1203,7 +1203,7 @@ func create_local_notification(profiles: Profiles, notify: LocalNotification) {
|
|||||||
title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
|
title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
|
||||||
identifier = "myBoostNotification"
|
identifier = "myBoostNotification"
|
||||||
case .like:
|
case .like:
|
||||||
title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, notify.event.content)
|
title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, to_reaction_emoji(ev: notify.event) ?? "")
|
||||||
identifier = "myLikeNotification"
|
identifier = "myLikeNotification"
|
||||||
case .dm:
|
case .dm:
|
||||||
title = displayName
|
title = displayName
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ func format_msats_abbrev(_ msats: Int64) -> String {
|
|||||||
formatter.positiveSuffix = "m"
|
formatter.positiveSuffix = "m"
|
||||||
formatter.positivePrefix = ""
|
formatter.positivePrefix = ""
|
||||||
formatter.minimumFractionDigits = 0
|
formatter.minimumFractionDigits = 0
|
||||||
formatter.maximumFractionDigits = 2
|
formatter.maximumFractionDigits = 3
|
||||||
formatter.roundingMode = .down
|
formatter.roundingMode = .down
|
||||||
formatter.roundingIncrement = 0.1
|
formatter.roundingIncrement = 0.1
|
||||||
formatter.multiplier = 1
|
formatter.multiplier = 1
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ enum WalletConnectState {
|
|||||||
class WalletModel: ObservableObject {
|
class WalletModel: ObservableObject {
|
||||||
var settings: UserSettingsStore
|
var settings: UserSettingsStore
|
||||||
private(set) var previous_state: WalletConnectState
|
private(set) var previous_state: WalletConnectState
|
||||||
|
var inital_percent: Int
|
||||||
|
|
||||||
@Published private(set) var connect_state: WalletConnectState
|
@Published private(set) var connect_state: WalletConnectState
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ class WalletModel: ObservableObject {
|
|||||||
self.connect_state = state
|
self.connect_state = state
|
||||||
self.previous_state = .none
|
self.previous_state = .none
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
|
self.inital_percent = settings.donation_percent
|
||||||
}
|
}
|
||||||
|
|
||||||
init(settings: UserSettingsStore) {
|
init(settings: UserSettingsStore) {
|
||||||
@@ -35,6 +37,7 @@ class WalletModel: ObservableObject {
|
|||||||
self.previous_state = .none
|
self.previous_state = .none
|
||||||
self.connect_state = .none
|
self.connect_state = .none
|
||||||
}
|
}
|
||||||
|
self.inital_percent = settings.donation_percent
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancel() {
|
func cancel() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
class Profile: Codable {
|
class Profile: Codable {
|
||||||
var value: [String: AnyCodable]
|
var value: [String: AnyCodable]
|
||||||
|
|
||||||
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) {
|
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?, damus_donation: Int?) {
|
||||||
self.value = [:]
|
self.value = [:]
|
||||||
self.name = name
|
self.name = name
|
||||||
self.display_name = display_name
|
self.display_name = display_name
|
||||||
@@ -21,12 +21,17 @@ class Profile: Codable {
|
|||||||
self.lud06 = lud06
|
self.lud06 = lud06
|
||||||
self.lud16 = lud16
|
self.lud16 = lud16
|
||||||
self.nip05 = nip05
|
self.nip05 = nip05
|
||||||
|
self.damus_donation = damus_donation
|
||||||
}
|
}
|
||||||
|
|
||||||
private func str(_ str: String) -> String? {
|
private func str(_ str: String) -> String? {
|
||||||
return get_val(str)
|
return get_val(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func int(_ key: String) -> Int? {
|
||||||
|
return get_val(key)
|
||||||
|
}
|
||||||
|
|
||||||
private func get_val<T>(_ v: String) -> T? {
|
private func get_val<T>(_ v: String) -> T? {
|
||||||
guard let val = self.value[v] else{
|
guard let val = self.value[v] else{
|
||||||
return nil
|
return nil
|
||||||
@@ -52,6 +57,10 @@ class Profile: Codable {
|
|||||||
set_val(key, val)
|
set_val(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func set_int(_ key: String, _ val: Int?) {
|
||||||
|
set_val(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
var reactions: Bool? {
|
var reactions: Bool? {
|
||||||
get { return get_val("reactions"); }
|
get { return get_val("reactions"); }
|
||||||
set(s) { set_val("reactions", s) }
|
set(s) { set_val("reactions", s) }
|
||||||
@@ -77,6 +86,11 @@ class Profile: Codable {
|
|||||||
set(s) { set_str("about", s) }
|
set(s) { set_str("about", s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var damus_donation: Int? {
|
||||||
|
get { return int("damus_donation"); }
|
||||||
|
set(s) { set_int("damus_donation", s) }
|
||||||
|
}
|
||||||
|
|
||||||
var picture: String? {
|
var picture: String? {
|
||||||
get { return str("picture"); }
|
get { return str("picture"); }
|
||||||
set(s) { set_str("picture", s) }
|
set(s) { set_str("picture", s) }
|
||||||
@@ -180,7 +194,7 @@ class Profile: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func make_test_profile() -> Profile {
|
func make_test_profile() -> Profile {
|
||||||
return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200", website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com")
|
return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200", website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com", damus_donation: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_ln_url(_ str: String?) -> URL? {
|
func make_ln_url(_ str: String?) -> URL? {
|
||||||
|
|||||||
@@ -492,11 +492,11 @@ func make_boost_event(pubkey: String, privkey: String, boosted: NostrEvent) -> N
|
|||||||
return ev
|
return ev
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent {
|
func make_like_event(pubkey: String, privkey: String, liked: NostrEvent, content: String = "🤙") -> NostrEvent {
|
||||||
var tags: [[String]] = liked.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") }
|
var tags: [[String]] = liked.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") }
|
||||||
tags.append(["e", liked.id])
|
tags.append(["e", liked.id])
|
||||||
tags.append(["p", liked.pubkey])
|
tags.append(["p", liked.pubkey])
|
||||||
let ev = NostrEvent(content: "🤙", pubkey: pubkey, kind: 7, tags: tags)
|
let ev = NostrEvent(content: content, pubkey: pubkey, kind: 7, tags: tags)
|
||||||
ev.calculate_id()
|
ev.calculate_id()
|
||||||
ev.sign(privkey: privkey)
|
ev.sign(privkey: privkey)
|
||||||
|
|
||||||
@@ -966,6 +966,28 @@ func first_eref_mention(ev: NostrEvent, privkey: String?) -> Mention? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transforms a `NostrEvent` of known kind `NostrKind.like`to a human-readable emoji.
|
||||||
|
If the known kind is not a `NostrKind.like`, it will return `nil`.
|
||||||
|
If the event content is an empty string or `+`, it will map that to a heart ❤️ emoji.
|
||||||
|
If the event content is a "-", it will map that to a dislike 👎 emoji.
|
||||||
|
Otherwise, it will return the event content at face value without transforming it.
|
||||||
|
*/
|
||||||
|
func to_reaction_emoji(ev: NostrEvent) -> String? {
|
||||||
|
guard ev.known_kind == NostrKind.like else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ev.content {
|
||||||
|
case "", "+":
|
||||||
|
return "❤️"
|
||||||
|
case "-":
|
||||||
|
return "👎"
|
||||||
|
default:
|
||||||
|
return ev.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension [ReferencedId] {
|
extension [ReferencedId] {
|
||||||
var pRefs: [ReferencedId] {
|
var pRefs: [ReferencedId] {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Profiles {
|
|||||||
qos: .userInteractive,
|
qos: .userInteractive,
|
||||||
attributes: .concurrent)
|
attributes: .concurrent)
|
||||||
|
|
||||||
var profiles: [String: TimestampedProfile] = [:]
|
private var profiles: [String: TimestampedProfile] = [:]
|
||||||
var validated: [String: NIP05] = [:]
|
var validated: [String: NIP05] = [:]
|
||||||
var nip05_pubkey: [String: String] = [:]
|
var nip05_pubkey: [String: String] = [:]
|
||||||
var zappers: [String: String] = [:]
|
var zappers: [String: String] = [:]
|
||||||
@@ -26,6 +26,12 @@ class Profiles {
|
|||||||
return validated[pk]
|
return validated[pk]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enumerated() -> EnumeratedSequence<[String: TimestampedProfile]> {
|
||||||
|
return queue.sync {
|
||||||
|
return profiles.enumerated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func lookup_zapper(pubkey: String) -> String? {
|
func lookup_zapper(pubkey: String) -> String? {
|
||||||
if let zapper = zappers[pubkey] {
|
if let zapper = zappers[pubkey] {
|
||||||
return zapper
|
return zapper
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class PostedEvent {
|
|||||||
self.on_flush = on_flush
|
self.on_flush = on_flush
|
||||||
self.flushed_once = false
|
self.flushed_once = false
|
||||||
self.remaining = remaining.map {
|
self.remaining = remaining.map {
|
||||||
Relayer(relay: $0, attempts: 0, retry_after: 2.0)
|
Relayer(relay: $0, attempts: 0, retry_after: 10.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ struct WalletConnectURL: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init?(str: String) {
|
init?(str: String) {
|
||||||
guard let url = URL(string: str), url.scheme == "nostrwalletconnect",
|
guard let url = URL(string: str),
|
||||||
|
url.scheme == "nostrwalletconnect" || url.scheme == "nostr+walletconnect",
|
||||||
let pk = url.host, pk.utf8.count == 64,
|
let pk = url.host, pk.utf8.count == 64,
|
||||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
let items = components.queryItems,
|
let items = components.queryItems,
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func search_users_for_autocomplete(profiles: Profiles, tags: [[String]], search
|
|||||||
}
|
}
|
||||||
|
|
||||||
// search profile cache as well
|
// search profile cache as well
|
||||||
for tup in profiles.profiles.enumerated() {
|
for tup in profiles.enumerated() {
|
||||||
let pk = tup.element.key
|
let pk = tup.element.key
|
||||||
let prof = tup.element.value.profile
|
let prof = tup.element.value.profile
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct EventProfileName: View {
|
|||||||
|
|
||||||
@State var display_name: DisplayName?
|
@State var display_name: DisplayName?
|
||||||
@State var nip05: NIP05?
|
@State var nip05: NIP05?
|
||||||
|
@State var donation: Int?
|
||||||
|
|
||||||
let size: EventViewKind
|
let size: EventViewKind
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ struct EventProfileName: View {
|
|||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
self.size = size
|
self.size = size
|
||||||
|
self._donation = State(wrappedValue: profile?.damus_donation)
|
||||||
}
|
}
|
||||||
|
|
||||||
var friend_type: FriendType? {
|
var friend_type: FriendType? {
|
||||||
@@ -45,6 +47,15 @@ struct EventProfileName: View {
|
|||||||
return profile.reactions == false
|
return profile.reactions == false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var supporter: Int? {
|
||||||
|
guard let donation, donation > 0
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return donation
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 2) {
|
HStack(spacing: 2) {
|
||||||
switch current_display_name {
|
switch current_display_name {
|
||||||
@@ -73,6 +84,10 @@ struct EventProfileName: View {
|
|||||||
Image("zap-hashtag")
|
Image("zap-hashtag")
|
||||||
.frame(width: 14, height: 14)
|
.frame(width: 14, height: 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let supporter {
|
||||||
|
SupporterBadge(percent: supporter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||||
let update = notif.object as! ProfileUpdate
|
let update = notif.object as! ProfileUpdate
|
||||||
@@ -81,6 +96,7 @@ struct EventProfileName: View {
|
|||||||
}
|
}
|
||||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||||
nip05 = damus_state.profiles.is_validated(pubkey)
|
nip05 = damus_state.profiles.is_validated(pubkey)
|
||||||
|
donation = update.profile.damus_donation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct ProfileName: View {
|
|||||||
|
|
||||||
@State var display_name: DisplayName?
|
@State var display_name: DisplayName?
|
||||||
@State var nip05: NIP05?
|
@State var nip05: NIP05?
|
||||||
|
@State var donation: Int?
|
||||||
|
|
||||||
init(pubkey: String, profile: Profile?, damus: DamusState, show_nip5_domain: Bool = true) {
|
init(pubkey: String, profile: Profile?, damus: DamusState, show_nip5_domain: Bool = true) {
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
@@ -75,6 +76,17 @@ struct ProfileName: View {
|
|||||||
return profile.reactions == false
|
return profile.reactions == false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var supporter: Int? {
|
||||||
|
guard let profile,
|
||||||
|
let donation = profile.damus_donation,
|
||||||
|
donation > 0
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return donation
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 2) {
|
HStack(spacing: 2) {
|
||||||
Text(verbatim: "\(prefix)\(name_choice)")
|
Text(verbatim: "\(prefix)\(name_choice)")
|
||||||
@@ -90,6 +102,9 @@ struct ProfileName: View {
|
|||||||
Image("zap-hashtag")
|
Image("zap-hashtag")
|
||||||
.frame(width: 14, height: 14)
|
.frame(width: 14, height: 14)
|
||||||
}
|
}
|
||||||
|
if let supporter {
|
||||||
|
SupporterBadge(percent: supporter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||||
let update = notif.object as! ProfileUpdate
|
let update = notif.object as! ProfileUpdate
|
||||||
@@ -98,6 +113,7 @@ struct ProfileName: View {
|
|||||||
}
|
}
|
||||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||||
nip05 = damus_state.profiles.is_validated(pubkey)
|
nip05 = damus_state.profiles.is_validated(pubkey)
|
||||||
|
donation = profile?.damus_donation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ func get_profile_url(picture: String?, pubkey: String, profiles: Profiles) -> UR
|
|||||||
func make_preview_profiles(_ pubkey: String) -> Profiles {
|
func make_preview_profiles(_ pubkey: String) -> Profiles {
|
||||||
let profiles = Profiles()
|
let profiles = Profiles()
|
||||||
let picture = "http://cdn.jb55.com/img/red-me.jpg"
|
let picture = "http://cdn.jb55.com/img/red-me.jpg"
|
||||||
let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, banner: "", website: "https://jb55.com", lud06: nil, lud16: nil, nip05: "jb55.com")
|
let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, banner: "", website: "https://jb55.com", lud06: nil, lud16: nil, nip05: "jb55.com", damus_donation: nil)
|
||||||
let ts_profile = TimestampedProfile(profile: profile, timestamp: 0, event: test_event)
|
let ts_profile = TimestampedProfile(profile: profile, timestamp: 0, event: test_event)
|
||||||
profiles.add(id: pubkey, profile: ts_profile)
|
profiles.add(id: pubkey, profile: ts_profile)
|
||||||
return profiles
|
return profiles
|
||||||
|
|||||||
@@ -496,8 +496,11 @@ struct ProfileView_Previews: PreviewProvider {
|
|||||||
func test_damus_state() -> DamusState {
|
func test_damus_state() -> DamusState {
|
||||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||||
let damus = DamusState.empty
|
let damus = DamusState.empty
|
||||||
|
let settings = UserSettingsStore()
|
||||||
|
settings.donation_percent = 100
|
||||||
|
settings.default_zap_amount = 1971
|
||||||
|
|
||||||
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io")
|
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io", damus_donation: nil)
|
||||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event)
|
let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event)
|
||||||
damus.profiles.add(id: pubkey, profile: tsprof)
|
damus.profiles.add(id: pubkey, profile: tsprof)
|
||||||
return damus
|
return damus
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ struct ReactionView: View {
|
|||||||
let reaction: NostrEvent
|
let reaction: NostrEvent
|
||||||
|
|
||||||
var content: String {
|
var content: String {
|
||||||
if reaction.content == "" || reaction.content == "+" {
|
return to_reaction_emoji(ev: reaction) ?? ""
|
||||||
return "❤️"
|
|
||||||
}
|
|
||||||
return reaction.content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|||||||
@@ -224,5 +224,5 @@ struct SaveKeysView_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func create_account_to_metadata(_ model: CreateAccountModel) -> Profile {
|
func create_account_to_metadata(_ model: CreateAccountModel) -> Profile {
|
||||||
return Profile(name: model.nick_name, display_name: model.real_name, about: model.about, picture: model.profile_image, banner: nil, website: nil, lud06: nil, lud16: nil, nip05: nil)
|
return Profile(name: model.nick_name, display_name: model.real_name, about: model.about, picture: model.profile_image, banner: nil, website: nil, lud06: nil, lud16: nil, nip05: nil, damus_donation: nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ func make_hashtagable(_ str: String) -> String {
|
|||||||
|
|
||||||
func search_profiles(profiles: Profiles, search: String) -> [SearchedUser] {
|
func search_profiles(profiles: Profiles, search: String) -> [SearchedUser] {
|
||||||
let new = search.lowercased()
|
let new = search.lowercased()
|
||||||
return profiles.profiles.enumerated().reduce(into: []) { acc, els in
|
return profiles.enumerated().reduce(into: []) { acc, els in
|
||||||
let pk = els.element.key
|
let pk = els.element.key
|
||||||
let prof = els.element.value.profile
|
let prof = els.element.value.profile
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,19 @@ struct WalletView: View {
|
|||||||
var tip_msats: String {
|
var tip_msats: String {
|
||||||
let msats = Int64(percent * Double(model.settings.default_zap_amount * 1000))
|
let msats = Int64(percent * Double(model.settings.default_zap_amount * 1000))
|
||||||
let s = format_msats_abbrev(msats)
|
let s = format_msats_abbrev(msats)
|
||||||
return s.split(separator: ".").first.map({ x in String(x) }) ?? s
|
// TODO: fix formatting and remove this hack
|
||||||
|
let parts = s.split(separator: ".")
|
||||||
|
if parts.count == 1 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if let end = parts[safe: 1] {
|
||||||
|
if end.allSatisfy({ c in c.isNumber }) {
|
||||||
|
return String(parts[0])
|
||||||
|
} else {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
var SupportDamus: some View {
|
var SupportDamus: some View {
|
||||||
@@ -93,6 +105,7 @@ struct WalletView: View {
|
|||||||
Text("\(Int(binding.wrappedValue))%")
|
Text("\(Int(binding.wrappedValue))%")
|
||||||
.font(.title.bold())
|
.font(.title.bold())
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
.frame(width: 80)
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack{
|
HStack{
|
||||||
@@ -103,7 +116,7 @@ struct WalletView: View {
|
|||||||
Text("\(Image("zap.fill")) \(format_msats_abbrev(Int64(model.settings.default_zap_amount) * 1000))")
|
Text("\(Image("zap.fill")) \(format_msats_abbrev(Int64(model.settings.default_zap_amount) * 1000))")
|
||||||
.font(.title)
|
.font(.title)
|
||||||
.foregroundColor(percent == 0 ? .gray : .yellow)
|
.foregroundColor(percent == 0 ? .gray : .yellow)
|
||||||
.frame(width: 100)
|
.frame(width: 120)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("Zap")
|
Text("Zap")
|
||||||
@@ -121,9 +134,10 @@ struct WalletView: View {
|
|||||||
Text("\(Image("zap.fill")) \(tip_msats)")
|
Text("\(Image("zap.fill")) \(tip_msats)")
|
||||||
.font(.title)
|
.font(.title)
|
||||||
.foregroundColor(percent == 0 ? .gray : Color.yellow)
|
.foregroundColor(percent == 0 ? .gray : Color.yellow)
|
||||||
.frame(width: 100)
|
.frame(width: 120)
|
||||||
}
|
}
|
||||||
Text("💜")
|
|
||||||
|
Text(percent == 0 ? "🩶" : "💜")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -154,6 +168,32 @@ struct WalletView: View {
|
|||||||
ConnectWalletView(model: model)
|
ConnectWalletView(model: model)
|
||||||
case .existing(let nwc):
|
case .existing(let nwc):
|
||||||
MainWalletView(nwc: nwc)
|
MainWalletView(nwc: nwc)
|
||||||
|
.onAppear() {
|
||||||
|
model.inital_percent = settings.donation_percent
|
||||||
|
}
|
||||||
|
.onChange(of: settings.donation_percent) { p in
|
||||||
|
guard let profile = damus_state.profiles.lookup(id: damus_state.pubkey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.damus_donation = p
|
||||||
|
|
||||||
|
notify(.profile_updated, ProfileUpdate(pubkey: damus_state.pubkey, profile: profile))
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
guard let keypair = damus_state.keypair.to_full(),
|
||||||
|
let profile = damus_state.profiles.lookup(id: damus_state.pubkey),
|
||||||
|
model.inital_percent != profile.damus_donation
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.damus_donation = settings.donation_percent
|
||||||
|
let meta = make_metadata_event(keypair: keypair, metadata: profile)
|
||||||
|
let tsprofile = TimestampedProfile(profile: profile, timestamp: meta.created_at, event: meta)
|
||||||
|
damus_state.profiles.add(id: damus_state.pubkey, profile: tsprofile)
|
||||||
|
damus_state.postbox.send(meta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,4 +32,26 @@ class LikeTests: XCTestCase {
|
|||||||
XCTAssertEqual(like_ev.last_refid()!.ref_id, id)
|
XCTAssertEqual(like_ev.last_refid()!.ref_id, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testToReactionEmoji() {
|
||||||
|
let privkey = "0fc2092231f958f8d57d66f5e238bb45b6a2571f44c0ce024bbc6f3a9c8a15fe"
|
||||||
|
let pubkey = "30c6d1dc7f7c156794fa15055e651b758a61b99f50fcf759de59386050bf6ae2"
|
||||||
|
let liked = NostrEvent(content: "awesome #[0] post", pubkey: "orig_pk", tags: [["p", "cindy"], ["e", "bob"]])
|
||||||
|
liked.calculate_id()
|
||||||
|
let id = liked.id
|
||||||
|
|
||||||
|
let emptyReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "")
|
||||||
|
let plusReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "+")
|
||||||
|
let minusReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "-")
|
||||||
|
let heartReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "❤️")
|
||||||
|
let thumbsUpReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "👍")
|
||||||
|
let shakaReaction = make_like_event(pubkey: pubkey, privkey: privkey, liked: liked, content: "🤙")
|
||||||
|
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: emptyReaction), "❤️")
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: plusReaction), "❤️")
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: minusReaction), "👎")
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: heartReaction), "❤️")
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: thumbsUpReaction), "👍")
|
||||||
|
XCTAssertEqual(to_reaction_emoji(ev: shakaReaction), "🤙")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user