Design: Forms, Login and Members screen. (#608)

* Rename Settings styles to Form styles.

* Removed redundant FormPickerRow.

* Use the button style everywhere and fix icon shapes.
This commit is contained in:
Doug
2023-02-22 13:37:02 +00:00
committed by GitHub
parent 7d5d801d20
commit 246879f1bf
50 changed files with 338 additions and 293 deletions
@@ -0,0 +1,122 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// A view to be added on the trailing edge of a form row.
enum FormRowAccessory: View {
case navigationLink
var body: some View {
switch self {
case .navigationLink:
return Image(systemName: "chevron.forward")
.font(.element.subheadlineBold)
.foregroundColor(.element.quaternaryContent)
}
}
}
/// Default button styling for form rows.
///
/// The primitive style is needed to set the list row insets to `0`. The inner style is then needed
/// to change the background colour depending on whether the button is currently pressed or not.
struct FormButtonStyle: PrimitiveButtonStyle {
/// An accessory to be added on the trailing side of the row.
var accessory: FormRowAccessory?
func makeBody(configuration: Configuration) -> some View {
Button(action: configuration.trigger) {
configuration.label
.frame(maxHeight: .infinity) // Make sure the label fills the cell vertically.
}
.buttonStyle(Style(accessory: accessory))
.listRowInsets(EdgeInsets()) // Remove insets so the background fills the cell.
}
/// Inner style used to set the pressed background colour.
struct Style: ButtonStyle {
var accessory: FormRowAccessory?
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
.labelStyle(FormRowLabelStyle())
.foregroundColor(.element.primaryContent)
.frame(maxWidth: .infinity, alignment: .leading)
accessory
}
.contentShape(Rectangle())
.padding(FormRow.insets) // Re-apply the normal insets using padding.
.background(configuration.isPressed ? Color.element.quinaryContent : .clear)
}
}
}
/// Small squared action button style for settings screens
struct FormActionButtonStyle: ButtonStyle {
let title: String
@ScaledMetric private var menuIconSize = 54.0
func makeBody(configuration: Configuration) -> some View {
VStack {
configuration.label
.buttonStyle(.plain)
.foregroundColor(.element.primaryContent)
.frame(width: menuIconSize, height: menuIconSize)
.background {
RoundedRectangle(cornerRadius: 16)
.fill(configuration.isPressed ? Color.element.quinaryContent : .element.formRowBackground)
}
Text(title)
.foregroundColor(.element.secondaryContent)
.font(.element.subheadline)
}
}
}
struct FormButtonStyles_Previews: PreviewProvider {
static var previews: some View {
Form {
Section {
Button { } label: {
Label("Hello world", systemImage: "globe")
}
.buttonStyle(FormButtonStyle())
Button { } label: {
Label("Show something", systemImage: "rectangle.portrait")
}
.buttonStyle(FormButtonStyle(accessory: .navigationLink))
ShareLink(item: "test")
.buttonStyle(FormButtonStyle())
}
.formSectionStyle()
Section {
Button { } label: {
Text("Hello world")
}
.buttonStyle(FormButtonStyle())
}
.formSectionStyle()
}
}
}
@@ -0,0 +1,22 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
enum FormRow {
/// Element specific insets that are used for all our Form rows.
static let insets = EdgeInsets(top: 7, leading: 16, bottom: 7, trailing: 16)
}
@@ -16,7 +16,7 @@
import SwiftUI
struct SettingsRowLabelStyle: LabelStyle {
struct FormRowLabelStyle: LabelStyle {
@ScaledMetric private var menuIconSize = 30.0
var alignment: VerticalAlignment = .firstTextBaseline
@@ -26,12 +26,31 @@ struct SettingsRowLabelStyle: LabelStyle {
configuration.icon
.foregroundColor(.element.secondaryContent)
.padding(4)
.frame(width: menuIconSize, height: menuIconSize)
.background(Color.element.formBackground)
.clipShape(RoundedRectangle(cornerRadius: 8))
.frame(width: menuIconSize, height: menuIconSize)
configuration.title
.font(.element.body)
.foregroundColor(.element.primaryContent)
}
}
}
struct FormRowLabelStyle_Previews: PreviewProvider {
static var previews: some View {
VStack(alignment: .leading) {
Label("Person", systemImage: "person")
.labelStyle(FormRowLabelStyle())
Label("Help", systemImage: "questionmark.circle")
.labelStyle(FormRowLabelStyle())
Label("Camera", systemImage: "camera")
.labelStyle(FormRowLabelStyle())
Label("Help", systemImage: "questionmark")
.labelStyle(FormRowLabelStyle())
}
.padding()
}
}
@@ -16,7 +16,7 @@
import SwiftUI
/// Style for section header
/// Style for form section headers
struct FormSectionHeaderStyle: ViewModifier {
func body(content: Content) -> some View {
content
@@ -25,9 +25,23 @@ struct FormSectionHeaderStyle: ViewModifier {
}
}
/// Standard style for form sections
struct FormSectionStyle: ViewModifier {
func body(content: Content) -> some View {
content
.listRowSeparator(.hidden)
.listRowInsets(FormRow.insets)
.listRowBackground(Color.element.formRowBackground)
}
}
extension View {
/// Applies the `FormSectionHeaderStyle` modifier to the view
func formSectionHeader() -> some View {
modifier(FormSectionHeaderStyle())
}
func formSectionStyle() -> some View {
modifier(FormSectionStyle())
}
}
@@ -1,41 +0,0 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// Small squared action button style for settings screens
struct SettingsActionButtonStyle: ButtonStyle {
let title: String
@ScaledMetric private var menuIconSize = 54.0
func makeBody(configuration: Configuration) -> some View {
VStack {
configuration.label
.buttonStyle(.plain)
.foregroundColor(.element.primaryContent)
.frame(width: menuIconSize, height: menuIconSize)
.background {
RoundedRectangle(cornerRadius: 16)
.fill(Color.element.formRowBackground.opacity(configuration.isPressed ? 0.5 : 1))
}
Text(title)
.foregroundColor(.element.secondaryContent)
.font(.element.subheadline)
}
}
}
@@ -1,53 +0,0 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// Default row that can be reused for settings screens
struct SettingsDefaultRow: View {
// MARK: Public
let title: String
let image: Image
let action: () -> Void
// MARK: Private
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
// MARK: Views
var body: some View {
Button(action: action) {
HStack(spacing: 16) {
Label(title: {
Text(title)
}, icon: {
image
})
.labelStyle(SettingsRowLabelStyle())
Spacer()
Image(systemName: "chevron.forward")
.foregroundColor(.element.tertiaryContent)
}
}
.listRowInsets(listRowInsets)
.listRowSeparator(.hidden)
.foregroundColor(.element.primaryContent)
}
}
@@ -1,53 +0,0 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// Picker row that can be reused for settings screens
struct SettingsPickerRow<SelectionValue: Hashable, Content: View>: View {
// MARK: Public
let title: String
let image: Image
@Binding var selection: SelectionValue
let content: () -> Content
// MARK: Private
@ScaledMetric private var menuIconSize = 30.0
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
// MARK: Views
var body: some View {
Picker(selection: $selection, content: content) {
HStack(spacing: 16) {
image
.foregroundColor(.element.systemGray)
.padding(4)
.background(Color.element.systemGray6)
.clipShape(RoundedRectangle(cornerRadius: 8))
.frame(width: menuIconSize, height: menuIconSize)
Text(title)
.font(.element.body)
.foregroundColor(.element.primaryContent)
}
}
.listRowInsets(listRowInsets)
.listRowSeparator(.hidden)
}
}
@@ -92,12 +92,12 @@ final class LoginCoordinator: CoordinatorProtocol {
static let loadingIndicatorIdentifier = "LoginCoordinatorLoading"
private func startLoading(isInteractionBlocking: Bool) {
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal,
title: ElementL10n.loading,
persistent: true))
if !isInteractionBlocking {
if isInteractionBlocking {
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal,
title: ElementL10n.loading,
persistent: true))
} else {
viewModel.update(isLoading: true)
}
}
@@ -75,7 +75,10 @@ struct LoginScreen: View {
.padding(.horizontal, 16)
.padding(.bottom, 8)
TextField(ElementL10n.loginSigninUsernameHint, text: $context.username)
TextField(ElementL10n.loginSigninUsernameHint,
text: $context.username,
// Prompt colour fixes a flicker that occurs before the text field style introspects the field.
prompt: Text(ElementL10n.loginSigninUsernameHint).foregroundColor(.element.tertiaryContent))
.focused($isUsernameFocused)
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername))
.disableAutocorrection(true)
@@ -86,7 +89,10 @@ struct LoginScreen: View {
.onSubmit { isPasswordFocused = true }
.padding(.bottom, 20)
SecureField(ElementL10n.loginSignupPasswordHint, text: $context.password)
SecureField(ElementL10n.loginSignupPasswordHint,
text: $context.password,
// Prompt colour fixes a flicker that occurs before the text field style introspects the field.
prompt: Text(ElementL10n.loginSignupPasswordHint).foregroundColor(.element.tertiaryContent))
.focused($isPasswordFocused)
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.password))
.textContentType(.password)
@@ -133,6 +139,8 @@ struct LoginScreen: View {
private func submit() {
guard context.viewState.canSubmit else { return }
context.send(viewAction: .next)
isUsernameFocused = false
isPasswordFocused = false
}
}
@@ -96,11 +96,15 @@ struct BugReportScreen: View {
private var sendLogsToggle: some View {
VStack(spacing: 8) {
Toggle(ElementL10n.bugReportScreenIncludeLogs, isOn: $context.sendingLogsEnabled)
.foregroundColor(.element.primaryContent)
.tint(Color.element.brand)
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.sendLogs)
.padding(.horizontal, 16)
.padding(.vertical, 11)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.element.formRowBackground))
.padding(.vertical, 6.5)
.background {
RoundedRectangle(cornerRadius: 14)
.fill(Color.element.formRowBackground)
}
Text(ElementL10n.bugReportScreenLogsDescription)
.font(.element.caption1)
@@ -117,13 +121,13 @@ struct BugReportScreen: View {
photoLibrary: .shared()) {
HStack(spacing: 16) {
Label(context.viewState.screenshot == nil ? ElementL10n.bugReportScreenAttachScreenshot : ElementL10n.bugReportScreenEditScreenshot, systemImage: "camera")
.labelStyle(SettingsRowLabelStyle())
.labelStyle(FormRowLabelStyle())
Spacer()
}
}
.padding(.horizontal, 16)
.padding(.vertical, 11)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.element.formRowBackground))
.buttonStyle(FormButtonStyle())
.background(Color.element.formRowBackground)
.cornerRadius(14)
.accessibilityIdentifier(A11yIdentifiers.bugReportScreen.attachScreenshot)
if let screenshot = context.viewState.screenshot {
ZStack(alignment: .topTrailing) {
@@ -173,11 +177,14 @@ struct BugReport_Previews: PreviewProvider {
isModallyPresented: false)
static var previews: some View {
Group {
NavigationStack {
BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(),
screenshot: nil,
isModallyPresented: false).context)
.previewDisplayName("Without Screenshot")
}
NavigationStack {
BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(),
screenshot: Asset.Images.appLogo.image,
isModallyPresented: false).context)
@@ -29,8 +29,9 @@ struct DeveloperOptionsScreenScreen: View {
Text("🥳")
.frame(maxWidth: .infinity)
}
.buttonStyle(FormButtonStyle())
}
.listRowBackground(Color.element.formRowBackground)
.formSectionStyle()
}
.overlay(effectsView)
.scrollContentBackground(.hidden)
@@ -43,6 +44,8 @@ struct DeveloperOptionsScreenScreen: View {
private var effectsView: some View {
if showConfetti {
EffectsView(effect: .confetti)
.ignoresSafeArea()
.allowsHitTesting(false)
.task { await removeConfettiAfterDelay() }
}
}
@@ -41,7 +41,7 @@ struct HomeScreen: View {
}
}
.shimmer()
.disabled(context.viewState.roomListMode == .skeletons)
.disabled(true)
} else {
LazyVStack(spacing: 0) {
ForEach(context.viewState.visibleRooms) { room in
@@ -79,6 +79,7 @@ struct HomeScreen: View {
}
}
.scrollDismissesKeyboard(.immediately)
.scrollDisabled(context.viewState.roomListMode == .skeletons)
.animation(.elementDefault, value: context.viewState.showSessionVerificationBanner)
.animation(.elementDefault, value: context.viewState.roomListMode)
.alert(item: $context.alertInfo) { $0.alert }
@@ -17,25 +17,18 @@
import SwiftUI
struct RoomDetailsScreen: View {
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
@ObservedObject var context: RoomDetailsViewModel.Context
var body: some View {
Form {
headerSection
if let topic = context.viewState.topic {
topicSection(with: topic)
.listRowBackground(Color.element.formRowBackground)
}
topicSection
aboutSection
.listRowBackground(Color.element.formRowBackground)
if context.viewState.isEncrypted {
securitySection
.listRowBackground(Color.element.formRowBackground)
}
}
.scrollContentBackground(.hidden)
@@ -71,12 +64,12 @@ struct RoomDetailsScreen: View {
Button { context.send(viewAction: .copyRoomLink) } label: {
Image(systemName: "link")
}
.buttonStyle(SettingsActionButtonStyle(title: ElementL10n.roomDetailsCopyLink))
.buttonStyle(FormActionButtonStyle(title: ElementL10n.roomDetailsCopyLink))
ShareLink(item: permalink) {
Image(systemName: "square.and.arrow.up")
}
.buttonStyle(SettingsActionButtonStyle(title: ElementL10n.inviteUsersToRoomActionInvite.capitalized))
.buttonStyle(FormActionButtonStyle(title: ElementL10n.inviteUsersToRoomActionInvite.capitalized))
}
.padding(.top, 32)
}
@@ -85,14 +78,18 @@ struct RoomDetailsScreen: View {
.listRowBackground(Color.clear)
}
private func topicSection(with topic: String) -> some View {
Section {
Text(topic)
.foregroundColor(.element.secondaryContent)
.font(.element.footnote)
} header: {
Text(ElementL10n.roomSettingsTopic)
.formSectionHeader()
@ViewBuilder
private var topicSection: some View {
if let topic = context.viewState.topic {
Section {
Text(topic)
.foregroundColor(.element.secondaryContent)
.font(.element.footnote)
} header: {
Text(ElementL10n.roomSettingsTopic)
.formSectionHeader()
}
.formSectionStyle()
}
}
@@ -101,24 +98,19 @@ struct RoomDetailsScreen: View {
Button {
context.send(viewAction: .processTapPeople)
} label: {
HStack {
Label(ElementL10n.bottomActionPeople, systemImage: "person")
.labelStyle(SettingsRowLabelStyle())
Spacer()
LabeledContent {
if context.viewState.isLoadingMembers {
ProgressView()
} else {
Text(String(context.viewState.members.count))
.foregroundColor(.element.secondaryContent)
.foregroundColor(.element.tertiaryContent)
.font(.element.body)
Image(systemName: "chevron.forward")
.foregroundColor(.element.quaternaryContent)
}
} label: {
Label(ElementL10n.bottomActionPeople, systemImage: "person")
}
}
.listRowInsets(listRowInsets)
.buttonStyle(FormButtonStyle(accessory: context.viewState.isLoadingMembers ? nil : .navigationLink))
.foregroundColor(.element.primaryContent)
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.people)
.disabled(context.viewState.isLoadingMembers)
@@ -126,33 +118,34 @@ struct RoomDetailsScreen: View {
Text(ElementL10n.roomDetailsAboutSectionTitle)
.formSectionHeader()
}
.formSectionStyle()
}
private var securitySection: some View {
Section {
HStack(alignment: .top) {
Label(title: {
Label {
VStack(alignment: .leading, spacing: 2) {
Text(ElementL10n.encryptionEnabled)
Text(ElementL10n.encryptionEnabledTileDescription)
.foregroundColor(.element.secondaryContent)
.font(.element.footnote)
}
}, icon: {
} icon: {
Image(systemName: "lock.shield")
})
.labelStyle(SettingsRowLabelStyle(alignment: .top))
}
.labelStyle(FormRowLabelStyle(alignment: .top))
Spacer()
Image(systemName: "checkmark")
.foregroundColor(.element.quaternaryContent)
}
.padding(.horizontal, -3)
} header: {
Text(ElementL10n.roomProfileSectionSecurity)
.formSectionHeader()
}
.formSectionStyle()
}
}
@@ -39,8 +39,9 @@ struct RoomMemberDetailsScreen: View {
}
}
.searchable(text: $context.searchQuery, placement: .navigationBarDrawer(displayMode: .always))
.alert(item: $context.alertInfo) { $0.alert }
.background(Color.element.background.ignoresSafeArea())
.navigationTitle(ElementL10n.bottomActionPeople)
.alert(item: $context.alertInfo) { $0.alert }
}
}
@@ -21,30 +21,23 @@ struct SettingsScreen: View {
@ScaledMetric private var avatarSize = AvatarSize.user(on: .settings).value
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
@ObservedObject var context: SettingsScreenViewModel.Context
var body: some View {
Form {
userSection
.listRowBackground(Color.element.formRowBackground)
if context.viewState.showSessionVerificationSection {
sessionVerificationSection
.listRowBackground(Color.element.formRowBackground)
}
simplifiedSection
.listRowBackground(Color.element.formRowBackground)
if context.viewState.showDeveloperOptions {
developerOptionsSection
.listRowBackground(Color.element.formRowBackground)
}
signOutSection
.listRowBackground(Color.element.formRowBackground)
}
.scrollContentBackground(.hidden)
.background(Color.element.formBackground.ignoresSafeArea())
@@ -69,6 +62,7 @@ struct SettingsScreen: View {
contentID: context.viewState.userID,
avatarSize: .user(on: .settings),
imageProvider: context.imageProvider)
.accessibilityHidden(true)
VStack(alignment: .leading, spacing: 4) {
Text(context.viewState.userDisplayName ?? "")
@@ -78,62 +72,66 @@ struct SettingsScreen: View {
.font(.element.subheadline)
.foregroundColor(.element.primaryContent)
}
.accessibilityElement(children: .combine)
}
.listRowInsets(listRowInsets)
}
.formSectionStyle()
}
private var sessionVerificationSection: some View {
Section {
SettingsDefaultRow(title: ElementL10n.settingsSessionVerification,
image: Image(systemName: "checkmark.shield")) {
context.send(viewAction: .sessionVerification)
Button { context.send(viewAction: .sessionVerification) } label: {
Label(ElementL10n.settingsSessionVerification, systemImage: "checkmark.shield")
}
.buttonStyle(FormButtonStyle())
}
.formSectionStyle()
}
private var developerOptionsSection: some View {
Section {
SettingsDefaultRow(title: ElementL10n.settingsDeveloperOptions,
image: Image(systemName: "hammer.circle")) {
context.send(viewAction: .developerOptions)
Button { context.send(viewAction: .developerOptions) } label: {
Label(ElementL10n.settingsDeveloperOptions, systemImage: "hammer.circle")
}
.buttonStyle(FormButtonStyle(accessory: .navigationLink))
.accessibilityIdentifier("sessionVerificationButton")
}
.formSectionStyle()
}
private var simplifiedSection: some View {
Section {
SettingsPickerRow(title: ElementL10n.settingsTimelineStyle,
image: Image(systemName: "rectangle.grid.1x2"),
selection: $context.timelineStyle) {
Picker(selection: $context.timelineStyle) {
ForEach(TimelineStyle.allCases, id: \.self) { style in
Text(style.name)
.tag(style)
}
} label: {
Label(ElementL10n.settingsTimelineStyle, systemImage: "rectangle.grid.1x2")
.labelStyle(FormRowLabelStyle())
}
.accessibilityIdentifier("timelineStylePicker")
.onChange(of: context.timelineStyle) { _ in
context.send(viewAction: .changedTimelineStyle)
}
SettingsDefaultRow(title: ElementL10n.sendBugReport,
image: Image(systemName: "questionmark.circle")) {
context.send(viewAction: .reportBug)
Button { context.send(viewAction: .reportBug) } label: {
Label(ElementL10n.sendBugReport, systemImage: "questionmark.circle")
}
.buttonStyle(FormButtonStyle(accessory: .navigationLink))
.accessibilityIdentifier("reportBugButton")
}
.formSectionStyle()
}
private var signOutSection: some View {
Section {
SettingsDefaultRow(title: ElementL10n.actionSignOut,
image: Image(systemName: "rectangle.portrait.and.arrow.right")) {
showingLogoutConfirmation = true
Button { showingLogoutConfirmation = true } label: {
Label(ElementL10n.actionSignOut, systemImage: "rectangle.portrait.and.arrow.right")
}
.buttonStyle(FormButtonStyle())
.accessibilityIdentifier("logoutButton")
.alert(ElementL10n.actionSignOut,
isPresented: $showingLogoutConfirmation) {
.alert(ElementL10n.actionSignOut, isPresented: $showingLogoutConfirmation) {
Button(ElementL10n.actionSignOut,
role: .destructive,
action: logout)
@@ -155,6 +153,7 @@ struct SettingsScreen: View {
}
.padding(.top, 24)
}
.formSectionStyle()
}
private var doneButton: some View {
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:840363fb8bda7290d75aad9dd2bfee3583299aca1682a18d9348c17cdff495de
size 115274
oid sha256:358aedfda97e567f398fc13908ba07135bd5d85c7b1000cc81ff3000b81d75d7
size 115132
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f542e446145a1ce909d977120e5d32cf172d6adb7bfd54ebb7f7b0428bb94f1d
size 115114
oid sha256:446e2ec1437988533e82ab70b91b57cabf1d3e43be62bdc39014325b2ebc2f26
size 115963
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7a370935dd82dde19d887038b076f155273e0dfaa3af6c9fa33bcfb97b825eb6
size 169111
oid sha256:e8872122c06b940ed87c5f191b5a13c61b9fe9e05aa0e16013d2e21932d3cf13
size 168982
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc4a8c344dd80070277035e811b84b31ae9050d4f11bb22e507e0cf402cc5e56
size 171658
oid sha256:844522141b319587c5179dc67202e97fdb9dba1e1fc8ce8358787b0dd38e63ce
size 171543
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e80577c2f9a41da888e211e5948169c7b8449f6da79403f2787f98e4f9532819
size 122129
oid sha256:95af9a204ad87a1aa1aa722623627395e81c8414023e28a67a40b92689e077aa
size 121979
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c1a4f312dabc8e0f7ca1f4555cc0fb9212845c40d54b104aaa8e6df0a1b1a1e
size 90593
oid sha256:bf58b16186bdaf165064a675a75a33a66a3ddd626c715e29bfd534844e209c1d
size 90332
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0677000cc125bb1c3aaaf02b0236939085c4ad2d1f34e653f2b81dfaafb98ab7
size 115608
oid sha256:023baadc85550005385b8fd770a55bc2f082d52514837385855184758dbcdcf5
size 116212
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:929b6e0abf913cc4598dedd2faef6845b73697d163480dab3339f88d90e43970
size 101082
oid sha256:50f9b885cf0c41930c1137c1e6ffb0517d0eca6e6d67a32f6cca41be3896ed67
size 109633
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:52c8c00c02ba4da3ea07981704182f02e9e3981befd6f7c7ade1b51f7ce3187b
size 151905
oid sha256:316c358d1c29ba6543cef3fe17a496120f58fc8c1f67f44eedfe6cbb0c842cf7
size 151665
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a28aa63ec2b888633e8898bb7c8d91ae784da1569d3faa45f29a209d6dea5354
size 151771
oid sha256:3a620a0f0aa381737f5c290aedd2a49316c0280701d2fd44402b3eccef00c7c5
size 154154
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:add279f6f7f8f9d15941d2b9b3c1046165bb7f5df30d964a50763a1fbbc59c05
size 165889
oid sha256:5587816cfd6aeaf9d6e7eccf45174292c3f32d7ac7ea0a81553103c50aba079c
size 167343
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:31791083395412c78e885160659df1ba7ba1a0cc2bc927fdc7e61b09f1cb6f6c
size 169888
oid sha256:4942a438cf706b6c69ba033c09d43b266940ecd1a39a155edb8e65cdd39939a6
size 171248
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:219bdb41bad2e50060ffb9b9052626ac77763a4e80e3f0a7e90e0f8a881f6f71
size 158742
oid sha256:2362b200a07dedfc74adcfcec55d20a8c6605b4bcc5ee3a726030c93d7a1728d
size 158371
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08ec2c9755ee86d41b9825cfb4ad031e480ac8aacc3591e1a145e27344c831ee
size 106500
oid sha256:fbcfc6944b4c32583168276b6d64f68618dde1de251c772156918aa7b6644594
size 108453
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:feb2d3126893d206fe486e22b0277f8cdf3e69fc1e748c1386f0f733189595b6
size 142603
oid sha256:e2d8a909c8ea5d9877ac951bd56334481f924b9a8ce1a495f7352164f1815d2a
size 145566
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f39c68000391860a8260c6faeb29954456d3f10f8cacef51ed1f5850ec7b85e1
size 126031
oid sha256:61f029120bf78b803acd6634c016daad77a70d49803e0a628a2621a6a0d2fe9b
size 133059
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e61714b5faf17d54f5e1053fb6d4062dae065caa59987fb6a35a66652dabc317
size 114745
oid sha256:1f28e2dcc9e240a607968f6d701d4ca7e8770e49edd3a6ede7e6c7fbb27a62ab
size 114594
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e66ec0e654b0c209b675142bd4acec0220b65d088822c35c14521ab57cec0b46
size 114579
oid sha256:8ba414a888dc1d6d28e5fb27a949493ab2a6dcc1e73216e6765bc5f4bd59bc45
size 115342
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e2771a646e6581abab1c86c79ae463c817cc61f8eef45e23dbccf856fa69286c
size 168402
oid sha256:e070d58f45db27aeb9fec478c5a1651fc03d6e6bf43abca1618093e06123e642
size 168248
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11073c8e77147d476713c92ae2209dde2593bd1e52f8444790eaca040d978f07
size 171000
oid sha256:44ad27e09e5b13ecb849a54fdfb38bd27c0a4869a9537178aa2c09ce343e0cd9
size 170880
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb1983b49b4ebd61e4717dcd8a500307839d3e4a96d24afb4e37f236c083da10
size 121992
oid sha256:c157347a9e3a790911d247451103146cbee0e341c798e1cc2d8b5938baaa806e
size 121841
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:999309a2b2227b3b5bc2c471fc60b7de0ff060bbffc60e18493651d31ccc2e2e
size 86938
oid sha256:5781e2acf57989a2091b6bcc1287a76966ab6b5ecea800b9e096338b31b60704
size 87190
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9892c8a4cb57e9d678204a0995f048cc9810c03bece188ee69504f0b81478e23
size 112193
oid sha256:8da611272e84a28cc3a02b3ffdda36703a6ad8c9927ad9bd5bc412b95018bb16
size 112731
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b84b32cd357de819895541fb6ed10c562649e9f3596108151cd93e4d815977d5
size 101777
oid sha256:30ff9c1d2ba944923c4ffa52b258263b03c57243c226b6916220fb532ff56111
size 110398
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9e910c1fb0e2e449b999f08c0e38f0fc1228506e903d22d53057687a3e1c6125
size 148744
oid sha256:64769e5b69982d2d83e2bbdfffb83f1d73079f077c8ee1c5b68163fc659d53f4
size 150686
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc190d5cda1f47929fb14fec12ff81a330d66e8ce4bca7846d896bc598410918
size 150772
oid sha256:be4d36138ecb1fdb0ce374d92827354851e53943e6a1bbdf62f4bf87ba755930
size 153234
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0668c40ede3181a2ff1091e4572908ed61549ca0fca4aee29b2f7ce4b5323bb
size 164888
oid sha256:71f01217f1d90a9a181047d51e373fbdd5411efa51578c9dcd156c573da7646c
size 166342
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5a5ee287e5f6d416e57105c6418f8c5346153af63027a7fb9f1443b8fe944a39
size 168860
oid sha256:72b1f8b6fca4375729f62d47c5b903f07419449cbbcbe9f77318b6ef9195a4a6
size 170255
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8e01515371cf24c0c527c2728d1a15090ff7162faa22b7d2dacfad15818b6aee
size 158582
oid sha256:3385b4006707085ccb922e443e5e84ee0b1ff4d9cee90fcaf8e369dcd37566b2
size 158211
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0065cc98273a41f0782f6f556187f8f3519db3063e0dc147edcdd37cad38dc6f
size 103310
oid sha256:7bedda0ec007954c232d31713a9e6eb6adae2b561d2a856dede57e68c62dae68
size 104960
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9a7db7bb77d8d87d185e8ed22b4f12be4a066678b0fa0f95200eb55111c0ab9
size 139278
oid sha256:59a7327e8b127222fcf1cef19094af0cc10446d5e5d6de09cf4c0765ffa3c83e
size 142239
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:63a0ed106d7a0cbe662657f8e55e9a8b3635e847d3c48e00fc8bb1bedb7f675d
size 123587
oid sha256:8298b57d94db8a9776db4051364722b82b04421bdfa920bccb0bbb2cb912fd2a
size 133085
+1
View File
@@ -0,0 +1 @@
Fix the background colour of the room members screen in dark mode.
+1
View File
@@ -0,0 +1 @@
Make sure forms have pressed states, remove incorrect disclosure indicators, stop login screen placeholders from flickering and don't block the loging screen when parsing a username.
+1
View File
@@ -0,0 +1 @@
Rename SettingsRow… to Form…Style and use these everywhere (sparingly on the Bug Report Screen which isn't a real form).