From d969a2582719077aae184e69e8a32e46976ef36f Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 24 Sep 2024 16:30:08 +0100 Subject: [PATCH 01/20] add AccountFlagView --- Nos.xcodeproj/project.pbxproj | 4 + Nos/Views/Moderation/AccountFlagView.swift | 142 +++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 Nos/Views/Moderation/AccountFlagView.swift diff --git a/Nos.xcodeproj/project.pbxproj b/Nos.xcodeproj/project.pbxproj index 9128f0541..6f980a16b 100644 --- a/Nos.xcodeproj/project.pbxproj +++ b/Nos.xcodeproj/project.pbxproj @@ -111,6 +111,7 @@ 03FE3F7C2C87AC9900D25810 /* Event+InlineMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */; }; 03FE3F7D2C87AC9900D25810 /* Event+InlineMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */; }; 03FE3F8C2C87BC9500D25810 /* text_note_multiple_media.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FE3F8A2C87BC9500D25810 /* text_note_multiple_media.json */; }; + 041C56C42CA1B48E007D3BB2 /* AccountFlagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */; }; 042406F32C907A15008F2A21 /* NosToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042406F22C907A15008F2A21 /* NosToggle.swift */; }; 04368D2B2C99A2C400DEAA2E /* FlagOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */; }; 04368D312C99A78800DEAA2E /* NosRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D302C99A78800DEAA2E /* NosRadioButton.swift */; }; @@ -653,6 +654,7 @@ 03FE3F782C87A9D900D25810 /* EventError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventError.swift; sourceTree = ""; }; 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Event+InlineMetadata.swift"; sourceTree = ""; }; 03FE3F8A2C87BC9500D25810 /* text_note_multiple_media.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = text_note_multiple_media.json; sourceTree = ""; }; + 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFlagView.swift; sourceTree = ""; }; 042406F22C907A15008F2A21 /* NosToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NosToggle.swift; sourceTree = ""; }; 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagOption.swift; sourceTree = ""; }; 04368D302C99A78800DEAA2E /* NosRadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NosRadioButton.swift; sourceTree = ""; }; @@ -1411,6 +1413,7 @@ isa = PBXGroup; children = ( 04368D4A2C99CFC700DEAA2E /* ContentFlagView.swift */, + 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */, ); path = Moderation; sourceTree = ""; @@ -2343,6 +2346,7 @@ C9F75AD22A02D41E005BBE45 /* ComposerActionBar.swift in Sources */, 2D06BB9D2AE249D70085F509 /* ThreadRootView.swift in Sources */, C9DEBFD2298941000078B43A /* NosApp.swift in Sources */, + 041C56C42CA1B48E007D3BB2 /* AccountFlagView.swift in Sources */, 5BFBB28B2BD9D79F002E909F /* URLParser.swift in Sources */, C930055F2A6AF8320098CA9E /* LoadingContent.swift in Sources */, 5B79F6462BA11725002DA9BE /* WizardSheetVStack.swift in Sources */, diff --git a/Nos/Views/Moderation/AccountFlagView.swift b/Nos/Views/Moderation/AccountFlagView.swift new file mode 100644 index 000000000..ae8472ab0 --- /dev/null +++ b/Nos/Views/Moderation/AccountFlagView.swift @@ -0,0 +1,142 @@ +import SwiftUI + +/// Displays pickers for selecting account flag option category, with additional stages shown +/// based on previous selections. +struct AccountFlagView: View { + @Binding var selectedFlagOptionCategory: FlagOption? + @Binding var selectedSendOptionCategory: FlagOption? + @Binding var selectedVisibilityOptionCategory: FlagOption? + @Binding var showSuccessView: Bool + var sendAction: () -> Void + @Environment(\.dismiss) private var dismiss + + var body: some View { + ZStack { + Color.appBg.ignoresSafeArea() + Group { + if showSuccessView { + successView + } else { + categoryView + } + } + .padding() + .nosNavigationBar(title: .localizable.flagContent) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { + dismiss() + resetSelections() + }, label: { + Text(.localizable.cancel) + .foregroundColor(.primaryTxt) + }) + .opacity(showSuccessView ? 0 : 1) + .disabled(showSuccessView) + } + ToolbarItem(placement: .navigationBarTrailing) { + ActionButton( + title: showSuccessView ? .localizable.done : .localizable.send, + action: { + if showSuccessView { + dismiss() + } else { + sendAction() + } + } + ) + .opacity(selectedVisibilityOptionCategory == nil ? 0.5 : 1) + .disabled(selectedVisibilityOptionCategory == nil) + } + } + } + } + + private func resetSelections() { + selectedFlagOptionCategory = nil + selectedSendOptionCategory = nil + selectedVisibilityOptionCategory = nil + } + + private var categoryView: some View { + ScrollView(showsIndicators: false) { + VStack(spacing: 30) { + FlagOptionPicker( + selectedOption: $selectedFlagOptionCategory, + options: FlagOption.flagContentCategories, + title: String(localized: .localizable.reportContent), + subtitle: String(localized: .localizable.reportContentMessage) + ) + + if selectedFlagOptionCategory != nil { + FlagOptionPicker( + selectedOption: $selectedSendOptionCategory, + options: FlagOption.flagAccountSendCategories, + title: String(localized: .localizable.flagContentSendTitle), + subtitle: nil + ) + .transition(.move(edge: .leading).combined(with: .opacity)) + } + + if selectedSendOptionCategory != nil { + FlagOptionPicker( + selectedOption: $selectedVisibilityOptionCategory, + options: FlagOption.flagAccountVisibilityCategories, + title: String(localized: .localizable.flagAccountMuteCategoryTitle), + subtitle: nil + ) + .transition(.move(edge: .leading).combined(with: .opacity)) + } + } + } + .animation(.easeInOut, value: selectedFlagOptionCategory) + .animation(.easeInOut, value: selectedSendOptionCategory) + } + + private var successView: some View { + VStack(spacing: 30) { + Image.circularCheckmark + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 116) + + Text(String(localized: .localizable.thanksForTag)) + .foregroundColor(.primaryTxt) + .font(.clarity(.regular, textStyle: .title2)) + .padding(.horizontal, 62) + + Text(String(localized: .localizable.keepOnHelpingUs)) + .padding(.horizontal, 68) + .foregroundColor(.secondaryTxt) + .multilineTextAlignment(.center) + .lineSpacing(6) + .font(.clarity(.regular, textStyle: .subheadline)) + } + } +} + +#Preview { + struct PreviewWrapper: View { + @State private var selectedFlagOptionCategory: FlagOption? + @State private var selectedSendOptionCategory: FlagOption? + @State private var selectedVisibilityOptionCategory: FlagOption? + @State private var showSuccessView = false + + var body: some View { + NavigationStack { + AccountFlagView( + selectedFlagOptionCategory: $selectedFlagOptionCategory, + selectedSendOptionCategory: $selectedSendOptionCategory, + selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, + showSuccessView: $showSuccessView, + sendAction: {} + ) + } + .onAppear { + selectedFlagOptionCategory = nil + } + .background(Color.appBg) + } + } + return PreviewWrapper() +} From ed7d94a3d50b46e8b307555a1baf323e6a2f41d6 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 24 Sep 2024 16:30:49 +0100 Subject: [PATCH 02/20] update ReportMenuModifier --- Nos/Views/Modifiers/ReportMenuModifier.swift | 34 +++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 0137495c9..77bbe062e 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -17,6 +17,7 @@ struct ReportMenuModifier: ViewModifier { @State private var confirmationDialogState: ConfirmationDialogState? @State private var selectedFlagOption: FlagOption? @State private var selectedFlagSendOption: FlagOption? + @State private var selectedVisibilityOptionCategory: FlagOption? @State private var showFlagSuccessView = false @Environment(\.managedObjectContext) private var viewContext @@ -54,7 +55,25 @@ struct ReportMenuModifier: ViewModifier { } } case .author: - oldModerationFlow(content: content) + content + .sheet(isPresented: $isPresented) { + NavigationStack { + AccountFlagView( + selectedFlagOptionCategory: $selectedFlagOption, + selectedSendOptionCategory: $selectedFlagSendOption, + selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, + showSuccessView: $showFlagSuccessView, + sendAction: { + if let selectCategory = selectedVisibilityOptionCategory?.category { + publishReportForNewModerationFlow(selectCategory) + determineFlaggedAccoutVisibility { + showFlagSuccessView = true + } + } + } + ) + } + } } } @@ -182,6 +201,19 @@ struct ReportMenuModifier: ViewModifier { } } + /// Determines the visibility status for a flagged account and applies the appropriate action, such as muting the account. + /// If the selected visibility category is `.mute`, the author of the `reportedObject` will be muted. + /// - Parameter completion: A closure that is executed once the visibility determination and actions (if any) are complete. + private func determineFlaggedAccoutVisibility(completion: () -> Void) { + if let author = reportedObject.author { + if case .visibility(let visibilityCategory) = selectedVisibilityOptionCategory?.category, + visibilityCategory == .mute { + mute(author: author) + } + completion() + } + } + /// An enum to simplify the user selection through the sequence of connected /// dialogs enum UserSelection: Equatable { From a9974a021ff81fa4bddf449dbc935b2c53dcdcae Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 24 Sep 2024 18:02:37 +0100 Subject: [PATCH 03/20] add card shadow --- Nos/Views/Components/FlagOptionPicker.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Nos/Views/Components/FlagOptionPicker.swift b/Nos/Views/Components/FlagOptionPicker.swift index 0be8a6df6..9fb2772e8 100644 --- a/Nos/Views/Components/FlagOptionPicker.swift +++ b/Nos/Views/Components/FlagOptionPicker.swift @@ -40,6 +40,7 @@ struct FlagOptionPicker: View { } .background(LinearGradient.cardBackground) .clipShape(RoundedRectangle(cornerRadius: 15)) + .mimicCardButtonStyle() } } From 8b5f88aa5a8c0a8335608b32185eb8affbcad5b4 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 24 Sep 2024 18:03:02 +0100 Subject: [PATCH 04/20] rename successView --- Nos/Views/Moderation/ContentFlagView.swift | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Nos/Views/Moderation/ContentFlagView.swift b/Nos/Views/Moderation/ContentFlagView.swift index 81e7e1b7b..260c4718b 100644 --- a/Nos/Views/Moderation/ContentFlagView.swift +++ b/Nos/Views/Moderation/ContentFlagView.swift @@ -14,7 +14,7 @@ struct ContentFlagView: View { Color.appBg.ignoresSafeArea() Group { if showSuccessView { - successView + flagSuccessView } else { categoryView } @@ -81,26 +81,26 @@ struct ContentFlagView: View { } } } +} - private var successView: some View { - VStack(spacing: 30) { - Image.circularCheckmark - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: 116) +var flagSuccessView: some View { + VStack(spacing: 30) { + Image.circularCheckmark + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 116) - Text(String(localized: .localizable.thanksForTag)) - .foregroundColor(.primaryTxt) - .font(.clarity(.regular, textStyle: .title2)) - .padding(.horizontal, 62) + Text(String(localized: .localizable.thanksForTag)) + .foregroundColor(.primaryTxt) + .font(.clarity(.regular, textStyle: .title2)) + .padding(.horizontal, 62) - Text(String(localized: .localizable.keepOnHelpingUs)) - .padding(.horizontal, 68) - .foregroundColor(.secondaryTxt) - .multilineTextAlignment(.center) - .lineSpacing(6) - .font(.clarity(.regular, textStyle: .subheadline)) - } + Text(String(localized: .localizable.keepOnHelpingUs)) + .padding(.horizontal, 68) + .foregroundColor(.secondaryTxt) + .multilineTextAlignment(.center) + .lineSpacing(6) + .font(.clarity(.regular, textStyle: .subheadline)) } } From a4b75d78ce6e0cd5bddf24db304ddd1c26e979c8 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 24 Sep 2024 18:03:23 +0100 Subject: [PATCH 05/20] refactor code --- Nos/Views/Moderation/AccountFlagView.swift | 23 +------------------- Nos/Views/Modifiers/ReportMenuModifier.swift | 5 ++--- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/Nos/Views/Moderation/AccountFlagView.swift b/Nos/Views/Moderation/AccountFlagView.swift index ae8472ab0..916099929 100644 --- a/Nos/Views/Moderation/AccountFlagView.swift +++ b/Nos/Views/Moderation/AccountFlagView.swift @@ -15,7 +15,7 @@ struct AccountFlagView: View { Color.appBg.ignoresSafeArea() Group { if showSuccessView { - successView + flagSuccessView } else { categoryView } @@ -92,27 +92,6 @@ struct AccountFlagView: View { .animation(.easeInOut, value: selectedFlagOptionCategory) .animation(.easeInOut, value: selectedSendOptionCategory) } - - private var successView: some View { - VStack(spacing: 30) { - Image.circularCheckmark - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: 116) - - Text(String(localized: .localizable.thanksForTag)) - .foregroundColor(.primaryTxt) - .font(.clarity(.regular, textStyle: .title2)) - .padding(.horizontal, 62) - - Text(String(localized: .localizable.keepOnHelpingUs)) - .padding(.horizontal, 68) - .foregroundColor(.secondaryTxt) - .multilineTextAlignment(.center) - .lineSpacing(6) - .font(.clarity(.regular, textStyle: .subheadline)) - } - } } #Preview { diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 77bbe062e..31a1abf59 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -201,9 +201,8 @@ struct ReportMenuModifier: ViewModifier { } } - /// Determines the visibility status for a flagged account and applies the appropriate action, such as muting the account. - /// If the selected visibility category is `.mute`, the author of the `reportedObject` will be muted. - /// - Parameter completion: A closure that is executed once the visibility determination and actions (if any) are complete. + /// Determines the visibility status for a flagged account and applies the appropriate action. + /// - Parameter completion: A closure that is executed once the necessary actions are complete. private func determineFlaggedAccoutVisibility(completion: () -> Void) { if let author = reportedObject.author { if case .visibility(let visibilityCategory) = selectedVisibilityOptionCategory?.category, From 73caed73182ba15504ce2bf7f86e571f5ef7ea15 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Wed, 25 Sep 2024 14:46:18 +0100 Subject: [PATCH 06/20] update AccountFlagView --- Nos/Views/Moderation/AccountFlagView.swift | 23 +++++++++++++++----- Nos/Views/Modifiers/ReportMenuModifier.swift | 3 ++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Nos/Views/Moderation/AccountFlagView.swift b/Nos/Views/Moderation/AccountFlagView.swift index 916099929..ad9924b5b 100644 --- a/Nos/Views/Moderation/AccountFlagView.swift +++ b/Nos/Views/Moderation/AccountFlagView.swift @@ -7,9 +7,15 @@ struct AccountFlagView: View { @Binding var selectedSendOptionCategory: FlagOption? @Binding var selectedVisibilityOptionCategory: FlagOption? @Binding var showSuccessView: Bool + + /// The target of the report. + let flagTarget: ReportTarget var sendAction: () -> Void + @Environment(\.dismiss) private var dismiss + @State private var flagCategories: [FlagOption] = [] + var body: some View { ZStack { Color.appBg.ignoresSafeArea() @@ -21,7 +27,7 @@ struct AccountFlagView: View { } } .padding() - .nosNavigationBar(title: .localizable.flagContent) + .nosNavigationBar(title: .localizable.flagUserTitle) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(action: { @@ -40,6 +46,8 @@ struct AccountFlagView: View { action: { if showSuccessView { dismiss() + resetSelections() + showSuccessView = false } else { sendAction() } @@ -50,6 +58,9 @@ struct AccountFlagView: View { } } } + .onAppear { + flagCategories = FlagOption.createFlagCategories(for: flagTarget) + } } private func resetSelections() { @@ -59,13 +70,13 @@ struct AccountFlagView: View { } private var categoryView: some View { - ScrollView(showsIndicators: false) { + ScrollView { VStack(spacing: 30) { FlagOptionPicker( selectedOption: $selectedFlagOptionCategory, - options: FlagOption.flagContentCategories, - title: String(localized: .localizable.reportContent), - subtitle: String(localized: .localizable.reportContentMessage) + options: flagCategories, + title: String(localized: .localizable.flagAccountCategoryTitle), + subtitle: String(localized: .localizable.flagAccountCategoryDescription) ) if selectedFlagOptionCategory != nil { @@ -100,6 +111,7 @@ struct AccountFlagView: View { @State private var selectedSendOptionCategory: FlagOption? @State private var selectedVisibilityOptionCategory: FlagOption? @State private var showSuccessView = false + let author = Author() var body: some View { NavigationStack { @@ -108,6 +120,7 @@ struct AccountFlagView: View { selectedSendOptionCategory: $selectedSendOptionCategory, selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, showSuccessView: $showSuccessView, + flagTarget: .author(author), sendAction: {} ) } diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 5b81d71c9..102a49c4e 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -63,7 +63,8 @@ struct ReportMenuModifier: ViewModifier { selectedFlagOptionCategory: $selectedFlagOption, selectedSendOptionCategory: $selectedFlagSendOption, selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, - showSuccessView: $showFlagSuccessView, + showSuccessView: $showFlagSuccessView, + flagTarget: reportedObject, sendAction: { if let selectCategory = selectedVisibilityOptionCategory?.category { publishReportForNewModerationFlow(selectCategory) From dff3b996b0dac772a947d42bb721d9ef50c3ed30 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 26 Sep 2024 16:07:51 +0100 Subject: [PATCH 07/20] rename AccountFlagView to UserFlagView --- Nos.xcodeproj/project.pbxproj | 8 +++--- ...countFlagView.swift => UserFlagView.swift} | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) rename Nos/Views/Moderation/{AccountFlagView.swift => UserFlagView.swift} (84%) diff --git a/Nos.xcodeproj/project.pbxproj b/Nos.xcodeproj/project.pbxproj index 903990414..1a2b8c5b8 100644 --- a/Nos.xcodeproj/project.pbxproj +++ b/Nos.xcodeproj/project.pbxproj @@ -114,7 +114,7 @@ 03FE3F7C2C87AC9900D25810 /* Event+InlineMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */; }; 03FE3F7D2C87AC9900D25810 /* Event+InlineMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */; }; 03FE3F8C2C87BC9500D25810 /* text_note_multiple_media.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FE3F8A2C87BC9500D25810 /* text_note_multiple_media.json */; }; - 041C56C42CA1B48E007D3BB2 /* AccountFlagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */; }; + 041C56C42CA1B48E007D3BB2 /* UserFlagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041C56C32CA1B48E007D3BB2 /* UserFlagView.swift */; }; 042406F32C907A15008F2A21 /* NosToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042406F22C907A15008F2A21 /* NosToggle.swift */; }; 04368D2B2C99A2C400DEAA2E /* FlagOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */; }; 04368D312C99A78800DEAA2E /* NosRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D302C99A78800DEAA2E /* NosRadioButton.swift */; }; @@ -661,7 +661,7 @@ 03FE3F782C87A9D900D25810 /* EventError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventError.swift; sourceTree = ""; }; 03FE3F7B2C87AC9900D25810 /* Event+InlineMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Event+InlineMetadata.swift"; sourceTree = ""; }; 03FE3F8A2C87BC9500D25810 /* text_note_multiple_media.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = text_note_multiple_media.json; sourceTree = ""; }; - 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFlagView.swift; sourceTree = ""; }; + 041C56C32CA1B48E007D3BB2 /* UserFlagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFlagView.swift; sourceTree = ""; }; 042406F22C907A15008F2A21 /* NosToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NosToggle.swift; sourceTree = ""; }; 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagOption.swift; sourceTree = ""; }; 04368D302C99A78800DEAA2E /* NosRadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NosRadioButton.swift; sourceTree = ""; }; @@ -1423,7 +1423,7 @@ isa = PBXGroup; children = ( 04368D4A2C99CFC700DEAA2E /* ContentFlagView.swift */, - 041C56C32CA1B48E007D3BB2 /* AccountFlagView.swift */, + 041C56C32CA1B48E007D3BB2 /* UserFlagView.swift */, ); path = Moderation; sourceTree = ""; @@ -2358,7 +2358,7 @@ C9F75AD22A02D41E005BBE45 /* ComposerActionBar.swift in Sources */, 2D06BB9D2AE249D70085F509 /* ThreadRootView.swift in Sources */, C9DEBFD2298941000078B43A /* NosApp.swift in Sources */, - 041C56C42CA1B48E007D3BB2 /* AccountFlagView.swift in Sources */, + 041C56C42CA1B48E007D3BB2 /* UserFlagView.swift in Sources */, 5BFBB28B2BD9D79F002E909F /* URLParser.swift in Sources */, C930055F2A6AF8320098CA9E /* LoadingContent.swift in Sources */, 5B79F6462BA11725002DA9BE /* WizardSheetVStack.swift in Sources */, diff --git a/Nos/Views/Moderation/AccountFlagView.swift b/Nos/Views/Moderation/UserFlagView.swift similarity index 84% rename from Nos/Views/Moderation/AccountFlagView.swift rename to Nos/Views/Moderation/UserFlagView.swift index ad9924b5b..44a341df0 100644 --- a/Nos/Views/Moderation/AccountFlagView.swift +++ b/Nos/Views/Moderation/UserFlagView.swift @@ -1,8 +1,8 @@ import SwiftUI -/// Displays pickers for selecting account flag option category, with additional stages shown +/// Displays pickers for selecting user flag options, with additional stages shown /// based on previous selections. -struct AccountFlagView: View { +struct UserFlagView: View { @Binding var selectedFlagOptionCategory: FlagOption? @Binding var selectedSendOptionCategory: FlagOption? @Binding var selectedVisibilityOptionCategory: FlagOption? @@ -73,17 +73,19 @@ struct AccountFlagView: View { ScrollView { VStack(spacing: 30) { FlagOptionPicker( - selectedOption: $selectedFlagOptionCategory, + previousSelection: .constant(nil), + currentSelection: $selectedFlagOptionCategory, options: flagCategories, - title: String(localized: .localizable.flagAccountCategoryTitle), - subtitle: String(localized: .localizable.flagAccountCategoryDescription) + title: String(localized: .localizable.flagUserCategoryTitle), + subtitle: String(localized: .localizable.flagUserCategoryDescription) ) if selectedFlagOptionCategory != nil { FlagOptionPicker( - selectedOption: $selectedSendOptionCategory, - options: FlagOption.flagAccountSendCategories, - title: String(localized: .localizable.flagContentSendTitle), + previousSelection: $selectedFlagOptionCategory, + currentSelection: $selectedSendOptionCategory, + options: FlagOption.flagUserSendOptions, + title: String(localized: .localizable.flagSendTitle), subtitle: nil ) .transition(.move(edge: .leading).combined(with: .opacity)) @@ -91,9 +93,10 @@ struct AccountFlagView: View { if selectedSendOptionCategory != nil { FlagOptionPicker( - selectedOption: $selectedVisibilityOptionCategory, - options: FlagOption.flagAccountVisibilityCategories, - title: String(localized: .localizable.flagAccountMuteCategoryTitle), + previousSelection: .constant(nil), + currentSelection: $selectedVisibilityOptionCategory, + options: FlagOption.flagUserVisibilityOptions, + title: String(localized: .localizable.flagUserMuteCategoryTitle), subtitle: nil ) .transition(.move(edge: .leading).combined(with: .opacity)) @@ -115,7 +118,7 @@ struct AccountFlagView: View { var body: some View { NavigationStack { - AccountFlagView( + UserFlagView( selectedFlagOptionCategory: $selectedFlagOptionCategory, selectedSendOptionCategory: $selectedSendOptionCategory, selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, From f36709f215af794ff7edf85579ba863ff9971590 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 26 Sep 2024 16:09:46 +0100 Subject: [PATCH 08/20] add unmute functionality --- Nos/Views/Modifiers/ReportMenuModifier.swift | 23 ++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 07ada5d1d..cb2df2a35 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -59,7 +59,7 @@ struct ReportMenuModifier: ViewModifier { content .sheet(isPresented: $isPresented) { NavigationStack { - AccountFlagView( + UserFlagView( selectedFlagOptionCategory: $selectedFlagOption, selectedSendOptionCategory: $selectedFlagSendOption, selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, @@ -202,14 +202,29 @@ struct ReportMenuModifier: ViewModifier { } } } - + + func unmute(author: Author) { + Task { + do { + try await author.unmute(viewContext: viewContext) + } catch { + Log.error(error.localizedDescription) + } + } + } + /// Determines the visibility status for a flagged account and applies the appropriate action. /// - Parameter completion: A closure that is executed once the necessary actions are complete. private func determineFlaggedAccoutVisibility(completion: () -> Void) { if let author = reportedObject.author { - if case .visibility(let visibilityCategory) = selectedVisibilityOptionCategory?.category, - visibilityCategory == .mute { + guard case .visibility(let visibilityCategory) = selectedVisibilityOptionCategory?.category else { return } + + if visibilityCategory == .mute { + guard !author.muted else { return } mute(author: author) + } else { + guard author.muted else { return } + unmute(author: author) } completion() } From 6f62d042cadc9a75b0e1ce410002060737415a75 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 26 Sep 2024 16:10:48 +0100 Subject: [PATCH 09/20] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a629dec1..0c76a74d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added SwiftSoup to parse Open Graph metadata. [#1165](https://github.com/planetary-social/nos/issues/1165) - Parse Open Graph metadata whenever an event contains a URL, doesn’t have `imeta` tags, and the URL points to an HTML document. [#1425](https://github.com/planetary-social/nos/issues/1425) - Added a new flow to flag notes. Currently behind the “Enable new moderation flow” feature flag. [#1489](https://github.com/planetary-social/nos/issues/1489) +- Added a new flow to flag users. Currently behind the “Enable new moderation flow” feature flag. [#1493](https://github.com/planetary-social/nos/issues/1493) ## [0.1.26] - 2024-09-09Z From 29ced9f3e10c784578e71e40ca777a10a9b6031b Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Mon, 30 Sep 2024 16:37:25 +0100 Subject: [PATCH 10/20] move FlagSuccessView to different flag --- Nos.xcodeproj/project.pbxproj | 28 ++++++++++++--------- Nos/Views/Moderation/FlagSuccessView.swift | 29 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 Nos/Views/Moderation/FlagSuccessView.swift diff --git a/Nos.xcodeproj/project.pbxproj b/Nos.xcodeproj/project.pbxproj index 5bac6fa02..e3fe4a46b 100644 --- a/Nos.xcodeproj/project.pbxproj +++ b/Nos.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 04368D2B2C99A2C400DEAA2E /* FlagOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */; }; 04368D312C99A78800DEAA2E /* NosRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D302C99A78800DEAA2E /* NosRadioButton.swift */; }; 04368D4B2C99CFC700DEAA2E /* ContentFlagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04368D4A2C99CFC700DEAA2E /* ContentFlagView.swift */; }; + 045EDCF32CAAF47600B67964 /* FlagSuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045EDCF22CAAF47600B67964 /* FlagSuccessView.swift */; }; 0496D6312C975E6900D29375 /* FlagOptionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0496D6302C975E6900D29375 /* FlagOptionPicker.swift */; }; 2D06BB9D2AE249D70085F509 /* ThreadRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */; }; 2D4010A22AD87DF300F93AD4 /* KnownFollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */; }; @@ -666,6 +667,7 @@ 04368D2A2C99A2C400DEAA2E /* FlagOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagOption.swift; sourceTree = ""; }; 04368D302C99A78800DEAA2E /* NosRadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NosRadioButton.swift; sourceTree = ""; }; 04368D4A2C99CFC700DEAA2E /* ContentFlagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentFlagView.swift; sourceTree = ""; }; + 045EDCF22CAAF47600B67964 /* FlagSuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagSuccessView.swift; sourceTree = ""; }; 0496D6302C975E6900D29375 /* FlagOptionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagOptionPicker.swift; sourceTree = ""; }; 2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadRootView.swift; sourceTree = ""; }; 2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KnownFollowersView.swift; sourceTree = ""; }; @@ -1424,6 +1426,7 @@ children = ( 04368D4A2C99CFC700DEAA2E /* ContentFlagView.swift */, 041C56C32CA1B48E007D3BB2 /* UserFlagView.swift */, + 045EDCF22CAAF47600B67964 /* FlagSuccessView.swift */, ); path = Moderation; sourceTree = ""; @@ -2098,7 +2101,7 @@ C9B737702AB24D5F00398BE7 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */, C91565BF2B2368FA0068EECA /* XCRemoteSwiftPackageReference "ViewInspector" */, 3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */, - C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */, + C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */, C9FD35112BCED5A6008F8D95 /* XCRemoteSwiftPackageReference "nostr-sdk-ios" */, 03C49ABE2C938A9C00502321 /* XCRemoteSwiftPackageReference "SwiftSoup" */, ); @@ -2286,6 +2289,7 @@ C9C2B78229E0735400548B4A /* RelaySubscriptionManager.swift in Sources */, 3FFB1D9629A6BBEC002A755D /* Collection+SafeSubscript.swift in Sources */, A34E439929A522F20057AFCB /* CurrentUser.swift in Sources */, + 045EDCF32CAAF47600B67964 /* FlagSuccessView.swift in Sources */, 03E1812F2C753C9B00886CC6 /* ImageButton.swift in Sources */, C9A0DADD29C689C900466635 /* NosNavigationBar.swift in Sources */, 3F30020529C1FDD9003D4F8B /* OnboardingStartView.swift in Sources */, @@ -2719,11 +2723,11 @@ /* Begin PBXTargetDependency section */ 3AD3185D2B294E9000026B07 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = 3AD3185C2B294E9000026B07 /* plugin:XCStringsToolPlugin */; + productRef = 3AD3185C2B294E9000026B07 /* XCStringsToolPlugin */; }; 3AEABEF32B2BF806001BC933 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = 3AEABEF22B2BF806001BC933 /* plugin:XCStringsToolPlugin */; + productRef = 3AEABEF22B2BF806001BC933 /* XCStringsToolPlugin */; }; C90862C229E9804B00C35A71 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -2732,11 +2736,11 @@ }; C9A6C7442AD83F7A001F9500 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = C9A6C7432AD83F7A001F9500 /* plugin:SwiftGenPlugin */; + productRef = C9A6C7432AD83F7A001F9500 /* SwiftGenPlugin */; }; C9D573402AB24A3700E06BB4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = C9D5733F2AB24A3700E06BB4 /* plugin:SwiftGenPlugin */; + productRef = C9D5733F2AB24A3700E06BB4 /* SwiftGenPlugin */; }; C9DEBFE6298941020078B43A /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -3579,7 +3583,7 @@ minimumVersion = 4.0.0; }; }; - C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */ = { + C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/GigaBitcoin/secp256k1.swift"; requirement = { @@ -3608,12 +3612,12 @@ package = 03C49ABE2C938A9C00502321 /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; - 3AD3185C2B294E9000026B07 /* plugin:XCStringsToolPlugin */ = { + 3AD3185C2B294E9000026B07 /* XCStringsToolPlugin */ = { isa = XCSwiftPackageProductDependency; package = 3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */; productName = "plugin:XCStringsToolPlugin"; }; - 3AEABEF22B2BF806001BC933 /* plugin:XCStringsToolPlugin */ = { + 3AEABEF22B2BF806001BC933 /* XCStringsToolPlugin */ = { isa = XCSwiftPackageProductDependency; package = 3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */; productName = "plugin:XCStringsToolPlugin"; @@ -3687,7 +3691,7 @@ package = C99DBF7C2A9E81CF00F7068F /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */; productName = SDWebImageSwiftUI; }; - C9A6C7432AD83F7A001F9500 /* plugin:SwiftGenPlugin */ = { + C9A6C7432AD83F7A001F9500 /* SwiftGenPlugin */ = { isa = XCSwiftPackageProductDependency; package = C9B737702AB24D5F00398BE7 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */; productName = "plugin:SwiftGenPlugin"; @@ -3707,7 +3711,7 @@ package = C9B71DBC2A8E9BAD0031ED9F /* XCRemoteSwiftPackageReference "sentry-cocoa" */; productName = Sentry; }; - C9D5733F2AB24A3700E06BB4 /* plugin:SwiftGenPlugin */ = { + C9D5733F2AB24A3700E06BB4 /* SwiftGenPlugin */ = { isa = XCSwiftPackageProductDependency; package = C9C8450C2AB249DB00654BC1 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */; productName = "plugin:SwiftGenPlugin"; @@ -3719,12 +3723,12 @@ }; C9FD34F52BCEC89C008F8D95 /* secp256k1 */ = { isa = XCSwiftPackageProductDependency; - package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */; + package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */; productName = secp256k1; }; C9FD34F72BCEC8B5008F8D95 /* secp256k1 */ = { isa = XCSwiftPackageProductDependency; - package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */; + package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */; productName = secp256k1; }; C9FD35122BCED5A6008F8D95 /* NostrSDK */ = { diff --git a/Nos/Views/Moderation/FlagSuccessView.swift b/Nos/Views/Moderation/FlagSuccessView.swift new file mode 100644 index 000000000..7c9169f97 --- /dev/null +++ b/Nos/Views/Moderation/FlagSuccessView.swift @@ -0,0 +1,29 @@ +import SwiftUI + +/// Shows a success message after the user has successfully flagged a user or content +struct FlagSuccessView: View { + var body: some View { + VStack(spacing: 30) { + Image.circularCheckmark + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 116) + + Text(String(localized: .localizable.thanksForTag)) + .foregroundColor(.primaryTxt) + .font(.clarity(.regular, textStyle: .title2)) + .padding(.horizontal, 62) + + Text(String(localized: .localizable.keepOnHelpingUs)) + .padding(.horizontal, 68) + .foregroundColor(.secondaryTxt) + .multilineTextAlignment(.center) + .lineSpacing(6) + .font(.clarity(.regular, textStyle: .subheadline)) + } + } +} + +#Preview { + FlagSuccessView() +} From da9771a9ba941708ba26e3a01277bbb18c87ec6e Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Mon, 30 Sep 2024 16:37:56 +0100 Subject: [PATCH 11/20] remove unnecessary category from name --- Nos/Views/Moderation/ContentFlagView.swift | 23 +--------- Nos/Views/Moderation/UserFlagView.swift | 48 ++++++++++---------- Nos/Views/Modifiers/ReportMenuModifier.swift | 12 ++--- 3 files changed, 31 insertions(+), 52 deletions(-) diff --git a/Nos/Views/Moderation/ContentFlagView.swift b/Nos/Views/Moderation/ContentFlagView.swift index c697eed6e..3401400d0 100644 --- a/Nos/Views/Moderation/ContentFlagView.swift +++ b/Nos/Views/Moderation/ContentFlagView.swift @@ -20,7 +20,7 @@ struct ContentFlagView: View { Color.appBg.ignoresSafeArea() Group { if showSuccessView { - flagSuccessView + FlagSuccessView() } else { categoryView } @@ -94,27 +94,6 @@ struct ContentFlagView: View { } } -var flagSuccessView: some View { - VStack(spacing: 30) { - Image.circularCheckmark - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: 116) - - Text(String(localized: .localizable.thanksForTag)) - .foregroundColor(.primaryTxt) - .font(.clarity(.regular, textStyle: .title2)) - .padding(.horizontal, 62) - - Text(String(localized: .localizable.keepOnHelpingUs)) - .padding(.horizontal, 68) - .foregroundColor(.secondaryTxt) - .multilineTextAlignment(.center) - .lineSpacing(6) - .font(.clarity(.regular, textStyle: .subheadline)) - } -} - #Preview { struct PreviewWrapper: View { @State private var selectedFlagOptionCategory: FlagOption? diff --git a/Nos/Views/Moderation/UserFlagView.swift b/Nos/Views/Moderation/UserFlagView.swift index 44a341df0..b55fba886 100644 --- a/Nos/Views/Moderation/UserFlagView.swift +++ b/Nos/Views/Moderation/UserFlagView.swift @@ -3,9 +3,9 @@ import SwiftUI /// Displays pickers for selecting user flag options, with additional stages shown /// based on previous selections. struct UserFlagView: View { - @Binding var selectedFlagOptionCategory: FlagOption? - @Binding var selectedSendOptionCategory: FlagOption? - @Binding var selectedVisibilityOptionCategory: FlagOption? + @Binding var selectedFlagOption: FlagOption? + @Binding var selectedSendOption: FlagOption? + @Binding var selectedVisibilityOption: FlagOption? @Binding var showSuccessView: Bool /// The target of the report. @@ -21,7 +21,7 @@ struct UserFlagView: View { Color.appBg.ignoresSafeArea() Group { if showSuccessView { - flagSuccessView + FlagSuccessView() } else { categoryView } @@ -53,8 +53,8 @@ struct UserFlagView: View { } } ) - .opacity(selectedVisibilityOptionCategory == nil ? 0.5 : 1) - .disabled(selectedVisibilityOptionCategory == nil) + .opacity(selectedVisibilityOption == nil ? 0.5 : 1) + .disabled(selectedVisibilityOption == nil) } } } @@ -64,9 +64,9 @@ struct UserFlagView: View { } private func resetSelections() { - selectedFlagOptionCategory = nil - selectedSendOptionCategory = nil - selectedVisibilityOptionCategory = nil + selectedFlagOption = nil + selectedSendOption = nil + selectedVisibilityOption = nil } private var categoryView: some View { @@ -74,16 +74,16 @@ struct UserFlagView: View { VStack(spacing: 30) { FlagOptionPicker( previousSelection: .constant(nil), - currentSelection: $selectedFlagOptionCategory, + currentSelection: $selectedFlagOption, options: flagCategories, title: String(localized: .localizable.flagUserCategoryTitle), subtitle: String(localized: .localizable.flagUserCategoryDescription) ) - if selectedFlagOptionCategory != nil { + if selectedFlagOption != nil { FlagOptionPicker( - previousSelection: $selectedFlagOptionCategory, - currentSelection: $selectedSendOptionCategory, + previousSelection: $selectedFlagOption, + currentSelection: $selectedSendOption, options: FlagOption.flagUserSendOptions, title: String(localized: .localizable.flagSendTitle), subtitle: nil @@ -91,10 +91,10 @@ struct UserFlagView: View { .transition(.move(edge: .leading).combined(with: .opacity)) } - if selectedSendOptionCategory != nil { + if selectedSendOption != nil { FlagOptionPicker( previousSelection: .constant(nil), - currentSelection: $selectedVisibilityOptionCategory, + currentSelection: $selectedVisibilityOption, options: FlagOption.flagUserVisibilityOptions, title: String(localized: .localizable.flagUserMuteCategoryTitle), subtitle: nil @@ -103,32 +103,32 @@ struct UserFlagView: View { } } } - .animation(.easeInOut, value: selectedFlagOptionCategory) - .animation(.easeInOut, value: selectedSendOptionCategory) + .animation(.easeInOut, value: selectedFlagOption) + .animation(.easeInOut, value: selectedSendOption) } } #Preview { struct PreviewWrapper: View { - @State private var selectedFlagOptionCategory: FlagOption? - @State private var selectedSendOptionCategory: FlagOption? - @State private var selectedVisibilityOptionCategory: FlagOption? + @State private var selectedFlagOption: FlagOption? + @State private var selectedSendOption: FlagOption? + @State private var selectedVisibilityOption: FlagOption? @State private var showSuccessView = false let author = Author() var body: some View { NavigationStack { UserFlagView( - selectedFlagOptionCategory: $selectedFlagOptionCategory, - selectedSendOptionCategory: $selectedSendOptionCategory, - selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, + selectedFlagOption: $selectedFlagOption, + selectedSendOption: $selectedSendOption, + selectedVisibilityOption: $selectedVisibilityOption, showSuccessView: $showSuccessView, flagTarget: .author(author), sendAction: {} ) } .onAppear { - selectedFlagOptionCategory = nil + selectedFlagOption = nil } .background(Color.appBg) } diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index cb2df2a35..3dd9bad26 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -17,7 +17,7 @@ struct ReportMenuModifier: ViewModifier { @State private var confirmationDialogState: ConfirmationDialogState? @State private var selectedFlagOption: FlagOption? @State private var selectedFlagSendOption: FlagOption? - @State private var selectedVisibilityOptionCategory: FlagOption? + @State private var selectedVisibilityOption: FlagOption? @State private var showFlagSuccessView = false @Environment(\.managedObjectContext) private var viewContext @@ -60,13 +60,13 @@ struct ReportMenuModifier: ViewModifier { .sheet(isPresented: $isPresented) { NavigationStack { UserFlagView( - selectedFlagOptionCategory: $selectedFlagOption, - selectedSendOptionCategory: $selectedFlagSendOption, - selectedVisibilityOptionCategory: $selectedVisibilityOptionCategory, + selectedFlagOption: $selectedFlagOption, + selectedSendOption: $selectedFlagSendOption, + selectedVisibilityOption: $selectedVisibilityOption, showSuccessView: $showFlagSuccessView, flagTarget: reportedObject, sendAction: { - if let selectCategory = selectedVisibilityOptionCategory?.category { + if let selectCategory = selectedVisibilityOption?.category { publishReportForNewModerationFlow(selectCategory) determineFlaggedAccoutVisibility { showFlagSuccessView = true @@ -217,7 +217,7 @@ struct ReportMenuModifier: ViewModifier { /// - Parameter completion: A closure that is executed once the necessary actions are complete. private func determineFlaggedAccoutVisibility(completion: () -> Void) { if let author = reportedObject.author { - guard case .visibility(let visibilityCategory) = selectedVisibilityOptionCategory?.category else { return } + guard case .visibility(let visibilityCategory) = selectedVisibilityOption?.category else { return } if visibilityCategory == .mute { guard !author.muted else { return } From 8758561db4a6acb78030ceb529cdbd17c4bc5aad Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Mon, 30 Sep 2024 20:51:14 +0100 Subject: [PATCH 12/20] refactor code --- Nos/Views/Moderation/ContentFlagView.swift | 9 ++++++--- Nos/Views/Moderation/FlagSuccessView.swift | 4 ++-- Nos/Views/Moderation/UserFlagView.swift | 9 ++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Nos/Views/Moderation/ContentFlagView.swift b/Nos/Views/Moderation/ContentFlagView.swift index 3401400d0..58f418c44 100644 --- a/Nos/Views/Moderation/ContentFlagView.swift +++ b/Nos/Views/Moderation/ContentFlagView.swift @@ -9,6 +9,9 @@ struct ContentFlagView: View { /// The target of the report. let flagTarget: ReportTarget + + /// Defines the action to be performed when the user sends a flag report. + /// It is called when the user taps the "Send" button after selecting all required options. var sendAction: () -> Void @Environment(\.dismiss) private var dismiss @@ -30,13 +33,13 @@ struct ContentFlagView: View { .nosNavigationBar(title: .localizable.flagContent) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { + Button { dismiss() resetSelections() - }, label: { + } label: { Text(.localizable.cancel) .foregroundColor(.primaryTxt) - }) + } .opacity(showSuccessView ? 0 : 1) .disabled(showSuccessView) } diff --git a/Nos/Views/Moderation/FlagSuccessView.swift b/Nos/Views/Moderation/FlagSuccessView.swift index 7c9169f97..882414480 100644 --- a/Nos/Views/Moderation/FlagSuccessView.swift +++ b/Nos/Views/Moderation/FlagSuccessView.swift @@ -12,10 +12,10 @@ struct FlagSuccessView: View { Text(String(localized: .localizable.thanksForTag)) .foregroundColor(.primaryTxt) .font(.clarity(.regular, textStyle: .title2)) - .padding(.horizontal, 62) + .padding(.horizontal, 25) Text(String(localized: .localizable.keepOnHelpingUs)) - .padding(.horizontal, 68) + .padding(.horizontal, 25) .foregroundColor(.secondaryTxt) .multilineTextAlignment(.center) .lineSpacing(6) diff --git a/Nos/Views/Moderation/UserFlagView.swift b/Nos/Views/Moderation/UserFlagView.swift index b55fba886..c413c947f 100644 --- a/Nos/Views/Moderation/UserFlagView.swift +++ b/Nos/Views/Moderation/UserFlagView.swift @@ -10,6 +10,9 @@ struct UserFlagView: View { /// The target of the report. let flagTarget: ReportTarget + + /// Defines the action to be performed when the user sends a flag report. + /// It is called when the user taps the "Send" button after selecting all required options. var sendAction: () -> Void @Environment(\.dismiss) private var dismiss @@ -30,13 +33,13 @@ struct UserFlagView: View { .nosNavigationBar(title: .localizable.flagUserTitle) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { + Button { dismiss() resetSelections() - }, label: { + } label: { Text(.localizable.cancel) .foregroundColor(.primaryTxt) - }) + } .opacity(showSuccessView ? 0 : 1) .disabled(showSuccessView) } From ec1ae9180f21c51746b9204d1d0712afd7c7ffec Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 1 Oct 2024 10:10:41 +0100 Subject: [PATCH 13/20] make mute/unmute async --- Nos/Views/Modifiers/ReportMenuModifier.swift | 41 ++++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 3dd9bad26..9ba910822 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -68,7 +68,8 @@ struct ReportMenuModifier: ViewModifier { sendAction: { if let selectCategory = selectedVisibilityOption?.category { publishReportForNewModerationFlow(selectCategory) - determineFlaggedAccoutVisibility { + Task { + await determineFlaggedAccoutVisibility() showFlagSuccessView = true } } @@ -112,7 +113,9 @@ struct ReportMenuModifier: ViewModifier { actions: { if let author = reportedObject.author { Button(String(localized: .localizable.yes)) { - mute(author: author) + Task { + try? await mute(author: author) + } } Button(String(localized: .localizable.no)) {} } @@ -193,40 +196,36 @@ struct ReportMenuModifier: ViewModifier { } } - func mute(author: Author) { - Task { - do { - try await author.mute(viewContext: viewContext) - } catch { - Log.error(error.localizedDescription) - } + func mute(author: Author) async throws { + do { + try await author.mute(viewContext: viewContext) + } catch { + Log.error(error.localizedDescription) + throw error } } - func unmute(author: Author) { - Task { - do { - try await author.unmute(viewContext: viewContext) - } catch { - Log.error(error.localizedDescription) - } + func unmute(author: Author) async throws { + do { + try await author.unmute(viewContext: viewContext) + } catch { + Log.error(error.localizedDescription) + throw error } } /// Determines the visibility status for a flagged account and applies the appropriate action. - /// - Parameter completion: A closure that is executed once the necessary actions are complete. - private func determineFlaggedAccoutVisibility(completion: () -> Void) { + private func determineFlaggedAccoutVisibility() async { if let author = reportedObject.author { guard case .visibility(let visibilityCategory) = selectedVisibilityOption?.category else { return } if visibilityCategory == .mute { guard !author.muted else { return } - mute(author: author) + try? await mute(author: author) } else { guard author.muted else { return } - unmute(author: author) + try? await unmute(author: author) } - completion() } } From 10bd3a36e5884bfefde2c5334cd7f25555a8d039 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Tue, 1 Oct 2024 16:15:30 +0100 Subject: [PATCH 14/20] remove "throw" from async function --- Nos/Views/Modifiers/ReportMenuModifier.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 9ba910822..5ca700f97 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -114,7 +114,7 @@ struct ReportMenuModifier: ViewModifier { if let author = reportedObject.author { Button(String(localized: .localizable.yes)) { Task { - try? await mute(author: author) + await mute(author: author) } } Button(String(localized: .localizable.no)) {} @@ -196,21 +196,19 @@ struct ReportMenuModifier: ViewModifier { } } - func mute(author: Author) async throws { + func mute(author: Author) async { do { try await author.mute(viewContext: viewContext) } catch { Log.error(error.localizedDescription) - throw error } } - func unmute(author: Author) async throws { + func unmute(author: Author) async { do { try await author.unmute(viewContext: viewContext) } catch { Log.error(error.localizedDescription) - throw error } } @@ -221,10 +219,10 @@ struct ReportMenuModifier: ViewModifier { if visibilityCategory == .mute { guard !author.muted else { return } - try? await mute(author: author) + await mute(author: author) } else { guard author.muted else { return } - try? await unmute(author: author) + await unmute(author: author) } } } From e9ff2d3b60ba51911a95ee69c0c251cbd1799626 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Wed, 2 Oct 2024 18:01:57 +0100 Subject: [PATCH 15/20] remove extra opacity on ActionButton --- Nos/Views/Moderation/ContentFlagView.swift | 1 - Nos/Views/Moderation/UserFlagView.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Nos/Views/Moderation/ContentFlagView.swift b/Nos/Views/Moderation/ContentFlagView.swift index 58f418c44..cf5829984 100644 --- a/Nos/Views/Moderation/ContentFlagView.swift +++ b/Nos/Views/Moderation/ContentFlagView.swift @@ -56,7 +56,6 @@ struct ContentFlagView: View { } } ) - .opacity(selectedSendOptionCategory == nil ? 0.5 : 1) .disabled(selectedSendOptionCategory == nil) } } diff --git a/Nos/Views/Moderation/UserFlagView.swift b/Nos/Views/Moderation/UserFlagView.swift index c413c947f..64066d312 100644 --- a/Nos/Views/Moderation/UserFlagView.swift +++ b/Nos/Views/Moderation/UserFlagView.swift @@ -56,7 +56,6 @@ struct UserFlagView: View { } } ) - .opacity(selectedVisibilityOption == nil ? 0.5 : 1) .disabled(selectedVisibilityOption == nil) } } From cc4c9179e53e484f9d225a0c075f84b55aa2b852 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 3 Oct 2024 17:58:25 +0100 Subject: [PATCH 16/20] remove unmute function --- Nos/Views/Modifiers/ReportMenuModifier.swift | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 5ca700f97..90a2cd055 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -204,14 +204,6 @@ struct ReportMenuModifier: ViewModifier { } } - func unmute(author: Author) async { - do { - try await author.unmute(viewContext: viewContext) - } catch { - Log.error(error.localizedDescription) - } - } - /// Determines the visibility status for a flagged account and applies the appropriate action. private func determineFlaggedAccoutVisibility() async { if let author = reportedObject.author { @@ -220,9 +212,6 @@ struct ReportMenuModifier: ViewModifier { if visibilityCategory == .mute { guard !author.muted else { return } await mute(author: author) - } else { - guard author.muted else { return } - await unmute(author: author) } } } From 8c242e55670e42d4c3e25ae80cbeecf36ecab832 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 3 Oct 2024 17:58:56 +0100 Subject: [PATCH 17/20] adjust logic to disable send button and show visibility section --- Nos/Views/Moderation/UserFlagView.swift | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Nos/Views/Moderation/UserFlagView.swift b/Nos/Views/Moderation/UserFlagView.swift index 64066d312..6eb42c269 100644 --- a/Nos/Views/Moderation/UserFlagView.swift +++ b/Nos/Views/Moderation/UserFlagView.swift @@ -8,6 +8,10 @@ struct UserFlagView: View { @Binding var selectedVisibilityOption: FlagOption? @Binding var showSuccessView: Bool + @Environment(\.dismiss) private var dismiss + + @State private var flagCategories: [FlagOption] = [] + /// The target of the report. let flagTarget: ReportTarget @@ -15,9 +19,22 @@ struct UserFlagView: View { /// It is called when the user taps the "Send" button after selecting all required options. var sendAction: () -> Void - @Environment(\.dismiss) private var dismiss + /// Indicates whether the target of the report is muted. + var isUserMuted: Bool { + flagTarget.author?.muted ?? false + } - @State private var flagCategories: [FlagOption] = [] + /// Determines if the send button should be disabled. + /// + /// The button's disabled state depends on two factors: + /// - For muted users: disabled when no send option is selected. + /// - For non-muted users: disabled when no visibility option is selected. + var isSendButtonDisabled: Bool { + if isUserMuted { + return selectedSendOption == nil + } + return selectedVisibilityOption == nil + } var body: some View { ZStack { @@ -56,7 +73,7 @@ struct UserFlagView: View { } } ) - .disabled(selectedVisibilityOption == nil) + .disabled(isSendButtonDisabled) } } } @@ -93,7 +110,7 @@ struct UserFlagView: View { .transition(.move(edge: .leading).combined(with: .opacity)) } - if selectedSendOption != nil { + if selectedSendOption != nil && !isUserMuted { FlagOptionPicker( previousSelection: .constant(nil), currentSelection: $selectedVisibilityOption, From bb5d0a076b14e2da05ddf64b443c7280cd8ba824 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 3 Oct 2024 20:53:29 +0100 Subject: [PATCH 18/20] fix user flagging not sending due to no visibility enum selected when user is muted --- Nos/Views/Modifiers/ReportMenuModifier.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 90a2cd055..6794fefe0 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -66,12 +66,11 @@ struct ReportMenuModifier: ViewModifier { showSuccessView: $showFlagSuccessView, flagTarget: reportedObject, sendAction: { - if let selectCategory = selectedVisibilityOption?.category { - publishReportForNewModerationFlow(selectCategory) - Task { - await determineFlaggedAccoutVisibility() - showFlagSuccessView = true - } + let selectCategory = selectedVisibilityOption?.category ?? .visibility(.unmute) + publishReportForNewModerationFlow(selectCategory) + Task { + await determineFlaggedAccoutVisibility() + showFlagSuccessView = true } } ) From 412f5a1df17b3a137b2cabed786dbeebd179cb97 Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Thu, 3 Oct 2024 20:54:05 +0100 Subject: [PATCH 19/20] rename unmute to dontMute in FlagOption --- Nos/Models/FlagOption.swift | 4 ++-- Nos/Views/Modifiers/ReportMenuModifier.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nos/Models/FlagOption.swift b/Nos/Models/FlagOption.swift index f1942c2d1..29656c003 100644 --- a/Nos/Models/FlagOption.swift +++ b/Nos/Models/FlagOption.swift @@ -84,7 +84,7 @@ struct FlagOption: Identifiable, Equatable { title: String(localized: .localizable.flagUserDontMuteTitle), description: String(localized: .localizable.flagUserDontMuteDescription), info: nil, - category: .visibility(.unmute) + category: .visibility(.dontMute) ) ] @@ -153,5 +153,5 @@ enum SendFlagPrivacy { /// Specifies whether a flagged user should be muted or not. enum FlagUserVisibility { case mute - case unmute + case dontMute } diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index 6794fefe0..ef56bec06 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -66,7 +66,7 @@ struct ReportMenuModifier: ViewModifier { showSuccessView: $showFlagSuccessView, flagTarget: reportedObject, sendAction: { - let selectCategory = selectedVisibilityOption?.category ?? .visibility(.unmute) + let selectCategory = selectedVisibilityOption?.category ?? .visibility(.dontMute) publishReportForNewModerationFlow(selectCategory) Task { await determineFlaggedAccoutVisibility() From 63f830fa4dcc587e87b4d7968f15c4629795fddf Mon Sep 17 00:00:00 2001 From: Itunu Raimi Date: Fri, 4 Oct 2024 13:40:18 +0100 Subject: [PATCH 20/20] rename function --- Nos/Views/Modifiers/ReportMenuModifier.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nos/Views/Modifiers/ReportMenuModifier.swift b/Nos/Views/Modifiers/ReportMenuModifier.swift index ef56bec06..6839a30b9 100644 --- a/Nos/Views/Modifiers/ReportMenuModifier.swift +++ b/Nos/Views/Modifiers/ReportMenuModifier.swift @@ -69,7 +69,7 @@ struct ReportMenuModifier: ViewModifier { let selectCategory = selectedVisibilityOption?.category ?? .visibility(.dontMute) publishReportForNewModerationFlow(selectCategory) Task { - await determineFlaggedAccoutVisibility() + await muteUserIfNeeded() showFlagSuccessView = true } } @@ -203,8 +203,8 @@ struct ReportMenuModifier: ViewModifier { } } - /// Determines the visibility status for a flagged account and applies the appropriate action. - private func determineFlaggedAccoutVisibility() async { + /// Determines if the user being flagged should be muted. + private func muteUserIfNeeded() async { if let author = reportedObject.author { guard case .visibility(let visibilityCategory) = selectedVisibilityOption?.category else { return }