Use Compound ListRow instead of Compound form styles. (#1484)

* Adopt ListRow Component.

* Update snapshots.

To check in future PR:
- Pseudo Bug Report Attach Screenshot label
- Bug Report screenshot padding (Use ListRow .custom?)
- De-bold Analytics & Notification Settings footer links
- Inline picker alignment perhaps?

* Changelog

* Update Compound.

* Use the label on the login screen.
This commit is contained in:
Doug 2023-08-16 16:08:30 +01:00 committed by GitHub
parent 7c69012667
commit c64fb44c96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 499 additions and 557 deletions

View File

@ -12,7 +12,7 @@ let package = Package(
.library(name: "DesignKit", targets: ["DesignKit"]) .library(name: "DesignKit", targets: ["DesignKit"])
], ],
dependencies: [ dependencies: [
.package(url: "https://github.com/vector-im/compound-ios", revision: "89e3ed5adef33be7eb65137b861833ffae64f961"), .package(url: "https://github.com/vector-im/compound-ios", revision: "50bb7cf313bd1ad17201fc7e4c1184737a0f44c2"),
.package(url: "https://github.com/vector-im/element-design-tokens", exact: "0.0.3"), .package(url: "https://github.com/vector-im/element-design-tokens", exact: "0.0.3"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect", from: "0.9.0") .package(url: "https://github.com/siteline/SwiftUI-Introspect", from: "0.9.0")
], ],

View File

@ -5475,7 +5475,7 @@
repositoryURL = "https://github.com/vector-im/compound-ios"; repositoryURL = "https://github.com/vector-im/compound-ios";
requirement = { requirement = {
kind = revision; kind = revision;
revision = 89e3ed5adef33be7eb65137b861833ffae64f961; revision = 50bb7cf313bd1ad17201fc7e4c1184737a0f44c2;
}; };
}; };
9A472EE0218FE7DCF5283429 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { 9A472EE0218FE7DCF5283429 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = {

View File

@ -5,7 +5,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/compound-design-tokens.git", "location" : "https://github.com/vector-im/compound-design-tokens.git",
"state" : { "state" : {
"revision" : "aa55111d94486acbfd3344cf4d08b64723bd6703" "revision" : "387d2b7211f07761c67e849c59414a1bb803defa"
} }
}, },
{ {
@ -13,7 +13,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/compound-ios", "location" : "https://github.com/vector-im/compound-ios",
"state" : { "state" : {
"revision" : "89e3ed5adef33be7eb65137b861833ffae64f961" "revision" : "50bb7cf313bd1ad17201fc7e4c1184737a0f44c2"
} }
}, },
{ {
@ -151,6 +151,15 @@
"version" : "8.6.0" "version" : "8.6.0"
} }
}, },
{
"identity" : "sfsafesymbols",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SFSafeSymbols/SFSafeSymbols.git",
"state" : {
"revision" : "7cca2d60925876b5953a2cf7341cd80fbeac983c",
"version" : "4.1.1"
}
},
{ {
"identity" : "swift-algorithms", "identity" : "swift-algorithms",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View File

@ -72,29 +72,27 @@ struct LoginScreen: View {
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.bottom, 8) .padding(.bottom, 8)
TextField(L10n.commonUsername, TextField(text: $context.username) {
text: $context.username, Text(L10n.commonUsername).foregroundColor(.compound.textPlaceholder)
// Prompt colour fixes a flicker that occurs before the text field style introspects the field. }
prompt: Text(L10n.commonUsername).foregroundColor(.compound.textPlaceholder)) .focused($isUsernameFocused)
.focused($isUsernameFocused) .textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername))
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername)) .disableAutocorrection(true)
.disableAutocorrection(true) .textContentType(.username)
.textContentType(.username) .autocapitalization(.none)
.autocapitalization(.none) .submitLabel(.next)
.submitLabel(.next) .onChange(of: isUsernameFocused, perform: usernameFocusChanged)
.onChange(of: isUsernameFocused, perform: usernameFocusChanged) .onSubmit { isPasswordFocused = true }
.onSubmit { isPasswordFocused = true } .padding(.bottom, 20)
.padding(.bottom, 20)
SecureField(L10n.commonPassword, SecureField(text: $context.password) {
text: $context.password, Text(L10n.commonPassword).foregroundColor(.compound.textPlaceholder)
// Prompt colour fixes a flicker that occurs before the text field style introspects the field. }
prompt: Text(L10n.commonPassword).foregroundColor(.compound.textPlaceholder)) .focused($isPasswordFocused)
.focused($isPasswordFocused) .textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.password))
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.password)) .textContentType(.password)
.textContentType(.password) .submitLabel(.done)
.submitLabel(.done) .onSubmit(submit)
.onSubmit(submit)
Spacer().frame(height: 32) Spacer().frame(height: 32)

View File

