Skip to content

Commit

Permalink
Merge pull request #1648 from planetary-social/remove-new-moderation-…
Browse files Browse the repository at this point in the history
…flow-feature-flag

Remove new moderation flow feature flag
  • Loading branch information
pelumy authored Oct 18, 2024
2 parents 968241b + 400243e commit ad46c33
Show file tree
Hide file tree
Showing 6 changed files with 13 additions and 291 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Migrate ObservableObject to @Observable where possible [#1458](https://github.com/planetary-social/nos/issues/1458)
- Added the Create Account onboarding screen. Currently behind the “New Onboarding Flow” feature flag. [#1594](https://github.com/planetary-social/nos/issues/1594)
- Increase build settings timeout in fastlane. [#1662](https://github.com/planetary-social/nos/pull/1662)
- Removed new moderation feature flag. [#1646](https://github.com/planetary-social/nos/issues/1646)

## [0.2.2] - 2024-10-11Z

Expand Down
29 changes: 0 additions & 29 deletions Nos/Assets/Localization/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -5509,35 +5509,6 @@
}
}
},
"enableNewModerationFlow" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enable new moderation flow"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Activar nuevo flujo de moderación"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "새로운 모더레이션 흐름 활성화"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "启用新审核流程"
}
}
}
},
"enterCode" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
5 changes: 0 additions & 5 deletions Nos/Service/FeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import SwiftUI

