Skip to content

Commit

Permalink
Split views into extensions (#321)
Browse files Browse the repository at this point in the history
Nothing but moving code around to reorganize views into the following
sections (MARK):

- Properties/Body
- Subviews
- Actions
  • Loading branch information
keeshux authored Jul 3, 2023
1 parent 7198150 commit d7ebcb2
Show file tree
Hide file tree
Showing 37 changed files with 1,449 additions and 1,240 deletions.
26 changes: 15 additions & 11 deletions Passepartout/App/Views/AboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
import SwiftUI

struct AboutView: View {
// private let appName = Unlocalized.appName

private let versionString = Constants.Global.appVersionString

private let redditURL = Constants.URLs.subreddit
Expand All @@ -52,11 +50,15 @@ struct AboutView: View {
supportSection
webSection
githubSection
}.themeSecondaryView()
.navigationTitle(L10n.About.title)
}.navigationTitle(L10n.About.title)
.themeSecondaryView()
}
}

private var infoSection: some View {
// MARK: -

private extension AboutView {
var infoSection: some View {
Section {
NavigationLink {
VersionView()
Expand All @@ -70,7 +72,7 @@ struct AboutView: View {
}
}

private var supportSection: some View {
var supportSection: some View {
Section {
Button(L10n.About.Items.JoinCommunity.caption) {
URL.open(redditURL)
Expand All @@ -82,7 +84,7 @@ struct AboutView: View {
}
}

private var webSection: some View {
var webSection: some View {
Section {
Button(L10n.About.Items.Website.caption) {
URL.open(homeURL)
Expand All @@ -101,7 +103,7 @@ struct AboutView: View {
}
}

private var githubSection: some View {
var githubSection: some View {
Section {
Button(Unlocalized.About.readme) {
URL.open(readmeURL)
Expand All @@ -115,13 +117,15 @@ struct AboutView: View {
}
}

extension AboutView {
private func shareOnTwitter() {
// MARK: -

private extension AboutView {
func shareOnTwitter() {
let url = Unlocalized.Social.twitterIntent(withMessage: shareMessage)
URL.open(url)
}

private func submitReview() {
func submitReview() {
let reviewURL = Reviewer.urlForReview(withAppId: Constants.App.appStoreId)
URL.open(reviewURL)
}
Expand Down
29 changes: 16 additions & 13 deletions Passepartout/App/Views/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct AccountView: View {

var body: some View {
List {
// TODO: interactive, re-enable after fixing
// TODO: interactive, re-enable after fixing
// Section {
// // TODO: interactive, l10n
// themeTextPicker(L10n.Global.Strings.authentication, selection: $liveAccount.authenticationMethod ?? .persistent, values: [
Expand All @@ -83,7 +83,7 @@ struct AccountView: View {
.withLeadingText(L10n.Account.Items.Password.caption)
}

// TODO: interactive, scan QR code
// TODO: interactive, scan QR code
case .totp:
themeSecureField(L10n.Account.Items.Password.placeholder, text: $liveAccount.password, contentType: .oneTimeCode)
.withLeadingText(L10n.Account.Items.Seed.caption)
Expand All @@ -102,8 +102,7 @@ struct AccountView: View {
}
}
}
}.navigationTitle(L10n.Account.title)
.toolbar {
}.toolbar {
CopySavingButton(
original: $account,
copy: $liveAccount,
Expand All @@ -112,25 +111,21 @@ struct AccountView: View {
saveAnyway: saveAnyway,
onSave: onSave
)
}
}

private func openGuidanceURL(_ url: URL) {
URL.open(url)
}.navigationTitle(L10n.Account.title)
}
}

// MARK: Provider
// MARK: -

extension AccountView {
private var usernamePlaceholder: String? {
private extension AccountView {
var usernamePlaceholder: String? {
guard let name = providerName else {
return nil
}
return providerManager.defaultUsername(name, vpnProtocol: vpnProtocol)
}

private var metadata: ProviderMetadata? {
var metadata: ProviderMetadata? {
guard let name = providerName else {
return nil
}
Expand All @@ -152,3 +147,11 @@ private extension Profile.Account.AuthenticationMethod {
}
}
}

// MARK: -

private extension AccountView {
func openGuidanceURL(_ url: URL) {
URL.open(url)
}
}
207 changes: 108 additions & 99 deletions Passepartout/App/Views/AddHostView+Name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ extension AddHostView {

@State private var isEnteringCredentials = false

private var isComplete: Bool {
!viewModel.processedProfile.isPlaceholder
}

init(
url: URL,
deletingURLOnSuccess: Bool,
Expand Down Expand Up @@ -84,123 +80,136 @@ extension AddHostView {
.navigationTitle(L10n.AddProfile.Shared.title)
.themeSecondaryView()
}
}
}

@ViewBuilder
private var mainView: some View {
AddProfileView.ProfileNameSection(
profileName: $viewModel.profileName,
errorMessage: viewModel.errorMessage
) {
processProfile(replacingExisting: false)
}.onAppear {
viewModel.presetName(withURL: url)
}.disabled(isComplete)
// MARK: -

if !isComplete {
if viewModel.requiresPassphrase {
encryptionSection
}
let headers = profileManager.headers.sorted()
if !headers.isEmpty {
AddProfileView.ExistingProfilesSection(
headers: headers,
profileName: $viewModel.profileName
)
}
} else {
completeSection
}
}
private extension AddHostView.NameView {

private var encryptionSection: some View {
Section {
SecureField(L10n.AddProfile.Host.Sections.Encryption.footer, text: $viewModel.encryptionPassphrase) {
processProfile(replacingExisting: false)
}
} header: {
Text(L10n.Global.Strings.encryption)
@ViewBuilder
var mainView: some View {
AddProfileView.ProfileNameSection(
profileName: $viewModel.profileName,
errorMessage: viewModel.errorMessage
) {
processProfile(replacingExisting: false)
}.onAppear {
viewModel.presetName(withURL: url)
}.disabled(isComplete)

if !isComplete {
if viewModel.requiresPassphrase {
encryptionSection
}
}

private var completeSection: some View {
Section {
Text(Unlocalized.Network.url)
.withTrailingText(url.lastPathComponent)
viewModel.processedProfile.vpnProtocols.first.map {
Text(L10n.Global.Strings.protocol)
.withTrailingText($0.description)
}
} header: {
Text(L10n.AddProfile.Shared.title)
} footer: {
themeErrorMessage(viewModel.errorMessage)
let headers = profileManager.headers.sorted()
if !headers.isEmpty {
AddProfileView.ExistingProfilesSection(
headers: headers,
profileName: $viewModel.profileName
)
}
} else {
completeSection
}
}

private var hiddenAccountLink: some View {
NavigationLink("", isActive: $isEnteringCredentials) {
AddProfileView.AccountWrapperView(
profile: $viewModel.processedProfile,
bindings: bindings
)
var encryptionSection: some View {
Section {
SecureField(L10n.AddProfile.Host.Sections.Encryption.footer, text: $viewModel.encryptionPassphrase) {
processProfile(replacingExisting: false)
}
} header: {
Text(L10n.Global.Strings.encryption)
}
}

private var nextString: String {
if !viewModel.processedProfile.isPlaceholder {
return viewModel.processedProfile.requiresCredentials ? L10n.Global.Strings.next : L10n.Global.Strings.save
} else {
return L10n.Global.Strings.next
var completeSection: some View {
Section {
Text(Unlocalized.Network.url)
.withTrailingText(url.lastPathComponent)
viewModel.processedProfile.vpnProtocols.first.map {
Text(L10n.Global.Strings.protocol)
.withTrailingText($0.description)
}
} header: {
Text(L10n.AddProfile.Shared.title)
} footer: {
themeErrorMessage(viewModel.errorMessage)
}
}

private func requestResourcePermissions() {
_ = url.startAccessingSecurityScopedResource()
var hiddenAccountLink: some View {
NavigationLink("", isActive: $isEnteringCredentials) {
AddProfileView.AccountWrapperView(
profile: $viewModel.processedProfile,
bindings: bindings
)
}
}

private func dropResourcePermissions() {
url.stopAccessingSecurityScopedResource()
var nextString: String {
if !viewModel.processedProfile.isPlaceholder {
return viewModel.processedProfile.requiresCredentials ? L10n.Global.Strings.next : L10n.Global.Strings.save
} else {
return L10n.Global.Strings.next
}
}

@ViewBuilder
private func alertOverwriteActions() -> some View {
Button(role: .destructive) {
processProfile(replacingExisting: true)
} label: {
Text(L10n.Global.Strings.ok)
}
Button(role: .cancel) {
} label: {
Text(L10n.Global.Strings.cancel)
}
@ViewBuilder
func alertOverwriteActions() -> some View {
Button(role: .destructive) {
processProfile(replacingExisting: true)
} label: {
Text(L10n.Global.Strings.ok)
}

private func alertOverwriteMessage() -> some View {
Text(L10n.AddProfile.Shared.Alerts.Overwrite.message)
Button(role: .cancel) {
} label: {
Text(L10n.Global.Strings.cancel)
}
}

private func processProfile(replacingExisting: Bool) {
viewModel.processURL(
url,
with: profileManager,
replacingExisting: replacingExisting,
deletingURLOnSuccess: deletingURLOnSuccess
)
}
func alertOverwriteMessage() -> some View {
Text(L10n.AddProfile.Shared.Alerts.Overwrite.message)
}

private func saveProfile() {
let result = viewModel.addProcessedProfile(to: profileManager)
guard result else {
return
}
var isComplete: Bool {
!viewModel.processedProfile.isPlaceholder
}
}

let profile = viewModel.processedProfile
if profile.requiresCredentials {
isEnteringCredentials = true
} else {
bindings.isPresented = false
profileManager.didCreateProfile.send(profile)
}
// MARK: -

private extension AddHostView.NameView {
func requestResourcePermissions() {
_ = url.startAccessingSecurityScopedResource()
}

func dropResourcePermissions() {
url.stopAccessingSecurityScopedResource()
}

func processProfile(replacingExisting: Bool) {
viewModel.processURL(
url,
with: profileManager,
replacingExisting: replacingExisting,
deletingURLOnSuccess: deletingURLOnSuccess
)
}

func saveProfile() {
let result = viewModel.addProcessedProfile(to: profileManager)
guard result else {
return
}

let profile = viewModel.processedProfile
if profile.requiresCredentials {
isEnteringCredentials = true
} else {
bindings.isPresented = false
profileManager.didCreateProfile.send(profile)
}
}
}
Loading

0 comments on commit d7ebcb2

Please sign in to comment.