@ -23,6 +23,8 @@ struct BugReportScreen: View {
@ObservedObject var context: BugReportScreenViewModel.Context @ObservedObject var context: BugReportScreenViewModel.Context
var photosPickerTitle: String { context.viewState.screenshot == nil ? L10n.screenBugReportAttachScreenshot : L10n.screenBugReportEditScreenshot }
var body: some View { var body: some View {
Form { Form {
textFieldSection textFieldSection
@ -31,7 +33,7 @@ struct BugReportScreen: View {
canContactSection canContactSection
} }
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.compoundForm() .compoundList()
.navigationTitle(L10n.commonReportABug) .navigationTitle(L10n.commonReportABug)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
@ -50,53 +52,48 @@ struct BugReportScreen: View {
private var textFieldSection: some View { private var textFieldSection: some View {
Section { Section {
TextField(L10n.screenBugReportEditorPlaceholder, ListRow(label: .plain(title: L10n.screenBugReportEditorPlaceholder),
text: $context.reportText, kind: .textField(text: $context.reportText))
prompt: Text(L10n.screenBugReportEditorPlaceholder).compoundFormTextFieldPlaceholder(),
axis: .vertical)
.lineLimit(4, reservesSpace: true) .lineLimit(4, reservesSpace: true)
.textFieldStyle(.compoundForm)
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.report) .accessibilityIdentifier(A11yIdentifiers.bugReportScreen.report)
} footer: { } footer: {
Text(L10n.screenBugReportEditorDescription) Text(L10n.screenBugReportEditorDescription)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
private var sendLogsSection: some View { private var sendLogsSection: some View {
Section { Section {
Toggle(L10n.screenBugReportIncludeLogs, isOn: $context.sendingLogsEnabled) ListRow(label: .plain(title: L10n.screenBugReportIncludeLogs),
.toggleStyle(.compoundForm) kind: .toggle($context.sendingLogsEnabled))
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.sendLogs) .accessibilityIdentifier(A11yIdentifiers.bugReportScreen.sendLogs)
} footer: { } footer: {
Text(L10n.screenBugReportLogsDescription) Text(L10n.screenBugReportLogsDescription)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
private var canContactSection: some View { private var canContactSection: some View {
Section { Section {
Toggle(L10n.screenBugReportContactMeTitle, isOn: $context.canContact) ListRow(label: .plain(title: L10n.screenBugReportContactMeTitle),
.toggleStyle(.compoundForm) kind: .toggle($context.canContact))
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.canContact) .accessibilityIdentifier(A11yIdentifiers.bugReportScreen.canContact)
} footer: { } footer: {
Text(L10n.screenBugReportContactMe) Text(L10n.screenBugReportContactMe)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
@ViewBuilder @ViewBuilder
private var attachScreenshotSection: some View { private var attachScreenshotSection: some View {
Section { Section {
PhotosPicker(selection: $selectedScreenshot, ListRow(kind: .custom {
matching: .screenshots, PhotosPicker(selection: $selectedScreenshot,
photoLibrary: .shared()) { matching: .screenshots,
Label(context.viewState.screenshot == nil ? L10n.screenBugReportAttachScreenshot : L10n.screenBugReportEditScreenshot, systemImage: "camera") photoLibrary: .shared()) {
} ListLabel.default(title: photosPickerTitle, systemIcon: .camera)
.buttonStyle(.compoundForm()) }
})
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.attachScreenshot) .accessibilityIdentifier(A11yIdentifiers.bugReportScreen.attachScreenshot)
} footer: { } footer: {
if let screenshot = context.viewState.screenshot { if let screenshot = context.viewState.screenshot {
@ -117,7 +114,6 @@ struct BugReportScreen: View {
.padding(.horizontal, 16) .padding(.horizontal, 16)
} }
} }
.compoundFormSection()
} }
@ToolbarContentBuilder @ToolbarContentBuilder

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct ReportContentScreen: View { struct ReportContentScreen: View {
@ -32,7 +33,7 @@ struct ReportContentScreen: View {
ignoreUserSection ignoreUserSection
} }
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.compoundForm() .compoundList()
.navigationTitle(L10n.actionReportContent) .navigationTitle(L10n.actionReportContent)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
@ -41,27 +42,23 @@ struct ReportContentScreen: View {
private var reasonSection: some View { private var reasonSection: some View {
Section { Section {
TextField(L10n.reportContentHint, ListRow(label: .plain(title: L10n.reportContentHint),
text: $context.reasonText, kind: .textField(text: $context.reasonText))
prompt: Text(L10n.reportContentHint).compoundFormTextFieldPlaceholder(),
axis: .vertical)
.lineLimit(4, reservesSpace: true) .lineLimit(4, reservesSpace: true)
.textFieldStyle(.compoundForm)
} footer: { } footer: {
Text(L10n.reportContentExplanation) Text(L10n.reportContentExplanation)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
private var ignoreUserSection: some View { private var ignoreUserSection: some View {
Section { Section {
Toggle(L10n.screenReportContentBlockUser, isOn: $context.ignoreUser) ListRow(label: .plain(title: L10n.screenReportContentBlockUser),
.toggleStyle(.compoundForm) kind: .toggle($context.ignoreUser))
.accessibilityIdentifier(A11yIdentifiers.reportContent.ignoreUser) .accessibilityIdentifier(A11yIdentifiers.reportContent.ignoreUser)
} footer: { } footer: {
Text(L10n.screenReportContentBlockUserHint) Text(L10n.screenReportContentBlockUserHint)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
} }

View File

@ -46,6 +46,16 @@ struct RoomDetailsEditScreenViewState: BindableState {
bindings.name != initialName bindings.name != initialName
} }
/// The string shown for the room's name when it can't be edited.
var nameRowTitle: String {
bindings.name.isEmpty ? L10n.commonRoomName : bindings.name
}
/// The string shown for the room's topic when it can't be edited.
var topicRowTitle: String {
bindings.topic.isEmpty ? L10n.commonTopic : bindings.topic
}
var topicDidChange: Bool { var topicDidChange: Bool {
bindings.topic != initialTopic bindings.topic != initialTopic
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct RoomDetailsEditScreen: View { struct RoomDetailsEditScreen: View {
@ -31,7 +32,7 @@ struct RoomDetailsEditScreen: View {
nameSection nameSection
topicSection topicSection
} }
.compoundForm() .compoundList()
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.navigationTitle(L10n.screenRoomDetailsEditRoomTitle) .navigationTitle(L10n.screenRoomDetailsEditRoomTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@ -85,42 +86,39 @@ struct RoomDetailsEditScreen: View {
private var nameSection: some View { private var nameSection: some View {
Section { Section {
let canEditName = context.viewState.canEditName if context.viewState.canEditName {
ListRow(label: .plain(title: L10n.commonRoomNamePlaceholder),
TextField(L10n.commonRoomName, kind: .textField(text: $context.name, axis: .horizontal))
text: $context.name, .focused($focus, equals: .name)
prompt: canEditName ? Text(L10n.commonRoomNamePlaceholder) : nil, } else {
axis: .horizontal) ListRow(kind: .custom {
.focused($focus, equals: .name) ListLabel.plain(title: context.viewState.nameRowTitle)
.textFieldStyle(.compoundForm) .listRowBackground(Color.clear)
.disabled(!canEditName) })
.listRowBackground(canEditName ? Color.element.formRowBackground : .clear) }
.clipShape(RoundedRectangle(cornerRadius: 8))
} header: { } header: {
Text(L10n.commonRoomName) Text(L10n.commonRoomName)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
private var topicSection: some View { private var topicSection: some View {
Section { Section {
let canEditTopic = context.viewState.canEditTopic if context.viewState.canEditTopic {
ListRow(label: .plain(title: L10n.commonTopicPlaceholder),
TextField(L10n.commonTopic, kind: .textField(text: $context.topic, axis: .vertical))
text: $context.topic, .focused($focus, equals: .topic)
prompt: canEditTopic ? Text(L10n.commonTopicPlaceholder).foregroundColor(.compound.textPlaceholder) : nil, .lineLimit(3...)
axis: .vertical) } else {
.focused($focus, equals: .topic) ListRow(kind: .custom {
.textFieldStyle(.compoundForm) ListLabel.plain(title: context.viewState.topicRowTitle)
.disabled(!canEditTopic) .listRowBackground(Color.clear)
.listRowBackground(canEditTopic ? Color.element.formRowBackground : .clear) })
.lineLimit(3...) }
} header: { } header: {
Text(L10n.commonTopic) Text(L10n.commonTopic)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
private var avatarOverlayIcon: some View { private var avatarOverlayIcon: some View {
@ -166,9 +164,23 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider {
roomProxy: RoomProxyMock(with: .init(name: "Room", displayName: "Room")), roomProxy: RoomProxyMock(with: .init(name: "Room", displayName: "Room")),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
static let readOnlyViewModel = {
let accountOwner = RoomMemberProxyMock.mockOwner(allowedStateEvents: [])
return RoomDetailsEditScreenViewModel(accountOwner: accountOwner,
mediaProvider: MockMediaProvider(),
roomProxy: RoomProxyMock(with: .init(name: "Room", displayName: "Room")),
userIndicatorController: UserIndicatorControllerMock.default)
}()
static var previews: some View { static var previews: some View {
NavigationStack { NavigationStack {
RoomDetailsEditScreen(context: viewModel.context) RoomDetailsEditScreen(context: viewModel.context)
} }
.previewDisplayName("Normal")
NavigationStack {
RoomDetailsEditScreen(context: readOnlyViewModel.context)
}
.previewDisplayName("Read only")
} }
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct RoomDetailsScreen: View { struct RoomDetailsScreen: View {
@ -47,7 +48,7 @@ struct RoomDetailsScreen: View {
leaveRoomSection leaveRoomSection
} }
.compoundForm() .compoundList()
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.alert(item: $context.leaveRoomAlertItem, .alert(item: $context.leaveRoomAlertItem,
actions: leaveRoomAlertActions, actions: leaveRoomAlertActions,
@ -125,74 +126,55 @@ struct RoomDetailsScreen: View {
if context.viewState.hasTopicSection { if context.viewState.hasTopicSection {
Section { Section {
if let topic = context.viewState.topic, !topic.isEmpty { if let topic = context.viewState.topic, !topic.isEmpty {
Text(topic) ListRow(label: .description(topic), kind: .label)
.compoundFormSecondaryTextRow()
.lineLimit(isTopicExpanded ? nil : 3) .lineLimit(isTopicExpanded ? nil : 3)
.onTapGesture { isTopicExpanded.toggle() } .onTapGesture { isTopicExpanded.toggle() }
} else { } else {
Button { ListRow(label: .plain(title: L10n.screenRoomDetailsAddTopicTitle),
context.send(viewAction: .processTapAddTopic) kind: .button { context.send(viewAction: .processTapAddTopic) })
} label: { .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.addTopic)
Text(L10n.screenRoomDetailsAddTopicTitle)
}
.buttonStyle(.compoundForm())
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.addTopic)
} }
} header: { } header: {
Text(L10n.commonTopic) Text(L10n.commonTopic)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
} }
private var aboutSection: some View { private var aboutSection: some View {
Section { Section {
Button { ListRow(label: .default(title: L10n.commonPeople, systemIcon: .person),
context.send(viewAction: .processTapPeople) details: .title(String(context.viewState.joinedMembersCount)),
} label: { kind: .navigationLink {
LabeledContent { context.send(viewAction: .processTapPeople)
Text(String(context.viewState.joinedMembersCount)) })
} label: { .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.people)
Label(L10n.commonPeople, systemImage: "person")
}
}
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.people)
if context.viewState.canInviteUsers { if context.viewState.canInviteUsers {
Button { ListRow(label: .default(title: L10n.screenRoomDetailsInvitePeopleTitle,
context.send(viewAction: .processTapInvite) systemIcon: .personBadgePlus),
} label: { kind: .navigationLink {
Label(L10n.screenRoomDetailsInvitePeopleTitle, systemImage: "person.badge.plus") context.send(viewAction: .processTapInvite)
} })
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.invite) .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.invite)
} }
} }
.buttonStyle(.compoundForm(accessory: .navigationLink))
.compoundFormSection()
} }
@ViewBuilder @ViewBuilder
private var notificationSection: some View { private var notificationSection: some View {
Section { Section {
Button { ListRow(label: .default(title: L10n.screenRoomDetailsNotificationTitle,
context.send(viewAction: .processTapNotifications) systemIcon: .bell),
} label: { details: context.viewState.notificationSettingsState.isLoading ? .isWaiting(true)
LabeledContent { : context.viewState.notificationSettingsState.isError ? .systemIcon(.exclamationmarkCircle)
if context.viewState.notificationSettingsState.isLoading { : .title(context.viewState.notificationSettingsState.label),
ProgressView() kind: .navigationLink {
} else if context.viewState.notificationSettingsState.isError { context.send(viewAction: .processTapNotifications)
Image(systemName: "exclamationmark.circle") })
} else { .disabled(context.viewState.notificationSettingsState.isLoading)
Text(context.viewState.notificationSettingsState.label) .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
}
} label: {
Label(L10n.screenRoomDetailsNotificationTitle, systemImage: "bell")
}
}
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
} }
.buttonStyle(.compoundForm(accessory: context.viewState.notificationSettingsState.isLoaded ? .navigationLink : nil))
.disabled(context.viewState.notificationSettingsState.isLoading) .disabled(context.viewState.notificationSettingsState.isLoading)
} }
@ -215,51 +197,38 @@ struct RoomDetailsScreen: View {
private var securitySection: some View { private var securitySection: some View {
if context.viewState.isEncrypted { if context.viewState.isEncrypted {
Section { Section {
Label { ListRow(label: .default(title: L10n.screenRoomDetailsEncryptionEnabledTitle,
Text(L10n.screenRoomDetailsEncryptionEnabledTitle) description: L10n.screenRoomDetailsEncryptionEnabledSubtitle,
} icon: { systemIcon: .lockShield,
Image(systemName: "lock.shield") iconAlignment: .top),
} kind: .label)
.labelStyle(.compoundFormRow(secondaryText: L10n.screenRoomDetailsEncryptionEnabledSubtitle,
alignment: .top))
} header: { } header: {
Text(L10n.commonSecurity) Text(L10n.commonSecurity)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
} }
private var leaveRoomSection: some View { private var leaveRoomSection: some View {
Section { Section {
Button(role: .destructive) { ListRow(label: .default(title: L10n.actionLeaveRoom,
context.send(viewAction: .processTapLeave) systemIcon: .doorRightHandOpen,
} label: { role: .destructive),
Label(L10n.actionLeaveRoom, systemImage: "door.right.hand.open") kind: .button { context.send(viewAction: .processTapLeave) })
}
.buttonStyle(.compoundForm())
} }
.compoundFormSection()
} }
private func ignoreUserSection(user: RoomMemberDetails) -> some View { private func ignoreUserSection(user: RoomMemberDetails) -> some View {
Section { Section {
Button(role: user.isIgnored ? nil : .destructive) { ListRow(label: .default(title: user.isIgnored ? L10n.screenDmDetailsUnblockUser : L10n.screenDmDetailsBlockUser,
context.send(viewAction: user.isIgnored ? .processTapUnignore : .processTapIgnore) systemIcon: .slashCircle,
} label: { role: user.isIgnored ? nil : .destructive),
LabeledContent { details: .isWaiting(context.viewState.isProcessingIgnoreRequest),
if context.viewState.isProcessingIgnoreRequest { kind: .button {
ProgressView() context.send(viewAction: user.isIgnored ? .processTapUnignore : .processTapIgnore)
} })
} label: { .disabled(context.viewState.isProcessingIgnoreRequest)
Label(user.isIgnored ? L10n.screenDmDetailsUnblockUser : L10n.screenDmDetailsBlockUser,
systemImage: "slash.circle")
}
}
.buttonStyle(.compoundForm())
.disabled(context.viewState.isProcessingIgnoreRequest)
} }
.compoundFormSection()
} }
@ViewBuilder @ViewBuilder

View File

@ -54,7 +54,7 @@ struct RoomNotificationSettingsScreenViewState: BindableState {
let strings = RoomNotificationSettingsScreenStrings() let strings = RoomNotificationSettingsScreenStrings()
var notificationSettingsState: RoomNotificationSettingsState = .loading var notificationSettingsState: RoomNotificationSettingsState = .loading
var availableCustomRoomNotificationModes: [RoomNotificationModeProxy] = [.allMessages, .mentionsAndKeywordsOnly, .mute] var availableCustomRoomNotificationModes: [RoomNotificationModeProxy] = [.allMessages, .mentionsAndKeywordsOnly, .mute]
var isRestoringDefautSetting = false var isRestoringDefaultSetting = false
var pendingCustomMode: RoomNotificationModeProxy? var pendingCustomMode: RoomNotificationModeProxy?
} }

View File

@ -82,7 +82,7 @@ class RoomNotificationSettingsScreenViewModel: RoomNotificationSettingsScreenVie
isOneToOne: roomProxy.activeMembersCount == 2) isOneToOne: roomProxy.activeMembersCount == 2)
guard !Task.isCancelled else { return } guard !Task.isCancelled else { return }
state.notificationSettingsState = .loaded(settings: settings) state.notificationSettingsState = .loaded(settings: settings)
if !state.isRestoringDefautSetting { if !state.isRestoringDefaultSetting {
state.bindings.allowCustomSetting = !settings.isDefault state.bindings.allowCustomSetting = !settings.isDefault
} }
if state.pendingCustomMode == nil { if state.pendingCustomMode == nil {
@ -106,14 +106,14 @@ class RoomNotificationSettingsScreenViewModel: RoomNotificationSettingsScreenVie
} }
private func restoreDefaultSetting() { private func restoreDefaultSetting() {
state.isRestoringDefautSetting = true state.isRestoringDefaultSetting = true
Task { Task {
do { do {
try await notificationSettingsProxy.restoreDefaultNotificationMode(roomId: roomProxy.id) try await notificationSettingsProxy.restoreDefaultNotificationMode(roomId: roomProxy.id)
} catch { } catch {
displayError(.restoreDefaultFailed) displayError(.restoreDefaultFailed)
} }
state.isRestoringDefautSetting = false state.isRestoringDefaultSetting = false
} }
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct RoomNotificationSettingsScreen: View { struct RoomNotificationSettingsScreen: View {
@ -29,7 +30,7 @@ struct RoomNotificationSettingsScreen: View {
customSettingsSection customSettingsSection
} }
} }
.compoundForm() .compoundList()
.navigationTitle(L10n.screenRoomDetailsNotificationTitle) .navigationTitle(L10n.screenRoomDetailsNotificationTitle)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .roomNotifications) .track(screen: .roomNotifications)
@ -40,35 +41,28 @@ struct RoomNotificationSettingsScreen: View {
@ViewBuilder @ViewBuilder
private var allowCustomSettingSection: some View { private var allowCustomSettingSection: some View {
Section { Section {
Toggle(isOn: $context.allowCustomSetting) { ListRow(label: .plain(title: L10n.screenRoomNotificationSettingsAllowCustom),
Text(L10n.screenRoomNotificationSettingsAllowCustom) kind: .toggle($context.allowCustomSetting))
} .accessibilityIdentifier(A11yIdentifiers.roomNotificationSettingsScreen.allowCustomSetting)
.toggleStyle(.compoundForm) .disabled(context.viewState.notificationSettingsState.isLoading)
.accessibilityIdentifier(A11yIdentifiers.roomNotificationSettingsScreen.allowCustomSetting) .onChange(of: context.allowCustomSetting) { _ in
.disabled(context.viewState.notificationSettingsState.isLoading) context.send(viewAction: .changedAllowCustomSettings)
.onChange(of: context.allowCustomSetting) { _ in }
context.send(viewAction: .changedAllowCustomSettings)
}
} footer: { } footer: {
Text(L10n.screenRoomNotificationSettingsAllowCustomFootnote) Text(L10n.screenRoomNotificationSettingsAllowCustomFootnote)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
@ViewBuilder @ViewBuilder
private var defaultSettingSection: some View { private var defaultSettingSection: some View {
Section { Section {
if context.viewState.isRestoringDefautSetting { ListRow(label: .plain(title: context.viewState.isRestoringDefaultSetting ? L10n.commonLoading : context.viewState.strings.string(for: context.viewState.notificationSettingsState)),
Text(L10n.commonLoading) kind: .label)
.foregroundColor(.compound.textPlaceholder) .disabled(context.viewState.isRestoringDefaultSetting)
} else {
Text(context.viewState.strings.string(for: context.viewState.notificationSettingsState))
.foregroundColor(.compound.textPrimary)
}
} header: { } header: {
Text(L10n.screenRoomNotificationSettingsDefaultSettingTitle) Text(L10n.screenRoomNotificationSettingsDefaultSettingTitle)
.compoundFormSectionHeader() .compoundListSectionHeader()
} footer: { } footer: {
Text(context.viewState.strings.customSettingFootnote) Text(context.viewState.strings.customSettingFootnote)
.environment(\.openURL, OpenURLAction { url in .environment(\.openURL, OpenURLAction { url in
@ -76,30 +70,25 @@ struct RoomNotificationSettingsScreen: View {
context.send(viewAction: .customSettingFootnoteLinkTapped) context.send(viewAction: .customSettingFootnoteLinkTapped)
return .handled return .handled
}) })
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
@ViewBuilder @ViewBuilder
private var customSettingsSection: some View { private var customSettingsSection: some View {
Section { Section {
Picker("", selection: $context.customMode) { ListRow(label: .plain(title: L10n.screenRoomNotificationSettingsCustomSettingsTitle),
ForEach(context.viewState.availableCustomRoomNotificationModes, id: \.self) { mode in kind: .inlinePicker(selection: $context.customMode,
Text(context.viewState.strings.string(for: mode)) items: context.viewState.availableCustomRoomNotificationModes.map {
.tag(mode) (title: context.viewState.strings.string(for: $0), tag: $0)
} }))
} .onChange(of: context.customMode) { mode in
.onChange(of: context.customMode) { mode in context.send(viewAction: .setCustomMode(mode))
context.send(viewAction: .setCustomMode(mode)) }
}
.labelsHidden()
.pickerStyle(.inline)
} header: { } header: {
Text(L10n.screenRoomNotificationSettingsCustomSettingsTitle) Text(L10n.screenRoomNotificationSettingsCustomSettingsTitle)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct AnalyticsSettingsScreen: View { struct AnalyticsSettingsScreen: View {
@ -23,25 +24,23 @@ struct AnalyticsSettingsScreen: View {
Form { Form {
analyticsSection analyticsSection
} }
.compoundForm() .compoundList()
.navigationTitle(L10n.commonAnalytics) .navigationTitle(L10n.commonAnalytics)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
} }
var analyticsSection: some View { var analyticsSection: some View {
Section { Section {
Toggle(isOn: $context.enableAnalytics) { ListRow(label: .default(title: L10n.screenAnalyticsSettingsShareData,
Label(L10n.screenAnalyticsSettingsShareData, systemImage: "chart.bar") systemIcon: .chartBar),
} kind: .toggle($context.enableAnalytics))
.toggleStyle(.compoundForm) .onChange(of: context.enableAnalytics) { _ in
.onChange(of: context.enableAnalytics) { _ in context.send(viewAction: .toggleAnalytics)
context.send(viewAction: .toggleAnalytics) }
}
} footer: { } footer: {
Text(context.viewState.strings.sectionFooter) Text(context.viewState.strings.sectionFooter)
.compoundFormSectionFooter() .compoundListSectionFooter()
} }
.compoundFormSection()
} }
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct LegalInformationScreen: View { struct LegalInformationScreen: View {
@ -23,20 +24,15 @@ struct LegalInformationScreen: View {
var body: some View { var body: some View {
Form { Form {
Section { Section {
Button(L10n.commonCopyright) { ListRow(label: .plain(title: L10n.commonCopyright),
openURL("https://element.io/copyright") kind: .navigationLink { openURL("https://element.io/copyright") })
} ListRow(label: .plain(title: L10n.commonAcceptableUsePolicy),
Button(L10n.commonAcceptableUsePolicy) { kind: .navigationLink { openURL("https://element.io/acceptable-use-policy-terms") })
openURL("https://element.io/acceptable-use-policy-terms") ListRow(label: .plain(title: L10n.commonPrivacyPolicy),
} kind: .navigationLink { openURL("https://element.io/privacy") })
Button(L10n.commonPrivacyPolicy) {
openURL("https://element.io/privacy")
}
} }
.buttonStyle(.compoundForm(accessory: .navigationLink))
.compoundFormSection()
} }
.compoundForm() .compoundList()
.navigationTitle(L10n.commonAbout) .navigationTitle(L10n.commonAbout)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
} }

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct NotificationSettingsScreen: View { struct NotificationSettingsScreen: View {
@ -35,7 +36,7 @@ struct NotificationSettingsScreen: View {
} }
} }
} }
.compoundForm() .compoundList()
.navigationTitle(L10n.screenNotificationSettingsTitle) .navigationTitle(L10n.screenNotificationSettingsTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
@ -58,120 +59,98 @@ struct NotificationSettingsScreen: View {
private var userPermissionSection: some View { private var userPermissionSection: some View {
Section { Section {
HStack(alignment: .firstTextBaseline, spacing: 13) { ListRow(kind: .custom {
Image(systemName: "exclamationmark.circle.fill") HStack(alignment: .firstTextBaseline, spacing: 13) {
.foregroundColor(.compound.iconTertiaryAlpha) Image(systemSymbol: .exclamationmarkCircleFill)
VStack(alignment: .leading, spacing: 2) { .foregroundColor(.compound.iconTertiaryAlpha)
Text(L10n.screenNotificationSettingsSystemNotificationsTurnedOff) VStack(alignment: .leading, spacing: 2) {
.font(.compound.bodyLG) Text(L10n.screenNotificationSettingsSystemNotificationsTurnedOff)
.foregroundColor(.compound.textPrimary) .font(.compound.bodyLG)
Text(context.viewState.strings.changeYourSystemSettings) .foregroundColor(.compound.textPrimary)
.font(.compound.bodySM) Text(context.viewState.strings.changeYourSystemSettings)
.foregroundColor(.compound.textSecondary) .font(.compound.bodySM)
.tint(.compound.textPrimary) .foregroundColor(.compound.textSecondary)
.environment(\.openURL, OpenURLAction { url in .tint(.compound.textPrimary)
context.send(viewAction: .linkClicked(url: url)) }
return .systemAction
})
} }
.padding(.vertical, 5) .padding(.horizontal, ListRowPadding.horizontal)
} .padding(.vertical, 8)
.environment(\.openURL, OpenURLAction { url in
context.send(viewAction: .linkClicked(url: url))
return .systemAction
})
})
} }
.compoundFormSection()
} }
private var enableNotificationSection: some View { private var enableNotificationSection: some View {
Section { Section {
Toggle(isOn: $context.enableNotifications) { ListRow(label: .plain(title: L10n.screenNotificationSettingsEnableNotifications),
Text(L10n.screenNotificationSettingsEnableNotifications) kind: .toggle($context.enableNotifications))
} .onChange(of: context.enableNotifications) { _ in
.toggleStyle(.compoundForm) context.send(viewAction: .changedEnableNotifications)
.onChange(of: context.enableNotifications) { _ in }
context.send(viewAction: .changedEnableNotifications)
}
} }
.compoundFormSection()
} }
private var roomsNotificationSection: some View { private var roomsNotificationSection: some View {
Section { Section {
// Group chats // Group chats
Button { ListRow(label: .plain(title: L10n.screenNotificationSettingsGroupChats),
context.send(viewAction: .groupChatsTapped) details: context.viewState.settings.map {
} label: { ListDetailsLabel.title(context.viewState.strings.string(for: $0.groupChatsMode))
LabeledContent { } ?? .isWaiting(true),
if let settings = context.viewState.settings { kind: .navigationLink {
Text(context.viewState.strings.string(for: settings.groupChatsMode)) context.send(viewAction: .groupChatsTapped)
} else { })
ProgressView() .disabled(context.viewState.settings == nil)
} .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
} label: {
Text(L10n.screenNotificationSettingsGroupChats)
}
}
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
.buttonStyle(.compoundForm(accessory: context.viewState.settings.flatMap { _ in .navigationLink }))
.disabled(context.viewState.settings == nil)
// Direct chats // Direct chats
Button { ListRow(label: .plain(title: L10n.screenNotificationSettingsDirectChats),
context.send(viewAction: .directChatsTapped) details: context.viewState.settings.map {
} label: { ListDetailsLabel.title(context.viewState.strings.string(for: $0.directChatsMode))
LabeledContent { } ?? .isWaiting(true),
if let settings = context.viewState.settings { kind: .navigationLink {
Text(context.viewState.strings.string(for: settings.directChatsMode)) context.send(viewAction: .directChatsTapped)
} else { })
ProgressView() .disabled(context.viewState.settings == nil)
} .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
} label: {
Text(L10n.screenNotificationSettingsDirectChats)
}
}
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
.buttonStyle(.compoundForm(accessory: context.viewState.settings.flatMap { _ in .navigationLink }))
.disabled(context.viewState.settings == nil)
} header: { } header: {
Text(L10n.screenNotificationSettingsNotificationSectionTitle) Text(L10n.screenNotificationSettingsNotificationSectionTitle)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
private var mentionsSection: some View { private var mentionsSection: some View {
Section { Section {
Toggle(isOn: $context.roomMentionsEnabled) { ListRow(label: .plain(title: L10n.screenNotificationSettingsRoomMentionLabel),
Text(L10n.screenNotificationSettingsRoomMentionLabel) kind: .toggle($context.roomMentionsEnabled))
} .disabled(context.viewState.settings?.roomMentionsEnabled == nil)
.toggleStyle(.compoundForm) .allowsHitTesting(!context.viewState.applyingChange)
.onChange(of: context.roomMentionsEnabled) { _ in .onChange(of: context.roomMentionsEnabled) { _ in
context.send(viewAction: .roomMentionChanged) context.send(viewAction: .roomMentionChanged)
} }
.disabled(context.viewState.settings?.roomMentionsEnabled == nil)
.allowsHitTesting(!context.viewState.applyingChange)
} header: { } header: {
Text(L10n.screenNotificationSettingsMentionsSectionTitle) Text(L10n.screenNotificationSettingsMentionsSectionTitle)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
private var callsSection: some View { private var callsSection: some View {
Section { Section {
Toggle(isOn: $context.callsEnabled) { ListRow(label: .plain(title: L10n.screenNotificationSettingsCallsLabel),
Text(L10n.screenNotificationSettingsCallsLabel) kind: .toggle($context.callsEnabled))
} .disabled(context.viewState.settings?.callsEnabled == nil)
.toggleStyle(.compoundForm) .allowsHitTesting(!context.viewState.applyingChange)
.onChange(of: context.callsEnabled) { _ in .onChange(of: context.callsEnabled) { _ in
context.send(viewAction: .callsChanged) context.send(viewAction: .callsChanged)
} }
.disabled(context.viewState.settings?.callsEnabled == nil)
.allowsHitTesting(!context.viewState.applyingChange)
} header: { } header: {
Text(L10n.screenNotificationSettingsAdditionalSettingsSectionTitle) Text(L10n.screenNotificationSettingsAdditionalSettingsSectionTitle)
.compoundFormSectionHeader() .compoundListSectionHeader()
} }
.compoundFormSection()
} }
} }

View File

@ -38,7 +38,7 @@ struct SettingsScreen: View {
signOutSection signOutSection
} }
.compoundForm() .compoundList()
.navigationTitle(L10n.commonSettings) .navigationTitle(L10n.commonSettings)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { .toolbar {
@ -48,118 +48,118 @@ struct SettingsScreen: View {
} }
} }
private var versionText: some View { private var versionText: Text {
Text(L10n.settingsVersionNumber(InfoPlistReader.main.bundleShortVersionString, InfoPlistReader.main.bundleVersion)) Text(L10n.settingsVersionNumber(InfoPlistReader.main.bundleShortVersionString, InfoPlistReader.main.bundleVersion))
} }
private var userSection: some View { private var userSection: some View {
Section { Section {
HStack(spacing: 12) { ListRow(kind: .custom {
LoadableAvatarImage(url: context.viewState.userAvatarURL, HStack(spacing: 12) {
name: context.viewState.userDisplayName, LoadableAvatarImage(url: context.viewState.userAvatarURL,
contentID: context.viewState.userID, name: context.viewState.userDisplayName,
avatarSize: .user(on: .settings), contentID: context.viewState.userID,
imageProvider: context.imageProvider) avatarSize: .user(on: .settings),
.accessibilityHidden(true) imageProvider: context.imageProvider)
.accessibilityHidden(true)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text(context.viewState.userDisplayName ?? "") Text(context.viewState.userDisplayName ?? "")
.font(.compound.headingMD) .font(.compound.headingMD)
.foregroundColor(.compound.textPrimary) .foregroundColor(.compound.textPrimary)
Text(context.viewState.userID) Text(context.viewState.userID)
.font(.compound.bodySM) .font(.compound.bodySM)
.foregroundColor(.compound.textSecondary) .foregroundColor(.compound.textSecondary)
}
.accessibilityElement(children: .combine)
} }
.accessibilityElement(children: .combine) .padding(.horizontal, ListRowPadding.horizontal)
} .padding(.vertical, 8)
})
} }
.compoundFormSection()
} }
private var sessionVerificationSection: some View { private var sessionVerificationSection: some View {
Section { Section {
Button { context.send(viewAction: .sessionVerification) } label: { ListRow(label: .default(title: L10n.actionCompleteVerification,
Label(L10n.actionCompleteVerification, systemImage: "checkmark.shield") systemIcon: .checkmarkShield),
} kind: .button { context.send(viewAction: .sessionVerification) })
.buttonStyle(.compoundForm())
} }
.compoundFormSection()
} }
private var developerOptionsSection: some View { private var developerOptionsSection: some View {
Section { Section {
Button { context.send(viewAction: .developerOptions) } label: { ListRow(label: .default(title: L10n.commonDeveloperOptions,
Label(L10n.commonDeveloperOptions, systemImage: "hammer.circle") systemIcon: .hammerCircle),
} kind: .navigationLink {
.buttonStyle(.compoundForm(accessory: .navigationLink)) context.send(viewAction: .developerOptions)
.accessibilityIdentifier("developerOptionsButton") })
.accessibilityIdentifier("developerOptionsButton")
} }
.compoundFormSection()
} }
private var simplifiedSection: some View { private var simplifiedSection: some View {
Section { Section {
Picker(selection: $context.timelineStyle) { ListRow(label: .default(title: L10n.commonMessageLayout,
ForEach(TimelineStyle.allCases, id: \.self) { style in systemIcon: .rectangleGrid1x2),
Text(style.name) kind: .picker(selection: $context.timelineStyle,
.tag(style) items: TimelineStyle.allCases.map { (title: $0.name, tag: $0) }))
.accessibilityIdentifier("timelineStylePicker")
.onChange(of: context.timelineStyle) { _ in
context.send(viewAction: .changedTimelineStyle)
} }
} label: {
Label(L10n.commonMessageLayout, systemImage: "rectangle.grid.1x2")
}
.labelStyle(.compoundFormRow())
.accessibilityIdentifier("timelineStylePicker")
.onChange(of: context.timelineStyle) { _ in
context.send(viewAction: .changedTimelineStyle)
}
// Notifications // Notifications
if context.viewState.showNotificationSettings { if context.viewState.showNotificationSettings {
Button { context.send(viewAction: .notifications) } label: { ListRow(label: .default(title: L10n.screenNotificationSettingsTitle,
Label(L10n.screenNotificationSettingsTitle, systemImage: "bell") systemIcon: .bell),
} kind: .navigationLink {
.buttonStyle(.compoundForm(accessory: .navigationLink)) context.send(viewAction: .notifications)
.accessibilityIdentifier("notificationsButton") })
.accessibilityIdentifier("notificationsButton")
} }
// Analytics // Analytics
Button { context.send(viewAction: .analytics) } label: { ListRow(label: .default(title: L10n.commonAnalytics,
Label(L10n.commonAnalytics, systemImage: "chart.bar") systemIcon: .chartBar),
} kind: .navigationLink {
.buttonStyle(.compoundForm(accessory: .navigationLink)) context.send(viewAction: .analytics)
.accessibilityIdentifier("analyticsButton") })
.accessibilityIdentifier("analyticsButton")
// Report Bug // Report Bug
Button { context.send(viewAction: .reportBug) } label: { ListRow(label: .default(title: L10n.commonReportABug,
Label(L10n.commonReportABug, systemImage: "ladybug") systemIcon: .ladybug),
} kind: .navigationLink {
.buttonStyle(.compoundForm(accessory: .navigationLink)) context.send(viewAction: .reportBug)
.accessibilityIdentifier("reportBugButton") })
.accessibilityIdentifier("reportBugButton")
// About // About
Button { context.send(viewAction: .about) } label: { ListRow(label: .default(title: L10n.commonAbout,
Label(L10n.commonAbout, systemImage: "questionmark.circle") systemIcon: .questionmarkCircle),
} kind: .navigationLink {
.buttonStyle(.compoundForm(accessory: .navigationLink)) context.send(viewAction: .about)
.accessibilityIdentifier("aboutButton") })
.accessibilityIdentifier("aboutButton")
} }
.compoundFormSection()
} }
private var signOutSection: some View { private var signOutSection: some View {
Section { Section {
Button { showingLogoutConfirmation = true } label: { ListRow(label: .default(title: L10n.screenSignoutPreferenceItem,
Label(L10n.screenSignoutPreferenceItem, systemImage: "rectangle.portrait.and.arrow.right") systemIcon: .rectanglePortraitAndArrowRight),
} kind: .button {
.buttonStyle(.compoundForm()) showingLogoutConfirmation = true
.accessibilityIdentifier("logoutButton") })
.alert(L10n.screenSignoutConfirmationDialogTitle, isPresented: $showingLogoutConfirmation) { .accessibilityIdentifier("logoutButton")
Button(L10n.screenSignoutConfirmationDialogSubmit, .alert(L10n.screenSignoutConfirmationDialogTitle, isPresented: $showingLogoutConfirmation) {
role: .destructive, Button(L10n.screenSignoutConfirmationDialogSubmit,
action: logout) role: .destructive,
} message: { action: logout)
Text(L10n.screenSignoutConfirmationDialogContent) } message: {
} Text(L10n.screenSignoutConfirmationDialogContent)
}
} footer: { } footer: {
VStack { VStack {
versionText versionText
@ -169,11 +169,10 @@ struct SettingsScreen: View {
Text(deviceID) Text(deviceID)
} }
} }
.compoundFormSectionFooter() .compoundListSectionFooter()
.textSelection(.enabled) .textSelection(.enabled)
.padding(.top, 24) .padding(.top, 24)
} }
.compoundFormSection()
} }
private var doneButton: some View { private var doneButton: some View {
@ -204,7 +203,10 @@ private extension TimelineStyle {
struct SettingsScreen_Previews: PreviewProvider { struct SettingsScreen_Previews: PreviewProvider {
static let viewModel = { static let viewModel = {
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com", let verificationController = SessionVerificationControllerProxyMock()
verificationController.isVerified = false
let userSession = MockUserSession(sessionVerificationController: verificationController,
clientProxy: MockClientProxy(userID: "@userid:example.com",
deviceID: "AAAAAAAAAAA"), deviceID: "AAAAAAAAAAA"),
mediaProvider: MockMediaProvider()) mediaProvider: MockMediaProvider())
ServiceLocator.shared.settings.notificationSettingsEnabled = true ServiceLocator.shared.settings.notificationSettingsEnabled = true

View File

@ -18,7 +18,7 @@ import Combine
struct MockUserSession: UserSessionProtocol { struct MockUserSession: UserSessionProtocol {
let callbacks = PassthroughSubject<UserSessionCallback, Never>() let callbacks = PassthroughSubject<UserSessionCallback, Never>()
let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil var sessionVerificationController: SessionVerificationControllerProxyProtocol?
var userID: String { clientProxy.userID } var userID: String { clientProxy.userID }
var deviceID: String? { clientProxy.deviceID } var deviceID: String? { clientProxy.deviceID }
var homeserver: String { clientProxy.homeserver } var homeserver: String { clientProxy.homeserver }

View File

@ -149,7 +149,10 @@ class MockScreen: Identifiable {
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .templateScreen: case .templateScreen:
return TemplateScreenCoordinator(parameters: .init()) let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = TemplateScreenCoordinator(parameters: .init())
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .home: case .home:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"), let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Compound
import SwiftUI import SwiftUI
struct TemplateScreen: View { struct TemplateScreen: View {
@ -22,22 +23,15 @@ struct TemplateScreen: View {
var body: some View { var body: some View {
Form { Form {
Section { Section {
TextField(text: $context.composerText) { ListRow(label: .plain(title: context.viewState.placeholder),
Text(context.viewState.placeholder) kind: .textField(text: $context.composerText))
.compoundFormTextFieldPlaceholder()
}
.textFieldStyle(.compoundForm)
Button { ListRow(label: .centeredAction(title: L10n.actionDone,
context.send(viewAction: .done) systemIcon: .doorLeftHandClosed),
} label: { kind: .button { context.send(viewAction: .done) })
Label("Done", systemImage: "door.left.hand.closed")
}
.buttonStyle(.compoundFormCentred())
} }
.compoundFormSection()
} }
.compoundForm() .compoundList()
.navigationTitle(context.viewState.title) .navigationTitle(context.viewState.title)
.onChange(of: context.composerText) { _ in .onChange(of: context.composerText) { _ in
context.send(viewAction: .textChanged) context.send(viewAction: .textChanged)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -83,7 +83,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
notificationSettingsProxyMock.callbacks.send(.settingsDidChange) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefautSetting) let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefaultSetting)
.removeDuplicates() .removeDuplicates()
.collect(3).first()) .collect(3).first())
viewModel.state.bindings.allowCustomSetting = false viewModel.state.bindings.allowCustomSetting = false

View File

@ -0,0 +1 @@
Use Compound ListRow instead of .xyzStyle(.compoundRow)

View File

@ -51,7 +51,7 @@ packages:
path: DesignKit path: DesignKit
Compound: Compound:
url: https://github.com/vector-im/compound-ios url: https://github.com/vector-im/compound-ios
revision: 89e3ed5adef33be7eb65137b861833ffae64f961 revision: 50bb7cf313bd1ad17201fc7e4c1184737a0f44c2
# path: ../compound-ios # path: ../compound-ios
AnalyticsEvents: AnalyticsEvents:
url: https://github.com/matrix-org/matrix-analytics-events url: https://github.com/matrix-org/matrix-analytics-events