/// Feature flags for enabling experimental or beta features.
enum FeatureFlag {
/// Whether the new moderation flow should be enabled or not.
/// - Note: See [#1489](https://github.com/planetary-social/nos/issues/1489) for details on the new moderation flow.
case newModerationFlow

/// Whether the new onboarding flow should be enabled or not.
/// - Note: See [Figma](https://www.figma.com/design/6MeujQUXzC1AuviHEHCs0J/Nos---In-Progress?node-id=9221-8504)
/// for the new flow.
Expand Down Expand Up @@ -35,7 +31,6 @@ protocol FeatureFlags {

/// Feature flags and their values.
private var featureFlags: [FeatureFlag: Bool] = [
.newModerationFlow: false,
.newOnboardingFlow: false
]

Expand Down
253 changes: 12 additions & 241 deletions Nos/Views/Modifiers/ReportMenuModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ import SwiftUINavigation
/// category and optionally mute the respective author.
struct ReportMenuModifier: ViewModifier {
@Binding var isPresented: Bool

var reportedObject: ReportTarget

@State private var userSelection: UserSelection?
@State private var confirmReport = false
@State private var showMuteDialog = false
@State private var confirmationDialogState: ConfirmationDialogState<UserSelection>?

@State private var selectedFlagOption: FlagOption?
@State private var selectedFlagSendOption: FlagOption?
@State private var selectedVisibilityOption: FlagOption?
Expand All @@ -24,18 +20,6 @@ struct ReportMenuModifier: ViewModifier {
@Dependency(\.featureFlags) private var featureFlags

func body(content: Content) -> some View {
Group {
if featureFlags.isEnabled(.newModerationFlow) {
newModerationFlow(content: content)
} else {
oldModerationFlow(content: content)
}
}
}

/// Displays the moderation flow based on the reported object type. The old flow is still displayed for the author.
@ViewBuilder
private func newModerationFlow(content: Content) -> some View {
switch reportedObject {
case .note:
content
Expand All @@ -44,11 +28,11 @@ struct ReportMenuModifier: ViewModifier {
ContentFlagView(
selectedFlagOptionCategory: $selectedFlagOption,
selectedSendOptionCategory: $selectedFlagSendOption,
showSuccessView: $showFlagSuccessView,
showSuccessView: $showFlagSuccessView,
flagTarget: reportedObject,
sendAction: {
if let selectCategory = selectedFlagOption?.category {
publishReportForNewModerationFlow(selectCategory)
publishReport(selectCategory)
showFlagSuccessView = true
}
}
Expand All @@ -63,11 +47,11 @@ struct ReportMenuModifier: ViewModifier {
selectedFlagOption: $selectedFlagOption,
selectedSendOption: $selectedFlagSendOption,
selectedVisibilityOption: $selectedVisibilityOption,
showSuccessView: $showFlagSuccessView,
showSuccessView: $showFlagSuccessView,
flagTarget: reportedObject,
sendAction: {
let selectCategory = selectedVisibilityOption?.category ?? .visibility(.dontMute)
publishReportForNewModerationFlow(selectCategory)
publishReport(selectCategory)
Task {
await muteUserIfNeeded()
showFlagSuccessView = true
Expand All @@ -79,130 +63,6 @@ struct ReportMenuModifier: ViewModifier {
}
}

// swiftlint:disable function_body_length
@ViewBuilder
func oldModerationFlow(content: Content) -> some View {
content
// ReportCategory menu
.confirmationDialog(unwrapping: $confirmationDialogState, action: processUserSelection)
.alert(
String(localized: "confirmFlag"),
isPresented: $confirmReport,
actions: {
Button("confirm") {
publishReport(userSelection)

if let author = reportedObject.author, !author.muted {
showMuteDialog = true
}
}
Button("cancel", role: .cancel) {
userSelection = nil
}
},
message: {
let text = getAlertMessage(for: userSelection, with: reportedObject)
Text(text)
}
)
// Mute user menu
.alert(
"muteUser",
isPresented: $showMuteDialog,
actions: {
if let author = reportedObject.author {
Button("yes") {
Task {
await mute(author: author)
}
}
Button("no") {}
}
},
message: {
if let author = reportedObject.author {
Text(String.localizedStringWithFormat(String(localized: "mutePrompt"), author.safeName))
} else {
Text("error")
}
}
)
.onChange(of: isPresented) { _, shouldPresent in
if shouldPresent {
let message: String
if case .noteCategorySelected = userSelection {
message = String(localized: "reportContentMessage")
} else {
message = String(localized: "flagUserMessage")
}
confirmationDialogState = ConfirmationDialogState(
title: TextState(String(localized: "reportContent")),
message: TextState(message),
buttons: topLevelButtons()
)
}
}
.onChange(of: confirmationDialogState) { _, newValue in
if newValue == nil {
isPresented = false
}
}
}
// swiftlint:enable function_body_length

func processUserSelection(_ userSelection: UserSelection?) {
self.userSelection = userSelection

guard let userSelection else {
return
}

switch userSelection {
case .noteCategorySelected(let category):
Task {
confirmationDialogState = ConfirmationDialogState(
title: TextState(
String.localizedStringWithFormat(String(localized: "reportActionTitle"), category.displayName)
),
message: TextState(
String.localizedStringWithFormat(String(localized: "reportActionTitle"), category.displayName)
),
buttons: [
ButtonState(action: .send(.sendToNos(category))) {
TextState("Send to Nos")
},
ButtonState(action: .send(.flagPublicly(category))) {
TextState("Flag Publicly")
}
]
)
}

case .authorCategorySelected(let category):
Task {
confirmationDialogState = ConfirmationDialogState(
title: TextState(
String.localizedStringWithFormat(String(localized: "reportActionTitle"), category.displayName)
),
message: TextState(
String.localizedStringWithFormat(String(localized: "reportActionTitle"), category.displayName)
),
buttons: [
ButtonState(action: .send(.sendToNos(category))) {
TextState("Send to Nos")
},
ButtonState(action: .send(.flagPublicly(category))) {
TextState("Flag Publicly")
}
]
)
}

case .sendToNos, .flagPublicly:
confirmReport = true
}
}

func mute(author: Author) async {
do {
try await author.mute(viewContext: viewContext)
Expand All @@ -223,72 +83,16 @@ struct ReportMenuModifier: ViewModifier {
}
}

/// An enum to simplify the user selection through the sequence of connected
/// dialogs
enum UserSelection: Equatable {
case noteCategorySelected(ReportCategory)
case authorCategorySelected(ReportCategory)
case sendToNos(ReportCategory)
case flagPublicly(ReportCategory)

func confirmationAlertMessage(for reportedObject: ReportTarget) -> String {
switch self {
case .sendToNos(let category):
switch reportedObject {
case .note:
return String.localizedStringWithFormat(
String(localized: "reportNoteSendToNosConfirmation"), category.displayName
)
case .author:
return String(localized: "reportAuthorSendToNosConfirmation")
}

case .flagPublicly(let category):
return String.localizedStringWithFormat(
String(localized: "reportFlagPubliclyConfirmation"), category.displayName
)

case .noteCategorySelected(let category),
.authorCategorySelected(let category):
return String.localizedStringWithFormat(
String(localized: "reportFlagPubliclyConfirmation"), category.displayName
)
}
}
}

/// List of the top-level report categories we care about.
func topLevelButtons() -> [ButtonState<UserSelection>] {
switch reportedObject {
case .note:
ReportCategory.noteCategories.map { category in
let userSelection = UserSelection.noteCategorySelected(category)

return ButtonState(action: .send(userSelection)) {
TextState(verbatim: category.displayName)
}
}
case .author:
ReportCategory.authorCategories.map { category in
let userSelection = UserSelection.authorCategorySelected(category)

return ButtonState(action: .send(userSelection)) {
TextState(verbatim: category.displayName)
}
}
}
}

/// Publishes a report based on the categories the user selected for the new moderation flow.
private func publishReportForNewModerationFlow(_ selectedCategory: FlagCategory) {
/// Publishes a report based on the category the user selected.
private func publishReport(_ selectedCategory: FlagCategory) {
if case .privacy(let privacyCategory) = selectedFlagSendOption?.category, privacyCategory == .sendToNos {
sendToNosForNewModerationFlow(selectedCategory)
sendToNos(selectedCategory)
} else {
flagPubliclyForNewModerationFlow(selectedCategory)
flagPublicly(selectedCategory)
}
}

private func sendToNosForNewModerationFlow(_ selectedCategory: FlagCategory) {
private func sendToNos(_ selectedCategory: FlagCategory) {
if case .report(let reportCategory) = selectedCategory {
// Call the publisher with the extracted ReportCategory
ReportPublisher().publishPrivateReport(
Expand All @@ -299,7 +103,7 @@ struct ReportMenuModifier: ViewModifier {
}
}

private func flagPubliclyForNewModerationFlow(_ selectedCategory: FlagCategory) {
private func flagPublicly(_ selectedCategory: FlagCategory) {
if case .report(let reportCategory) = selectedCategory {
ReportPublisher().publishPublicReport(
for: reportedObject,
Expand All @@ -308,39 +112,6 @@ struct ReportMenuModifier: ViewModifier {
)
}
}

/// Publishes a report based on user input for the old moderation flow.
func publishReport(_ userSelection: UserSelection?) {
switch userSelection {
case .sendToNos(let selectedCategory):
sendToNos(selectedCategory)
case .flagPublicly(let selectedCategory):
flagPublicly(selectedCategory)
case .noteCategorySelected, .authorCategorySelected, .none:
// This would be a dev error
Log.error("Invalid user selection")
}
}

func sendToNos(_ selectedCategory: ReportCategory) {
ReportPublisher().publishPrivateReport(
for: reportedObject,
category: selectedCategory,
context: viewContext
)
}

func flagPublicly(_ selectedCategory: ReportCategory) {
ReportPublisher().publishPublicReport(
for: reportedObject,
category: selectedCategory,
context: viewContext
)
}

func getAlertMessage(for userSelection: UserSelection?, with reportedObject: ReportTarget) -> String {
userSelection?.confirmationAlertMessage(for: reportedObject) ?? String(localized: "error")
}
}

extension View {
Expand Down
Loading

0 comments on commit ad46c33

Please sign in to comment.