Add a ModerationAndSafetySettingsScreen. (#6278)

This commit is contained in:
Doug
2026-10-07 09:10:14 +01:00
committed by GitHub
parent 09085c8bb8
commit 51dd2efe23
13 changed files with 363 additions and 0 deletions
@@ -0,0 +1,113 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Compound
import SwiftUI
struct ModerationAndSafetySettingsScreen: View {
@Bindable var context: ModerationAndSafetySettingsScreenViewModel.Context
var body: some View {
Form {
sharePresenceSection
invitesSection
timelineMediaSection
blockedUsersSection
}
.compoundList()
.navigationTitle(L10n.commonModerationAndSafety)
.navigationBarTitleDisplayMode(.inline)
}
private var sharePresenceSection: some View {
Section {
ListRow(label: .plain(title: L10n.screenAdvancedSettingsSharePresence,
description: L10n.screenAdvancedSettingsSharePresenceDescription),
kind: .toggle($context.sharePresence))
} header: {
Text(L10n.screenModerationAndSafetySharePresenceHeading)
.compoundListSectionHeader()
}
}
@ViewBuilder
private var invitesSection: some View {
let binding = Binding(get: {
context.viewState.hideInviteAvatars
}, set: { newValue in
context.send(viewAction: .updateHideInviteAvatars(newValue))
})
Section {
ListRow(label: .plain(title: L10n.screenAdvancedSettingsHideInviteAvatarsToggleTitle),
details: context.viewState.isWaitingHideInviteAvatars ? .isWaiting(true) : nil,
kind: .toggle(binding))
.disabled(context.viewState.isWaitingHideInviteAvatars)
} header: {
Text(L10n.screenModerationAndSafetyInvitesHeading)
.compoundListSectionHeader()
}
}
@ViewBuilder
private var timelineMediaSection: some View {
let binding = Binding(get: {
context.viewState.timelineMediaVisibility
}, set: { newValue in
context.send(viewAction: .updateTimelineMediaVisibility(newValue))
})
Section {
ListRow(label: .plain(title: L10n.screenAdvancedSettingsShowMediaTimelineTitle),
details: .isWaiting(context.viewState.isWaitingTimelineMediaVisibility),
kind: .inlinePicker(selection: binding,
items: TimelineMediaVisibility.items))
.disabled(context.viewState.isWaitingTimelineMediaVisibility)
} header: {
Text(L10n.screenAdvancedSettingsShowMediaTimelineTitle)
.compoundListSectionHeader()
} footer: {
Text(L10n.screenAdvancedSettingsShowMediaTimelineSubtitle)
.compoundListSectionFooter()
}
}
@ViewBuilder
private var blockedUsersSection: some View {
if context.viewState.showBlockedUsers {
Section {
ListRow(label: .default(title: L10n.commonBlockedUsers,
icon: \.block),
kind: .navigationLink {
context.send(viewAction: .blockedUsers)
})
}
}
}
}
// MARK: - Previews
struct ModerationAndSafetySettingsScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = ModerationAndSafetySettingsScreenViewModel(userSession: UserSessionMock(.init()),
userIndicatorController: UserIndicatorControllerMock())
static var previews: some View {
ElementNavigationStack {
ModerationAndSafetySettingsScreen(context: viewModel.context)
}
.snapshotPreferences(expect: viewModel.context.observe(\.viewState.showBlockedUsers))
}
}
private extension TimelineMediaVisibility {
static var items: [(title: String, tag: TimelineMediaVisibility)] {
[(title: L10n.screenAdvancedSettingsShowMediaTimelineAlwaysHide, tag: .never),
(title: L10n.screenAdvancedSettingsShowMediaTimelinePrivateRooms, tag: .privateOnly),
(title: L10n.screenAdvancedSettingsShowMediaTimelineAlwaysShow, tag: .always)]
}
}