---
damus/Views/ActionBar/EventActionBar.swift | 139 ++++++++--
.../Settings/ReactionsSettingsView.swift | 254 ++++++++++++++++++
2 files changed, 378 insertions(+), 15 deletions(-)
create mode 100644 damus/Views/Settings/ReactionsSettingsView.swift
diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift
index ab450c4e..d25b94f9 100644
--- a/damus/Views/ActionBar/EventActionBar.swift
+++ b/damus/Views/ActionBar/EventActionBar.swift
@@ -72,11 +72,11 @@ struct EventActionBar: View {
Spacer()
HStack(spacing: 4) {
- LikeButton(liked: bar.liked) {
+ LikeButton(liked: bar.liked) { emoji in
if bar.liked {
notify(.delete, bar.our_like)
} else {
- send_like()
+ send_like(emoji: emoji)
}
}
@@ -143,12 +143,12 @@ struct EventActionBar: View {
}
}
- func send_like() {
+ func send_like(emoji: String) {
guard let privkey = damus_state.keypair.privkey else {
return
}
- let like_ev = make_like_event(pubkey: damus_state.pubkey, privkey: privkey, liked: event)
+ let like_ev = make_like_event(pubkey: damus_state.pubkey, privkey: privkey, liked: event, content: emoji)
self.bar.our_like = like_ev
@@ -172,15 +172,20 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) ->
struct LikeButton: View {
let liked: Bool
- let action: () -> ()
+ let action: (_ emoji: String) -> Void
+
+ // For reactions background
+ @State private var showReactionsBG = 0
+ @State private var showEmojis: [Int] = []
+ @State private var rotateThumb = -45
+
+ @State private var isReactionsVisible = false
// Following four are Shaka animation properties
let timer = Timer.publish(every: 0.10, on: .main, in: .common).autoconnect()
@State private var shouldAnimate = false
@State private var rotationAngle = 0.0
@State private var amountOfAngleIncrease: Double = 0.0
-
- var body: some View {
Button(action: {
withAnimation(Animation.easeOut(duration: 0.15)) {
@@ -194,14 +199,118 @@ struct LikeButton: View {
.mask(Image("shaka.fill")
.resizable()
.aspectRatio(contentMode: .fit)
- )
- .frame(width: 20, height: 20)
- } else {
- Image("shaka")
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: 20, height: 20)
- .foregroundColor(.gray)
+ )
+ } else {
+ Text(emoji)
+ }
+ }
+
+ var body: some View {
+ ZStack {
+ Button(action: {
+ if (!isReactionsVisible) {
+ withAnimation(Animation.easeOut(duration: 0.15)) {
+ self.action(damus_state.settings.default_emoji_reaction)
+ shouldAnimate = true
+ amountOfAngleIncrease = 20.0
+ }
+ }
+ }) {
+ if let unwrappedLikedEmoji = liked_emoji {
+ let maskView: some View = buildMaskView(for: unwrappedLikedEmoji)
+
+ maskView
+ .frame(width: 20, height: 20)
+ } else {
+ Image("shaka")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 20, height: 20)
+ .foregroundColor(.gray)
+ }
+ }
+ .accessibilityLabel(NSLocalizedString("Like", comment: "Accessibility Label for Like button"))
+ .rotationEffect(Angle(degrees: shouldAnimate ? rotationAngle : 0))
+ .onReceive(self.timer) { _ in
+ // Shaka animation logic
+ rotationAngle = amountOfAngleIncrease
+ if amountOfAngleIncrease == 0 {
+ timer.upstream.connect().cancel()
+ return
+ }
+ amountOfAngleIncrease = -amountOfAngleIncrease
+ if amountOfAngleIncrease < 0 {
+ amountOfAngleIncrease += 2.5
+ } else {
+ amountOfAngleIncrease -= 2.5
+ }
+ }
+ .simultaneousGesture(
+ LongPressGesture(minimumDuration: 0.5).onEnded { _ in
+ startLongPressTimer()
+ }
+ )
+ .overlay(
+ Group {
+ if isReactionsVisible {
+ AnyView(
+ ZStack {
+ RoundedRectangle(cornerRadius: 10)
+ .frame(width: 250, height: 50)
+ .foregroundColor(DamusColors.black)
+ .scaleEffect(Double(showReactionsBG), anchor: .topTrailing)
+ .animation(
+ .interpolatingSpring(stiffness: 170, damping: 15).delay(0.05),
+ value: showReactionsBG
+ )
+ .overlay(
+ Rectangle()
+ .foregroundColor(Color.white.opacity(0.2))
+ .frame(width: 250, height: 50)
+ .clipShape(
+ RoundedRectangle(cornerRadius: 10)
+ )
+ )
+ HStack {
+ ScrollView(.horizontal, showsIndicators: false) {
+ HStack(spacing: 20) {
+ ForEach(emojis, id: \.self) { emoji in
+ Text(emoji)
+ .scaleEffect(Double(showEmojis.count >= emojis.firstIndex(of: emoji)! + 1 ? showEmojis[emojis.firstIndex(of: emoji)!] : 0))
+ .onTapGesture {
+ emojiTapped(emoji)
+ }
+ }
+ }
+ .padding(.horizontal, 20)
+ }
+ }
+ }
+ .onTapGesture {
+ withAnimation(.easeOut(duration: 0.2)) {
+ isReactionsVisible = false
+ showReactionsBG = 0
+ }
+ showEmojis = []
+ }
+ ).offset(y: -40)
+ } else {
+ AnyView(EmptyView())
+ }
+ }
+ )
+ }
+ }
+
+ private func startLongPressTimer() {
+ UIImpactFeedbackGenerator(style: .medium).impactOccurred()
+ showEmojis = Array(repeating: 0, count: emojis.count) // Initialize the showEmojis array
+
+ for (index, _) in emojis.enumerated() {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
+ withAnimation(.interpolatingSpring(stiffness: 170, damping: 8)) {
+ showEmojis[index] = 1
+ }
}
}
.accessibilityLabel(NSLocalizedString("Like", comment: "Accessibility Label for Like button"))
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
new file mode 100644
index 00000000..20329c41
--- /dev/null
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -0,0 +1,254 @@
+//
+// ReactionsSettingsView.swift
+// damus
+//
+// Created by Suhail Saqan on 7/3/23.
+//
+
+import SwiftUI
+import Combine
+
+let default_emoji_reactions = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]
+
+struct ReactionsSettingsView: View {
+ @ObservedObject var settings: UserSettingsStore
+
+ @State var new_emoji: String = ""
+ @State private var showActionButtons = false
+
+ @Environment(\.dismiss) var dismiss
+
+ var recommended: [String] {
+ return getMissingRecommendedEmojis(added: settings.emoji_reactions)
+ }
+
+ var body: some View {
+ Form {
+ Section {
+ AddEmojiView(emoji: $new_emoji)
+ } header: {
+ HStack {
+ Text(NSLocalizedString("Add Emoji", comment: "Label for section for adding an emoji to the reactions list."))
+ .font(.system(size: 18, weight: .heavy))
+ .padding(.bottom, 5)
+ }
+ } footer: {
+ VStack {
+ HStack {
+ Spacer()
+ if(!new_emoji.isEmpty) {
+ Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted emoji.")) {
+ new_emoji = ""
+ UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
+ }
+ .font(.system(size: 14, weight: .bold))
+ .frame(width: 80, height: 30)
+ .foregroundColor(.white)
+ .background(LINEAR_GRADIENT)
+ .clipShape(Capsule())
+ .padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
+
+ Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted emoji.")) {
+ if (isValidEmoji(new_emoji)) {
+ settings.emoji_reactions.append(new_emoji)
+ new_emoji = ""
+ }
+ }
+ .font(.system(size: 14, weight: .bold))
+ .frame(width: 80, height: 30)
+ .foregroundColor(.white)
+ .background(LINEAR_GRADIENT)
+ .clipShape(Capsule())
+ .padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
+ }
+ }
+ }
+ }
+
+ Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
+ selection: $settings.default_emoji_reaction) {
+ ForEach(settings.emoji_reactions, id: \.self) { emoji in
+ Text(emoji)
+ }
+ }
+
+ Section {
+ List(Array(settings.emoji_reactions), id: \.self) { emoji in
+ EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
+ }
+ } header: {
+ HStack {
+ Text(NSLocalizedString("Emoji Reactions", comment: "Section title for emoji reactions that are currently added."))
+ .font(.system(size: 18, weight: .heavy))
+ .padding(.bottom, 5)
+ }
+ }
+
+ if recommended.count > 0 {
+ Section {
+ List(Array(recommended), id: \.self) { emoji in
+ EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
+ }
+ } header: {
+ Text(NSLocalizedString("Recommended Emojis", comment: "Section title for recommend emojis"))
+ .font(.system(size: 18, weight: .heavy))
+ .padding(.bottom, 5)
+ }
+ }
+ }
+ .navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
+ .navigationBarTitleDisplayMode(.large)
+ .toolbar {
+ if showActionButtons {
+ Button("Done") {
+ showActionButtons.toggle()
+ }
+ } else {
+ Button("Edit") {
+ showActionButtons.toggle()
+ }
+ }
+ }
+ }
+
+ func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
+ let addedSet = Set(added)
+ let missingEmojis = recommended.filter { !addedSet.contains($0) }
+ return missingEmojis
+ }
+}
+
+func isValidEmoji(_ string: String) -> Bool {
+ let count = string.count
+
+ // Check if the string consists of a single character
+ guard count == 1 else {
+ return false
+ }
+
+ // Check if the character is an emoji
+ for scalar in string.unicodeScalars {
+ if !scalar.properties.isEmoji && !scalar.properties.isEmojiPresentation {
+ return false
+ }
+ }
+
+ return true
+}
+
+struct AddEmojiView: View {
+ @Binding var emoji: String
+
+ var body: some View {
+ ZStack(alignment: .leading) {
+ HStack{
+ TextField(NSLocalizedString("⚡", comment: "Placeholder example for an emoji reaction"), text: $emoji)
+ .padding(2)
+ .padding(.leading, 25)
+ .opacity(emoji == "" ? 0.5 : 1)
+ .autocorrectionDisabled(true)
+ .textInputAutocapitalization(.never)
+ .onReceive(Just(emoji)) { newEmoji in
+ if let lastEmoji = newEmoji.last.map(String.init), isValidEmoji(lastEmoji) {
+ self.emoji = lastEmoji
+ } else {
+ self.emoji = ""
+ }
+ }
+
+ Label("", image: "close-circle")
+ .foregroundColor(.accentColor)
+ .padding(.trailing, -25.0)
+ .opacity((emoji == "") ? 0.0 : 1.0)
+ .onTapGesture {
+ self.emoji = ""
+ }
+ }
+
+ Label("", image: "copy2")
+ .padding(.leading, -10)
+ .onTapGesture {
+ if let pastedEmoji = UIPasteboard.general.string {
+ self.emoji = pastedEmoji
+ }
+ }
+ }
+ }
+}
+
+struct EmojiView: View {
+ @ObservedObject var settings: UserSettingsStore
+
+ let emoji: String
+ let recommended: Bool
+
+ @Binding var showActionButtons: Bool
+
+ var body: some View {
+ Group {
+ HStack {
+ if showActionButtons {
+ if recommended {
+ AddButton()
+ } else {
+ RemoveButton()
+ }
+ }
+
+ Text(emoji)
+ }
+ }
+ .swipeActions {
+ if (!recommended){
+ RemoveButton()
+ .tint(.red)
+ } else {
+ AddButton()
+ .tint(.green)
+ }
+ }
+ .contextMenu {
+ CopyAction(emoji: emoji)
+ }
+ }
+
+ func CopyAction(emoji: String) -> some View {
+ Button {
+ UIPasteboard.general.setValue(emoji, forPasteboardType: "public.plain-text")
+ } label: {
+ Label(NSLocalizedString("Copy", comment: "Button to copy an emoji reaction"), image: "copy2")
+ }
+ }
+
+ func RemoveButton() -> some View {
+ Button(action: {
+ if let index = settings.emoji_reactions.firstIndex(of: emoji) {
+ settings.emoji_reactions.remove(at: index)
+ }
+ }) {
+ Image(systemName: "minus.circle")
+ .resizable()
+ .frame(width: 20, height: 20)
+ .foregroundColor(.red)
+ .padding(.leading, 5)
+ }
+ }
+
+ func AddButton() -> some View {
+ Button(action: {
+ settings.emoji_reactions.append(emoji)
+ }) {
+ Image(systemName: "plus.circle")
+ .resizable()
+ .frame(width: 20, height: 20)
+ .foregroundColor(.green)
+ .padding(.leading, 5)
+ }
+ }
+}
+
+struct ReactionsSettingsView_Previews: PreviewProvider {
+ static var previews: some View {
+ ReactionsSettingsView(settings: UserSettingsStore())
+ }
+}
--
2.39.2 (Apple Git-143)
From 9583996c4c6eff5a875d3567c6aaff6a931ca2c0 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:11:36 -0500
Subject: [PATCH 02/12] add reaction settings view
---
damus/Views/ConfigView.swift | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift
index 90fcdf74..8520a2fb 100644
--- a/damus/Views/ConfigView.swift
+++ b/damus/Views/ConfigView.swift
@@ -59,6 +59,10 @@ struct ConfigView: View {
NavigationLink(value: Route.TranslationSettings(settings: settings)) {
IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green)
}
+
+ NavigationLink(value: Route.ReactionsSettings(settings: settings)) {
+ IconLabel(NSLocalizedString("Reactions", comment: "Section header for reactions settings"), img_name: "shaka.fill", color: .purple)
+ }
}
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
--
2.39.2 (Apple Git-143)
From 0bf43f740eb42e24048d275b2b7abb45ec60ae68 Mon Sep 17 00:00:00 2001
From: William Casarin <
jb...@jb55.com>
Date: Wed, 5 Jul 2023 10:25:35 -0700
Subject: [PATCH 03/12] route: add ReactionSettings router
---
damus/Util/Router.swift | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift
index 66d0fa7b..41624c8e 100644
--- a/damus/Util/Router.swift
+++ b/damus/Util/Router.swift
@@ -27,6 +27,7 @@ enum Route: Hashable {
case NotificationSettings(settings: UserSettingsStore)
case ZapSettings(settings: UserSettingsStore)
case TranslationSettings(settings: UserSettingsStore)
+ case ReactionsSettings(settings: UserSettingsStore)
case SearchSettings(settings: UserSettingsStore)
case Thread(thread: ThreadModel)
case Reposts(reposts: RepostsModel)
@@ -80,6 +81,8 @@ enum Route: Hashable {
ZapSettingsView(settings: settings)
case .TranslationSettings(let settings):
TranslationSettingsView(settings: settings)
+ case .ReactionsSettings(let settings):
+ ReactionsSettingsView(settings: settings)
case .SearchSettings(let settings):
SearchSettingsView(settings: settings)
case .Thread(let thread):
@@ -149,6 +152,8 @@ enum Route: Hashable {
return true
case (.TranslationSettings(_), .TranslationSettings(_)):
return true
+ case (.ReactionsSettings(_), .ReactionsSettings(_)):
+ return true
case (.SearchSettings(_), .SearchSettings(_)):
return true
case (.Thread(let lhs_threadModel), .Thread(thread: let rhs_threadModel)):
@@ -228,6 +233,8 @@ enum Route: Hashable {
hasher.combine("zapSettings")
case .TranslationSettings(_):
hasher.combine("translationSettings")
+ case .ReactionsSettings(_):
+ hasher.combine("reactionsSettings")
case .SearchSettings(_):
hasher.combine("searchSettings")
case .Thread(let threadModel):
--
2.39.2 (Apple Git-143)
From 263c766433d723c510fdeda09f16e02b44113f80 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:12:35 -0500
Subject: [PATCH 04/12] add screen to edit emojis reactions,
ReactionsSettingsView
---
damus.xcodeproj/project.pbxproj | 4 ++
.../Settings/ReactionsSettingsView.swift | 53 ++++++++++++-------
2 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
index c2399f19..2e468e6c 100644
--- a/damus.xcodeproj/project.pbxproj
+++ b/damus.xcodeproj/project.pbxproj
@@ -44,6 +44,7 @@
4C06670E28FDEAA000038D2A /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670D28FDEAA000038D2A /* utf8.c */; };
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
+ 4C15C7152A55DE7A00D0A0DB /* ReactionsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */; };
4C190F202A535FC200027FD5 /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */; };
4C190F222A53950D00027FD5 /* bool_setting.wasm in Resources */ = {isa = PBXBuildFile; fileRef = 4C190F212A53950D00027FD5 /* bool_setting.wasm */; };
4C190F252A547D2000027FD5 /* LoadScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F242A547D2000027FD5 /* LoadScript.swift */; };
@@ -469,6 +470,7 @@
4C06670D28FDEAA000038D2A /* utf8.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utf8.c; sourceTree = "<group>"; };
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
+ 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsSettingsView.swift; sourceTree = "<group>"; };
4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
4C190F212A53950D00027FD5 /* bool_setting.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = bool_setting.wasm; path = nostrscript/bool_setting.wasm; sourceTree = SOURCE_ROOT; };
4C190F242A547D2000027FD5 /* LoadScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadScript.swift; sourceTree = "<group>"; };
@@ -1006,6 +1008,7 @@
4C1A9A1B29DDCF8B00516EAC /* Settings */ = {
isa = PBXGroup;
children = (
+ 4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */,
4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */,
4C1A9A1E29DDD24B00516EAC /* AppearanceSettingsView.swift */,
4C1A9A2029DDD3E100516EAC /* KeySettingsView.swift */,
@@ -1788,6 +1791,7 @@
4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */,
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */,
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
+ 4C15C7152A55DE7A00D0A0DB /* ReactionsSettingsView.swift in Sources */,
4C8D00C829DF791C0036AF10 /* CompatibleAttribute.swift in Sources */,
4C7D09742A0AEF9000943473 /* AlbyGradient.swift in Sources */,
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */,
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 20329c41..e4876c96 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -39,7 +39,6 @@ struct ReactionsSettingsView: View {
if(!new_emoji.isEmpty) {
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted emoji.")) {
new_emoji = ""
- UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
.font(.system(size: 14, weight: .bold))
.frame(width: 80, height: 30)
@@ -111,31 +110,13 @@ struct ReactionsSettingsView: View {
}
}
- func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
+ func getMissingRecommendedEmojis(added: [String], recommended: [String] = default_emoji_reactions) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
return missingEmojis
}
}
-func isValidEmoji(_ string: String) -> Bool {
- let count = string.count
-
- // Check if the string consists of a single character
- guard count == 1 else {
- return false
- }
-
- // Check if the character is an emoji
- for scalar in string.unicodeScalars {
- if !scalar.properties.isEmoji && !scalar.properties.isEmojiPresentation {
- return false
- }
- }
-
- return true
-}
-
struct AddEmojiView: View {
@Binding var emoji: String
@@ -247,6 +228,38 @@ struct EmojiView: View {
}
}
+/// From:
https://stackoverflow.com/a/39425959
+extension Character {
+ /// A simple emoji is one scalar and presented to the user as an Emoji
+ var isSimpleEmoji: Bool {
+ guard let firstScalar = unicodeScalars.first else { return false }
+ return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
+ }
+
+ /// Checks if the scalars will be merged into an emoji
+ var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }
+
+ var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
+}
+
+extension String {
+ var isSingleEmoji: Bool { count == 1 && containsEmoji }
+
+ var containsEmoji: Bool { contains { $0.isEmoji } }
+
+ var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }
+
+ var emojiString: String { emojis.map { String($0) }.reduce("", +) }
+
+ var emojis: [Character] { filter { $0.isEmoji } }
+
+ var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
+}
+
+func isValidEmoji(_ string: String) -> Bool {
+ return string.isSingleEmoji
+}
+
struct ReactionsSettingsView_Previews: PreviewProvider {
static var previews: some View {
ReactionsSettingsView(settings: UserSettingsStore())
--
2.39.2 (Apple Git-143)
From 00209c145212c86902023a65675cafc2b5d5d8c9 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:12:58 -0500
Subject: [PATCH 05/12] set emoji_reactions in settings
---
damus/Models/UserSettingsStore.swift | 3 +++
1 file changed, 3 insertions(+)
diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift
index 820ec7c4..3940900b 100644
--- a/damus/Models/UserSettingsStore.swift
+++ b/damus/Models/UserSettingsStore.swift
@@ -157,6 +157,9 @@ class UserSettingsStore: ObservableObject {
@Setting(key: "donation_percent", default_value: 0)
var donation_percent: Int
+
+ @Setting(key: "emoji_reactions", default_value: default_emoji_reactions)
+ var emoji_reactions: [String]
// Helper for inverse of disable_animation.
// disable_animation was introduced as a setting first, but it's more natural for the settings UI to show the inverse.
--
2.39.2 (Apple Git-143)
From 4c9380cbe172841fb8a8efc8227fa015ea88a6e0 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:19:25 -0500
Subject: [PATCH 06/12] make it not show edit for recommended list
---
.../Settings/ReactionsSettingsView.swift | 99 +++++++------------
1 file changed, 34 insertions(+), 65 deletions(-)
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index e4876c96..3fe56f9c 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -8,8 +8,6 @@
import SwiftUI
import Combine
-let default_emoji_reactions = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]
-
struct ReactionsSettingsView: View {
@ObservedObject var settings: UserSettingsStore
@@ -18,11 +16,19 @@ struct ReactionsSettingsView: View {
@Environment(\.dismiss) var dismiss
+ init(settings: UserSettingsStore) {
+ self._settings = ObservedObject(initialValue: settings)
+ }
+
var recommended: [String] {
return getMissingRecommendedEmojis(added: settings.emoji_reactions)
}
var body: some View {
+ MainContent
+ }
+
+ var MainContent: some View {
Form {
Section {
AddEmojiView(emoji: $new_emoji)
@@ -39,6 +45,7 @@ struct ReactionsSettingsView: View {
if(!new_emoji.isEmpty) {
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted emoji.")) {
new_emoji = ""
+ UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
.font(.system(size: 14, weight: .bold))
.frame(width: 80, height: 30)
@@ -64,13 +71,6 @@ struct ReactionsSettingsView: View {
}
}
- Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
- selection: $settings.default_emoji_reaction) {
- ForEach(settings.emoji_reactions, id: \.self) { emoji in
- Text(emoji)
- }
- }
-
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
@@ -85,7 +85,7 @@ struct ReactionsSettingsView: View {
if recommended.count > 0 {
Section {
- List(Array(recommended), id: \.self) { emoji in
+ List(Array(settings.emoji_reactions), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
}
} header: {
@@ -95,7 +95,7 @@ struct ReactionsSettingsView: View {
}
}
}
- .navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
+ .navigationTitle(NSLocalizedString("Relays", comment: "Title of emojis view"))
.navigationBarTitleDisplayMode(.large)
.toolbar {
if showActionButtons {
@@ -110,13 +110,33 @@ struct ReactionsSettingsView: View {
}
}
- func getMissingRecommendedEmojis(added: [String], recommended: [String] = default_emoji_reactions) -> [String] {
+
+
+ func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
return missingEmojis
}
}
+func isValidEmoji(_ string: String) -> Bool {
+ let count = string.count
+
+ // Check if the string consists of a single character
+ guard count == 1 else {
+ return false
+ }
+
+ // Check if the character is an emoji
+ for scalar in string.unicodeScalars {
+ if !scalar.properties.isEmoji && !scalar.properties.isEmojiPresentation {
+ return false
+ }
+ }
+
+ return true
+}
+
struct AddEmojiView: View {
@Binding var emoji: String
@@ -168,12 +188,8 @@ struct EmojiView: View {
var body: some View {
Group {
HStack {
- if showActionButtons {
- if recommended {
- AddButton()
- } else {
- RemoveButton()
- }
+ if showActionButtons && !recommended {
+ RemoveButton()
}
Text(emoji)
@@ -183,9 +199,6 @@ struct EmojiView: View {
if (!recommended){
RemoveButton()
.tint(.red)
- } else {
- AddButton()
- .tint(.green)
}
}
.contextMenu {
@@ -214,50 +227,6 @@ struct EmojiView: View {
.padding(.leading, 5)
}
}
-
- func AddButton() -> some View {
- Button(action: {
- settings.emoji_reactions.append(emoji)
- }) {
- Image(systemName: "plus.circle")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(.green)
- .padding(.leading, 5)
- }
- }
-}
-
-/// From:
https://stackoverflow.com/a/39425959
-extension Character {
- /// A simple emoji is one scalar and presented to the user as an Emoji
- var isSimpleEmoji: Bool {
- guard let firstScalar = unicodeScalars.first else { return false }
- return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
- }
-
- /// Checks if the scalars will be merged into an emoji
- var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }
-
- var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
-}
-
-extension String {
- var isSingleEmoji: Bool { count == 1 && containsEmoji }
-
- var containsEmoji: Bool { contains { $0.isEmoji } }
-
- var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }
-
- var emojiString: String { emojis.map { String($0) }.reduce("", +) }
-
- var emojis: [Character] { filter { $0.isEmoji } }
-
- var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
-}
-
-func isValidEmoji(_ string: String) -> Bool {
- return string.isSingleEmoji
}
struct ReactionsSettingsView_Previews: PreviewProvider {
--
2.39.2 (Apple Git-143)
From a3e099456189edd28496c31a9d023e66efa40b60 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:49:53 -0500
Subject: [PATCH 07/12] add ability to set default emoji and fix recommended
view
---
damus/Models/UserSettingsStore.swift | 3 ++
.../Settings/ReactionsSettingsView.swift | 36 +++++++++++++++----
2 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift
index 3940900b..850740ca 100644
--- a/damus/Models/UserSettingsStore.swift
+++ b/damus/Models/UserSettingsStore.swift
@@ -160,6 +160,9 @@ class UserSettingsStore: ObservableObject {
@Setting(key: "emoji_reactions", default_value: default_emoji_reactions)
var emoji_reactions: [String]
+
+ @Setting(key: "default_emoji_reaction", default_value: "🤙")
+ var default_emoji_reaction: String
// Helper for inverse of disable_animation.
// disable_animation was introduced as a setting first, but it's more natural for the settings UI to show the inverse.
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 3fe56f9c..427e7bb5 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -71,6 +71,13 @@ struct ReactionsSettingsView: View {
}
}
+ Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
+ selection: $settings.default_emoji_reaction) {
+ ForEach(settings.emoji_reactions, id: \.self) { emoji in
+ Text(emoji)
+ }
+ }
+
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
@@ -85,7 +92,7 @@ struct ReactionsSettingsView: View {
if recommended.count > 0 {
Section {
- List(Array(settings.emoji_reactions), id: \.self) { emoji in
+ List(Array(recommended), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
}
} header: {
@@ -95,7 +102,7 @@ struct ReactionsSettingsView: View {
}
}
}
- .navigationTitle(NSLocalizedString("Relays", comment: "Title of emojis view"))
+ .navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
.navigationBarTitleDisplayMode(.large)
.toolbar {
if showActionButtons {
@@ -110,8 +117,6 @@ struct ReactionsSettingsView: View {
}
}
-
-
func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
@@ -188,8 +193,12 @@ struct EmojiView: View {
var body: some View {
Group {
HStack {
- if showActionButtons && !recommended {
- RemoveButton()
+ if showActionButtons {
+ if recommended {
+ AddButton()
+ } else {
+ RemoveButton()
+ }
}
Text(emoji)
@@ -199,6 +208,9 @@ struct EmojiView: View {
if (!recommended){
RemoveButton()
.tint(.red)
+ } else {
+ AddButton()
+ .tint(.green)
}
}
.contextMenu {
@@ -227,6 +239,18 @@ struct EmojiView: View {
.padding(.leading, 5)
}
}
+
+ func AddButton() -> some View {
+ Button(action: {
+ settings.emoji_reactions.append(emoji)
+ }) {
+ Image(systemName: "plus.circle")
+ .resizable()
+ .frame(width: 20, height: 20)
+ .foregroundColor(.green)
+ .padding(.leading, 5)
+ }
+ }
}
struct ReactionsSettingsView_Previews: PreviewProvider {
--
2.39.2 (Apple Git-143)
From 161dc84f2793c45a9012a2668943a04f10319942 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 02:23:36 -0500
Subject: [PATCH 08/12] add multiple emoji selector on long press
---
damus/Views/ActionBar/EventActionBar.swift | 62 ++++++++++++----------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift
index d25b94f9..bc286405 100644
--- a/damus/Views/ActionBar/EventActionBar.swift
+++ b/damus/Views/ActionBar/EventActionBar.swift
@@ -72,7 +72,7 @@ struct EventActionBar: View {
Spacer()
HStack(spacing: 4) {
- LikeButton(liked: bar.liked) { emoji in
+ LikeButton(damus_state: damus_state, liked: bar.liked, liked_emoji: bar.our_like != nil ? to_reaction_emoji(ev: bar.our_like!) : nil) { emoji in
if bar.liked {
notify(.delete, bar.our_like)
} else {
@@ -171,7 +171,9 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) ->
}
struct LikeButton: View {
+ let damus_state: DamusState
let liked: Bool
+ let liked_emoji: String?
let action: (_ emoji: String) -> Void
// For reactions background
@@ -187,16 +189,23 @@ struct LikeButton: View {
@State private var rotationAngle = 0.0
@State private var amountOfAngleIncrease: Double = 0.0
- Button(action: {
- withAnimation(Animation.easeOut(duration: 0.15)) {
- self.action()
- shouldAnimate = true
- amountOfAngleIncrease = 20.0
- }
- }) {
- if liked {
- LINEAR_GRADIENT
- .mask(Image("shaka.fill")
+ var emojis: [String] {
+ damus_state.settings.emoji_reactions
+ }
+
+ init(damus_state: DamusState, liked: Bool, liked_emoji: String?, action: @escaping (_ emoji: String) -> Void) {
+ self.damus_state = damus_state
+ self.liked = liked
+ self.liked_emoji = liked_emoji
+ self.action = action
+ }
+
+ @ViewBuilder
+ func buildMaskView(for emoji: String) -> some View {
+ if emoji == "🤙" {
+ LINEAR_GRADIENT
+ .mask(
+ Image("shaka.fill")
.resizable()
.aspectRatio(contentMode: .fit)
)
@@ -257,7 +266,7 @@ struct LikeButton: View {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(width: 250, height: 50)
- .foregroundColor(DamusColors.black)
+ .foregroundColor(reactionsBGColor)
.scaleEffect(Double(showReactionsBG), anchor: .topTrailing)
.animation(
.interpolatingSpring(stiffness: 170, damping: 15).delay(0.05),
@@ -313,22 +322,21 @@ struct LikeButton: View {
}
}
}
- .accessibilityLabel(NSLocalizedString("Like", comment: "Accessibility Label for Like button"))
- .rotationEffect(Angle(degrees: shouldAnimate ? rotationAngle : 0))
- .onReceive(self.timer) { _ in
- // Shaka animation logic
- rotationAngle = amountOfAngleIncrease
- if amountOfAngleIncrease == 0 {
- timer.upstream.connect().cancel()
- return
- }
- amountOfAngleIncrease = -amountOfAngleIncrease
- if amountOfAngleIncrease < 0 {
- amountOfAngleIncrease += 2.5
- } else {
- amountOfAngleIncrease -= 2.5
- }
+
+ isReactionsVisible = true
+ showReactionsBG = 1
+ }
+
+ private func emojiTapped(_ emoji: String) {
+ print("Tapped emoji: \(emoji)")
+
+ self.action(emoji)
+
+ withAnimation(.easeOut(duration: 0.2)) {
+ isReactionsVisible = false
+ showReactionsBG = 0
}
+ showEmojis = []
}
}
--
2.39.2 (Apple Git-143)
From 6447e984f14e0a8082e0e13071e42fd8a85c0a16 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:12:35 -0500
Subject: [PATCH 09/12] add screen to edit emojis reactions,
ReactionsSettingsView
---
.../Settings/ReactionsSettingsView.swift | 46 ++++---------------
1 file changed, 10 insertions(+), 36 deletions(-)
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 427e7bb5..037d9ffd 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -71,16 +71,9 @@ struct ReactionsSettingsView: View {
}
}
- Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
- selection: $settings.default_emoji_reaction) {
- ForEach(settings.emoji_reactions, id: \.self) { emoji in
- Text(emoji)
- }
- }
-
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
- EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
+ EmojiView(settings: settings, emoji: emoji, showActionButtons: $showActionButtons)
}
} header: {
HStack {
@@ -92,8 +85,8 @@ struct ReactionsSettingsView: View {
if recommended.count > 0 {
Section {
- List(Array(recommended), id: \.self) { emoji in
- EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
+ List(Array(settings.emoji_reactions), id: \.self) { emoji in
+ EmojiView(settings: settings, emoji: emoji, showActionButtons: $showActionButtons)
}
} header: {
Text(NSLocalizedString("Recommended Emojis", comment: "Section title for recommend emojis"))
@@ -102,7 +95,7 @@ struct ReactionsSettingsView: View {
}
}
}
- .navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
+ .navigationTitle(NSLocalizedString("Relays", comment: "Title of emojis view"))
.navigationBarTitleDisplayMode(.large)
.toolbar {
if showActionButtons {
@@ -117,6 +110,8 @@ struct ReactionsSettingsView: View {
}
}
+
+
func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
@@ -186,7 +181,6 @@ struct EmojiView: View {
@ObservedObject var settings: UserSettingsStore
let emoji: String
- let recommended: Bool
@Binding var showActionButtons: Bool
@@ -194,27 +188,19 @@ struct EmojiView: View {
Group {
HStack {
if showActionButtons {
- if recommended {
- AddButton()
- } else {
- RemoveButton()
- }
+ RemoveButton()
}
Text(emoji)
}
}
.swipeActions {
- if (!recommended){
- RemoveButton()
- .tint(.red)
- } else {
- AddButton()
- .tint(.green)
- }
+ RemoveButton()
+ .tint(.red)
}
.contextMenu {
CopyAction(emoji: emoji)
+ RemoveButton()
}
}
@@ -239,18 +225,6 @@ struct EmojiView: View {
.padding(.leading, 5)
}
}
-
- func AddButton() -> some View {
- Button(action: {
- settings.emoji_reactions.append(emoji)
- }) {
- Image(systemName: "plus.circle")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(.green)
- .padding(.leading, 5)
- }
- }
}
struct ReactionsSettingsView_Previews: PreviewProvider {
--
2.39.2 (Apple Git-143)
From 71bf487cc0b0fe529e712bc4a509ac0e64e183bd Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:19:25 -0500
Subject: [PATCH 10/12] make it not show edit for recommended list
---
damus/Views/Settings/ReactionsSettingsView.swift | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 037d9ffd..3fe56f9c 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -73,7 +73,7 @@ struct ReactionsSettingsView: View {
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
- EmojiView(settings: settings, emoji: emoji, showActionButtons: $showActionButtons)
+ EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
}
} header: {
HStack {
@@ -86,7 +86,7 @@ struct ReactionsSettingsView: View {
if recommended.count > 0 {
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
- EmojiView(settings: settings, emoji: emoji, showActionButtons: $showActionButtons)
+ EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
}
} header: {
Text(NSLocalizedString("Recommended Emojis", comment: "Section title for recommend emojis"))
@@ -181,13 +181,14 @@ struct EmojiView: View {
@ObservedObject var settings: UserSettingsStore
let emoji: String
+ let recommended: Bool
@Binding var showActionButtons: Bool
var body: some View {
Group {
HStack {
- if showActionButtons {
+ if showActionButtons && !recommended {
RemoveButton()
}
@@ -195,12 +196,13 @@ struct EmojiView: View {
}
}
.swipeActions {
- RemoveButton()
- .tint(.red)
+ if (!recommended){
+ RemoveButton()
+ .tint(.red)
+ }
}
.contextMenu {
CopyAction(emoji: emoji)
- RemoveButton()
}
}
--
2.39.2 (Apple Git-143)
From 7f85ff801aeb0231905ca99e538bf271268a9185 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 01:49:53 -0500
Subject: [PATCH 11/12] add ability to set default emoji and fix recommended
view
---
.../Settings/ReactionsSettingsView.swift | 36 +++++++++++++++----
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 3fe56f9c..427e7bb5 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -71,6 +71,13 @@ struct ReactionsSettingsView: View {
}
}
+ Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
+ selection: $settings.default_emoji_reaction) {
+ ForEach(settings.emoji_reactions, id: \.self) { emoji in
+ Text(emoji)
+ }
+ }
+
Section {
List(Array(settings.emoji_reactions), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: false, showActionButtons: $showActionButtons)
@@ -85,7 +92,7 @@ struct ReactionsSettingsView: View {
if recommended.count > 0 {
Section {
- List(Array(settings.emoji_reactions), id: \.self) { emoji in
+ List(Array(recommended), id: \.self) { emoji in
EmojiView(settings: settings, emoji: emoji, recommended: true, showActionButtons: $showActionButtons)
}
} header: {
@@ -95,7 +102,7 @@ struct ReactionsSettingsView: View {
}
}
}
- .navigationTitle(NSLocalizedString("Relays", comment: "Title of emojis view"))
+ .navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
.navigationBarTitleDisplayMode(.large)
.toolbar {
if showActionButtons {
@@ -110,8 +117,6 @@ struct ReactionsSettingsView: View {
}
}
-
-
func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
@@ -188,8 +193,12 @@ struct EmojiView: View {
var body: some View {
Group {
HStack {
- if showActionButtons && !recommended {
- RemoveButton()
+ if showActionButtons {
+ if recommended {
+ AddButton()
+ } else {
+ RemoveButton()
+ }
}
Text(emoji)
@@ -199,6 +208,9 @@ struct EmojiView: View {
if (!recommended){
RemoveButton()
.tint(.red)
+ } else {
+ AddButton()
+ .tint(.green)
}
}
.contextMenu {
@@ -227,6 +239,18 @@ struct EmojiView: View {
.padding(.leading, 5)
}
}
+
+ func AddButton() -> some View {
+ Button(action: {
+ settings.emoji_reactions.append(emoji)
+ }) {
+ Image(systemName: "plus.circle")
+ .resizable()
+ .frame(width: 20, height: 20)
+ .foregroundColor(.green)
+ .padding(.leading, 5)
+ }
+ }
}
struct ReactionsSettingsView_Previews: PreviewProvider {
--
2.39.2 (Apple Git-143)
From ae17cbd073884ca00e46942d467047679b129501 Mon Sep 17 00:00:00 2001
From: Suhail Saqan <
suhail...@gmail.com>
Date: Wed, 5 Jul 2023 14:44:52 -0500
Subject: [PATCH 12/12] fix `isValidEmoji`, recommended emoji settings store,
and change color background
---
damus/Views/ActionBar/EventActionBar.swift | 2 +-
.../Settings/ReactionsSettingsView.swift | 63 ++++++++++---------
2 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift
index bc286405..6bb4e27e 100644
--- a/damus/Views/ActionBar/EventActionBar.swift
+++ b/damus/Views/ActionBar/EventActionBar.swift
@@ -266,7 +266,7 @@ struct LikeButton: View {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(width: 250, height: 50)
- .foregroundColor(reactionsBGColor)
+ .foregroundColor(DamusColors.black)
.scaleEffect(Double(showReactionsBG), anchor: .topTrailing)
.animation(
.interpolatingSpring(stiffness: 170, damping: 15).delay(0.05),
diff --git a/damus/Views/Settings/ReactionsSettingsView.swift b/damus/Views/Settings/ReactionsSettingsView.swift
index 427e7bb5..e4876c96 100644
--- a/damus/Views/Settings/ReactionsSettingsView.swift
+++ b/damus/Views/Settings/ReactionsSettingsView.swift
@@ -8,6 +8,8 @@
import SwiftUI
import Combine
+let default_emoji_reactions = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]
+
struct ReactionsSettingsView: View {
@ObservedObject var settings: UserSettingsStore
@@ -16,19 +18,11 @@ struct ReactionsSettingsView: View {
@Environment(\.dismiss) var dismiss
- init(settings: UserSettingsStore) {
- self._settings = ObservedObject(initialValue: settings)
- }
-
var recommended: [String] {
return getMissingRecommendedEmojis(added: settings.emoji_reactions)
}
var body: some View {
- MainContent
- }
-
- var MainContent: some View {
Form {
Section {
AddEmojiView(emoji: $new_emoji)
@@ -45,7 +39,6 @@ struct ReactionsSettingsView: View {
if(!new_emoji.isEmpty) {
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted emoji.")) {
new_emoji = ""
- UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
.font(.system(size: 14, weight: .bold))
.frame(width: 80, height: 30)
@@ -117,31 +110,13 @@ struct ReactionsSettingsView: View {
}
}
- func getMissingRecommendedEmojis(added: [String], recommended: [String] = ["🤣", "🤙", "⚡", "💜", "🔥", "😀", "😃", "😄", "🥶"]) -> [String] {
+ func getMissingRecommendedEmojis(added: [String], recommended: [String] = default_emoji_reactions) -> [String] {
let addedSet = Set(added)
let missingEmojis = recommended.filter { !addedSet.contains($0) }
return missingEmojis
}
}
-func isValidEmoji(_ string: String) -> Bool {
- let count = string.count
-
- // Check if the string consists of a single character
- guard count == 1 else {
- return false
- }
-
- // Check if the character is an emoji
- for scalar in string.unicodeScalars {
- if !scalar.properties.isEmoji && !scalar.properties.isEmojiPresentation {
- return false
- }
- }
-
- return true
-}
-
struct AddEmojiView: View {
@Binding var emoji: String
@@ -253,6 +228,38 @@ struct EmojiView: View {
}
}
+/// From:
https://stackoverflow.com/a/39425959
+extension Character {
+ /// A simple emoji is one scalar and presented to the user as an Emoji
+ var isSimpleEmoji: Bool {
+ guard let firstScalar = unicodeScalars.first else { return false }
+ return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
+ }
+
+ /// Checks if the scalars will be merged into an emoji
+ var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }
+
+ var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
+}
+
+extension String {
+ var isSingleEmoji: Bool { count == 1 && containsEmoji }
+
+ var containsEmoji: Bool { contains { $0.isEmoji } }
+
+ var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }
+
+ var emojiString: String { emojis.map { String($0) }.reduce("", +) }
+
+ var emojis: [Character] { filter { $0.isEmoji } }
+
+ var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
+}
+
+func isValidEmoji(_ string: String) -> Bool {
+ return string.isSingleEmoji
+}
+
struct ReactionsSettingsView_Previews: PreviewProvider {
static var previews: some View {
ReactionsSettingsView(settings: UserSettingsStore())
--
2.39.2 (Apple Git-143)