Skip to content

Commit

Permalink
Focus some text fields on appearance (#334)
Browse files Browse the repository at this point in the history
Feature from iOS 15, use it on:

- New profile name
- New profile passphrase
- Renamed profile name
- Account username
  • Loading branch information
keeshux authored Jul 23, 2023
1 parent 6ede6f0 commit 34f6738
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 3 deletions.
17 changes: 16 additions & 1 deletion Passepartout/App/Views/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ import PassepartoutLibrary
import SwiftUI

struct AccountView: View {
enum Field {
case username

case password

case seed
}

@ObservedObject private var providerManager: ProviderManager

private let providerName: ProviderName?
Expand All @@ -41,6 +49,8 @@ struct AccountView: View {

@State private var liveAccount = Profile.Account()

@FocusState private var focusedField: Field?

init(
providerName: ProviderName?,
vpnProtocol: VPNProtocolType,
Expand Down Expand Up @@ -71,6 +81,7 @@ struct AccountView: View {
TextField(usernamePlaceholder ?? L10n.Account.Items.Username.placeholder, text: $liveAccount.username)
.textContentType(.username)
.keyboardType(.emailAddress)
.focused($focusedField, equals: .username)
.themeRawTextStyle()
.withLeadingText(L10n.Account.Items.Username.caption)

Expand All @@ -80,12 +91,14 @@ struct AccountView: View {
EmptyView()
} else {
themeSecureField(L10n.Account.Items.Password.placeholder, text: $liveAccount.password)
.focused($focusedField, equals: .password)
.withLeadingText(L10n.Account.Items.Password.caption)
}

// TODO: interactive, scan QR code
case .totp:
// TODO: interactive, scan QR code
themeSecureField(L10n.Account.Items.Password.placeholder, text: $liveAccount.password, contentType: .oneTimeCode)
.focused($focusedField, equals: .seed)
.withLeadingText(L10n.Account.Items.Seed.caption)
}
} footer: {
Expand All @@ -111,6 +124,8 @@ struct AccountView: View {
saveAnyway: saveAnyway,
onSave: onSave
)
}.onAppear {
focusedField = .username
}.navigationTitle(L10n.Account.title)
}
}
Expand Down
11 changes: 9 additions & 2 deletions Passepartout/App/Views/AddHostView+Name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ extension AddHostView {

@State private var isEnteringCredentials = false

@FocusState private var focusedField: AddProfileView.Field?

init(
url: URL,
deletingURLOnSuccess: Bool,
Expand Down Expand Up @@ -75,7 +77,11 @@ extension AddHostView {
isPresented: $viewModel.isAskingOverwrite,
actions: alertOverwriteActions,
message: alertOverwriteMessage
).onAppear(perform: requestResourcePermissions)
).onChange(of: viewModel.requiresPassphrase) {
if $0 {
focusedField = .passphrase
}
}.onAppear(perform: requestResourcePermissions)
.onDisappear(perform: dropResourcePermissions)
.navigationTitle(L10n.AddProfile.Shared.title)
.themeSecondaryView()
Expand All @@ -91,6 +97,7 @@ private extension AddHostView.NameView {
var mainView: some View {
AddProfileView.ProfileNameSection(
profileName: $viewModel.profileName,
focusedField: $focusedField,
errorMessage: viewModel.errorMessage
) {
processProfile(replacingExisting: false)
Expand Down Expand Up @@ -118,7 +125,7 @@ private extension AddHostView.NameView {
Section {
SecureField(L10n.AddProfile.Host.Sections.Encryption.footer, text: $viewModel.encryptionPassphrase) {
processProfile(replacingExisting: false)
}
}.focused($focusedField, equals: .passphrase)
} header: {
Text(L10n.Global.Strings.encryption)
}
Expand Down
11 changes: 11 additions & 0 deletions Passepartout/App/Views/AddProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,36 @@ import PassepartoutLibrary
import SwiftUI

enum AddProfileView {
enum Field {
case name

case passphrase
}

struct Bindings {
@Binding var isPresented: Bool
}

struct ProfileNameSection: View {
@Binding var profileName: String

@FocusState.Binding var focusedField: Field?

let errorMessage: String?

let onCommit: () -> Void

var body: some View {
Section {
TextField(L10n.Global.Placeholders.profileName, text: $profileName, onCommit: onCommit)
.focused($focusedField, equals: .name)
.themeValidProfileName()
} header: {
Text(L10n.Global.Strings.name)
} footer: {
themeErrorMessage(errorMessage)
}.onAppear {
focusedField = .name
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Passepartout/App/Views/AddProviderView+Name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ extension AddProviderView {

@State private var isEnteringCredentials = false

@FocusState private var focusedField: AddProfileView.Field?

init(
profile: Binding<Profile>,
providerMetadata: ProviderMetadata,
Expand All @@ -57,6 +59,7 @@ extension AddProviderView {
List {
AddProfileView.ProfileNameSection(
profileName: $viewModel.profileName,
focusedField: $focusedField,
errorMessage: viewModel.errorMessage
) {
saveProfile(replacingExisting: false)
Expand Down
8 changes: 8 additions & 0 deletions Passepartout/App/Views/ProfileView+Rename.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import SwiftUI

extension ProfileView {
struct RenameView: View {
enum Field {
case name
}

@Environment(\.presentationMode) private var presentationMode

@ObservedObject private var profileManager: ProfileManager
Expand All @@ -38,6 +42,8 @@ extension ProfileView {

@State private var isOverwritingExistingProfile = false

@FocusState private var focusedField: Field?

init(currentProfile: ObservableProfile) {
profileManager = .shared
self.currentProfile = currentProfile
Expand All @@ -48,6 +54,7 @@ extension ProfileView {
Section {
TextField(L10n.Global.Placeholders.profileName, text: $newName, onCommit: commitRenaming)
.themeValidProfileName()
.focused($focusedField, equals: .name)
.onAppear(perform: loadCurrentName)
} header: {
Text(L10n.Profile.Alerts.Rename.title)
Expand Down Expand Up @@ -96,6 +103,7 @@ private extension ProfileView.RenameView {
private extension ProfileView.RenameView {
func loadCurrentName() {
newName = currentProfile.value.header.name
focusedField = .name
}

func commitRenaming() {
Expand Down

0 comments on commit 34f6738

Please sign in to comment.