From b9a3face34b252c0efff241eb78bdcda54c5616f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Tr=C3=A9guier?= Date: Tue, 17 Sep 2024 16:10:26 +0200 Subject: [PATCH] Switch to Apple's swift-format tool --- .swift-format | 5 + Fyreplace.xcodeproj/project.pbxproj | 2 + Fyreplace/Config/Config.swift | 12 +- Fyreplace/Data/Keychain.swift | 7 +- Fyreplace/Data/Tokens.swift | 2 +- .../Extensions/Array+RawRepresentable.swift | 4 +- Fyreplace/Fakes/FakeClient.swift | 212 +++++++++++++----- Fyreplace/FyreplaceApp.swift | 5 +- Fyreplace/Views/Forms/EnvironmentPicker.swift | 3 +- Fyreplace/Views/Forms/LogoHeader.swift | 20 +- Fyreplace/Views/Forms/SubmitButton.swift | 16 +- Fyreplace/Views/MainView.swift | 9 +- .../Views/Navigation/CompactNavigation.swift | 8 +- Fyreplace/Views/Navigation/Destination.swift | 10 +- .../Views/Navigation/DynamicNavigation.swift | 3 +- .../Views/Navigation/RegularNavigation.swift | 2 +- Fyreplace/Views/Screens/LoginScreen.swift | 38 ++-- .../Views/Screens/LoginScreenProtocol.swift | 6 +- .../Views/Screens/MultiChoiceScreen.swift | 2 +- Fyreplace/Views/Screens/RegisterScreen.swift | 60 +++-- .../Screens/RegisterScreenProtocol.swift | 15 +- FyreplaceTests/Screens/LoginScreenTests.swift | 7 +- .../Screens/RegisterScreenTests.swift | 11 +- FyreplaceTests/StoringEventBus.swift | 3 +- 24 files changed, 301 insertions(+), 161 deletions(-) create mode 100644 .swift-format diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..95d360e --- /dev/null +++ b/.swift-format @@ -0,0 +1,5 @@ +{ + "indentation": { + "spaces": 4 + } +} diff --git a/Fyreplace.xcodeproj/project.pbxproj b/Fyreplace.xcodeproj/project.pbxproj index e628da3..3714a48 100644 --- a/Fyreplace.xcodeproj/project.pbxproj +++ b/Fyreplace.xcodeproj/project.pbxproj @@ -167,6 +167,7 @@ 4DE785842C88EF8C000EC4E5 /* RequestIdMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestIdMiddleware.swift; sourceTree = ""; }; 4DE785872C88F392000EC4E5 /* HTTPField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPField.swift; sourceTree = ""; }; 4DE785942C8B17AE000EC4E5 /* SubmitOrCancel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubmitOrCancel.swift; sourceTree = ""; }; + 4DF3737F2C99C23D0008AB04 /* .swift-format */ = {isa = PBXFileReference; explicitFileType = text.json; path = ".swift-format"; sourceTree = ""; }; 4DFB906F2C5908DE00D4DABF /* LoginScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenTests.swift; sourceTree = ""; }; 4DFB90752C59173C00D4DABF /* RegisterScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterScreenTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -273,6 +274,7 @@ 4D54C93C2BF26090001DE071 /* FyreplaceTests */, 4D54C9462BF26090001DE071 /* FyreplaceUITests */, 4D54C9632BF28695001DE071 /* .gitignore */, + 4DF3737F2C99C23D0008AB04 /* .swift-format */, 4D0DDC292C18A467006CD503 /* .xcode-version */, 4D6641DB2C5B963500BE3D07 /* .ios-test-version */, 4DCEF8652C452EBA00F53085 /* .env-example */, diff --git a/Fyreplace/Config/Config.swift b/Fyreplace/Config/Config.swift index 021ae69..df1ee7f 100644 --- a/Fyreplace/Config/Config.swift +++ b/Fyreplace/Config/Config.swift @@ -93,7 +93,9 @@ struct Config { return Client( serverURL: url(for: environment), configuration: .init(dateTranscoder: .iso8601WithFractionalSeconds), - transport: URLSessionTransport(configuration: .init(session: .init(configuration: configuration))), + transport: URLSessionTransport( + configuration: .init(session: .init(configuration: configuration)) + ), middlewares: [RequestIdMiddleware(), AuthenticationMiddleware()] ) } @@ -109,17 +111,17 @@ struct Config { } } -private extension [String: Any] { - func string(_ key: String) -> String? { +extension [String: Any] { + fileprivate func string(_ key: String) -> String? { return self[key] as? String } - func url(_ key: String) -> URL? { + fileprivate func url(_ key: String) -> URL? { guard let s = string(key) else { return nil } return .init(string: s) } - func dictionary(_ key: String) -> [String: Any]? { + fileprivate func dictionary(_ key: String) -> [String: Any]? { return self[key] as? [String: Any] } } diff --git a/Fyreplace/Data/Keychain.swift b/Fyreplace/Data/Keychain.swift index e892ae7..c567230 100644 --- a/Fyreplace/Data/Keychain.swift +++ b/Fyreplace/Data/Keychain.swift @@ -23,8 +23,8 @@ struct Keychain { info[kSecReturnData] = true var itemReference: CFTypeRef? guard SecItemCopyMatching(info as CFDictionary, &itemReference) == errSecSuccess, - let data = itemReference as? Data, - let value = String(data: data, encoding: .utf8) + let data = itemReference as? Data, + let value = String(data: data, encoding: .utf8) else { return "" } return value } @@ -33,8 +33,9 @@ struct Keychain { func set(_ value: String) -> Bool { guard !value.isEmpty else { return delete() } let data = value.data(using: .utf8) + let attributes = [kSecValueData: data] as CFDictionary - if SecItemUpdate(query as CFDictionary, [kSecValueData: data] as CFDictionary) == errSecSuccess { + if SecItemUpdate(query as CFDictionary, attributes) == errSecSuccess { return true } else { var info = query diff --git a/Fyreplace/Data/Tokens.swift b/Fyreplace/Data/Tokens.swift index 2d43203..e4ee44c 100644 --- a/Fyreplace/Data/Tokens.swift +++ b/Fyreplace/Data/Tokens.swift @@ -31,7 +31,7 @@ func scheduleTokenRefresh() { func refreshToken(using api: APIProtocol) async -> String? { guard let response = try? await api.getNewToken().ok, - let newToken = try? await String(collecting: response.body.plainText, upTo: 1024) + let newToken = try? await String(collecting: response.body.plainText, upTo: 1024) else { return nil } return newToken } diff --git a/Fyreplace/Extensions/Array+RawRepresentable.swift b/Fyreplace/Extensions/Array+RawRepresentable.swift index fa9c538..734d3de 100644 --- a/Fyreplace/Extensions/Array+RawRepresentable.swift +++ b/Fyreplace/Extensions/Array+RawRepresentable.swift @@ -4,14 +4,14 @@ extension Array: RawRepresentable where Element: Codable { public init?(rawValue: String) { self.init() guard let data = rawValue.data(using: .utf8), - let result = try? JSONDecoder().decode([Element].self, from: data) + let result = try? JSONDecoder().decode([Element].self, from: data) else { return nil } result.forEach { append($0) } } public var rawValue: String { guard let data = try? JSONEncoder().encode(self), - let value = String(data: data, encoding: .utf8) + let value = String(data: data, encoding: .utf8) else { return "[]" } return value } diff --git a/Fyreplace/Fakes/FakeClient.swift b/Fyreplace/Fakes/FakeClient.swift index 4f42d7e..483e6e0 100644 --- a/Fyreplace/Fakes/FakeClient.swift +++ b/Fyreplace/Fakes/FakeClient.swift @@ -9,23 +9,33 @@ extension APIProtocol { // MARK: Chapters extension FakeClient { - func createChapter(_: Operations.createChapter.Input) async throws -> Operations.createChapter.Output { + func createChapter(_: Operations.createChapter.Input) async throws + -> Operations.createChapter.Output + { fatalError("Not implemented") } - func deleteChapter(_: Operations.deleteChapter.Input) async throws -> Operations.deleteChapter.Output { + func deleteChapter(_: Operations.deleteChapter.Input) async throws + -> Operations.deleteChapter.Output + { fatalError("Not implemented") } - func setChapterImage(_: Operations.setChapterImage.Input) async throws -> Operations.setChapterImage.Output { + func setChapterImage(_: Operations.setChapterImage.Input) async throws + -> Operations.setChapterImage.Output + { fatalError("Not implemented") } - func setChapterPosition(_: Operations.setChapterPosition.Input) async throws -> Operations.setChapterPosition.Output { + func setChapterPosition(_: Operations.setChapterPosition.Input) async throws + -> Operations.setChapterPosition.Output + { fatalError("Not implemented") } - func setChapterText(_: Operations.setChapterText.Input) async throws -> Operations.setChapterText.Output { + func setChapterText(_: Operations.setChapterText.Input) async throws + -> Operations.setChapterText.Output + { fatalError("Not implemented") } } @@ -33,27 +43,39 @@ extension FakeClient { // MARK: Comments extension FakeClient { - func acknowledgeComment(_: Operations.acknowledgeComment.Input) async throws -> Operations.acknowledgeComment.Output { + func acknowledgeComment(_: Operations.acknowledgeComment.Input) async throws + -> Operations.acknowledgeComment.Output + { fatalError("Not implemented") } - func countComments(_: Operations.countComments.Input) async throws -> Operations.countComments.Output { + func countComments(_: Operations.countComments.Input) async throws + -> Operations.countComments.Output + { fatalError("Not implemented") } - func createComment(_: Operations.createComment.Input) async throws -> Operations.createComment.Output { + func createComment(_: Operations.createComment.Input) async throws + -> Operations.createComment.Output + { fatalError("Not implemented") } - func deleteComment(_: Operations.deleteComment.Input) async throws -> Operations.deleteComment.Output { + func deleteComment(_: Operations.deleteComment.Input) async throws + -> Operations.deleteComment.Output + { fatalError("Not implemented") } - func listComments(_: Operations.listComments.Input) async throws -> Operations.listComments.Output { + func listComments(_: Operations.listComments.Input) async throws + -> Operations.listComments.Output + { fatalError("Not implemented") } - func setCommentReported(_: Operations.setCommentReported.Input) async throws -> Operations.setCommentReported.Output { + func setCommentReported(_: Operations.setCommentReported.Input) async throws + -> Operations.setCommentReported.Output + { fatalError("Not implemented") } } @@ -61,27 +83,39 @@ extension FakeClient { // MARK: Emails extension FakeClient { - func activateEmail(_: Operations.activateEmail.Input) async throws -> Operations.activateEmail.Output { + func activateEmail(_: Operations.activateEmail.Input) async throws + -> Operations.activateEmail.Output + { fatalError("Not implemented") } - func countEmails(_: Operations.countEmails.Input) async throws -> Operations.countEmails.Output { + func countEmails(_: Operations.countEmails.Input) async throws + -> Operations.countEmails.Output + { fatalError("Not implemented") } - func createEmail(_: Operations.createEmail.Input) async throws -> Operations.createEmail.Output { + func createEmail(_: Operations.createEmail.Input) async throws + -> Operations.createEmail.Output + { fatalError("Not implemented") } - func deleteEmail(_: Operations.deleteEmail.Input) async throws -> Operations.deleteEmail.Output { + func deleteEmail(_: Operations.deleteEmail.Input) async throws + -> Operations.deleteEmail.Output + { fatalError("Not implemented") } - func listEmails(_: Operations.listEmails.Input) async throws -> Operations.listEmails.Output { + func listEmails(_: Operations.listEmails.Input) async throws + -> Operations.listEmails.Output + { fatalError("Not implemented") } - func setMainEmail(_: Operations.setMainEmail.Input) async throws -> Operations.setMainEmail.Output { + func setMainEmail(_: Operations.setMainEmail.Input) async throws + -> Operations.setMainEmail.Output + { fatalError("Not implemented") } } @@ -89,43 +123,63 @@ extension FakeClient { // MARK: Posts extension FakeClient { - func countPosts(_: Operations.countPosts.Input) async throws -> Operations.countPosts.Output { + func countPosts(_: Operations.countPosts.Input) async throws + -> Operations.countPosts.Output + { fatalError("Not implemented") } - func createPost(_: Operations.createPost.Input) async throws -> Operations.createPost.Output { + func createPost(_: Operations.createPost.Input) async throws + -> Operations.createPost.Output + { fatalError("Not implemented") } - func deletePost(_: Operations.deletePost.Input) async throws -> Operations.deletePost.Output { + func deletePost(_: Operations.deletePost.Input) async throws + -> Operations.deletePost.Output + { fatalError("Not implemented") } - func getPost(_: Operations.getPost.Input) async throws -> Operations.getPost.Output { + func getPost(_: Operations.getPost.Input) async throws + -> Operations.getPost.Output + { fatalError("Not implemented") } - func listPosts(_: Operations.listPosts.Input) async throws -> Operations.listPosts.Output { + func listPosts(_: Operations.listPosts.Input) async throws + -> Operations.listPosts.Output + { fatalError("Not implemented") } - func listPostsFeed(_: Operations.listPostsFeed.Input) async throws -> Operations.listPostsFeed.Output { + func listPostsFeed(_: Operations.listPostsFeed.Input) async throws + -> Operations.listPostsFeed.Output + { fatalError("Not implemented") } - func publishPost(_: Operations.publishPost.Input) async throws -> Operations.publishPost.Output { + func publishPost(_: Operations.publishPost.Input) async throws + -> Operations.publishPost.Output + { fatalError("Not implemented") } - func setPostReported(_: Operations.setPostReported.Input) async throws -> Operations.setPostReported.Output { + func setPostReported(_: Operations.setPostReported.Input) async throws + -> Operations.setPostReported.Output + { fatalError("Not implemented") } - func setPostSubscribed(_: Operations.setPostSubscribed.Input) async throws -> Operations.setPostSubscribed.Output { + func setPostSubscribed(_: Operations.setPostSubscribed.Input) async throws + -> Operations.setPostSubscribed.Output + { fatalError("Not implemented") } - func votePost(_: Operations.votePost.Input) async throws -> Operations.votePost.Output { + func votePost(_: Operations.votePost.Input) async throws + -> Operations.votePost.Output + { fatalError("Not implemented") } } @@ -133,7 +187,9 @@ extension FakeClient { // MARK: Reports extension FakeClient { - func listReports(_: Operations.listReports.Input) async throws -> Operations.listReports.Output { + func listReports(_: Operations.listReports.Input) async throws + -> Operations.listReports.Output + { fatalError("Not implemented") } } @@ -141,15 +197,21 @@ extension FakeClient { // MARK: Subscriptions extension FakeClient { - func clearUnreadSubscriptions(_: Operations.clearUnreadSubscriptions.Input) async throws -> Operations.clearUnreadSubscriptions.Output { + func clearUnreadSubscriptions(_: Operations.clearUnreadSubscriptions.Input) async throws + -> Operations.clearUnreadSubscriptions.Output + { fatalError("Not implemented") } - func deleteSubscription(_: Operations.deleteSubscription.Input) async throws -> Operations.deleteSubscription.Output { + func deleteSubscription(_: Operations.deleteSubscription.Input) async throws + -> Operations.deleteSubscription.Output + { fatalError("Not implemented") } - func listUnreadSubscriptions(_: Operations.listUnreadSubscriptions.Input) async throws -> Operations.listUnreadSubscriptions.Output { + func listUnreadSubscriptions(_: Operations.listUnreadSubscriptions.Input) async throws + -> Operations.listUnreadSubscriptions.Output + { fatalError("Not implemented") } } @@ -164,7 +226,9 @@ extension FakeClient { static let badToken = "bad-token" static let goodToken = "good-token" - func createNewToken(_ input: Operations.createNewToken.Input) async throws -> Operations.createNewToken.Output { + func createNewToken(_ input: Operations.createNewToken.Input) async throws + -> Operations.createNewToken.Output + { return switch input.body { case let .json(json) where json.identifier == Self.goodIdentifer: .ok(.init()) @@ -174,9 +238,12 @@ extension FakeClient { } } - func createToken(_ input: Operations.createToken.Input) async throws -> Operations.createToken.Output { + func createToken(_ input: Operations.createToken.Input) async throws + -> Operations.createToken.Output + { return switch input.body { - case let .json(json) where json.identifier == Self.goodIdentifer && json.secret == Self.goodSecret: + case let .json(json) + where json.identifier == Self.goodIdentifer && json.secret == Self.goodSecret: .created(.init(body: .plainText(.init(stringLiteral: Self.goodToken)))) case .json: @@ -184,7 +251,9 @@ extension FakeClient { } } - func getNewToken(_: Operations.getNewToken.Input) async throws -> Operations.getNewToken.Output { + func getNewToken(_: Operations.getNewToken.Input) async throws + -> Operations.getNewToken.Output + { return .ok(.init(body: .plainText(.init(stringLiteral: Self.goodToken)))) } } @@ -200,11 +269,15 @@ extension FakeClient { static let usedEmail = "used-email" static let goodEmail = "good-email" - func countBlockedUsers(_: Operations.countBlockedUsers.Input) async throws -> Operations.countBlockedUsers.Output { + func countBlockedUsers(_: Operations.countBlockedUsers.Input) async throws + -> Operations.countBlockedUsers.Output + { fatalError("Not implemented") } - func createUser(_ input: Operations.createUser.Input) async throws -> Operations.createUser.Output { + func createUser(_ input: Operations.createUser.Input) async throws + -> Operations.createUser.Output + { return switch input.body { case let .json(json) where json.username == Self.badUsername: .badRequest(.init(body: .json(.init()))) @@ -222,57 +295,80 @@ extension FakeClient { .conflict(.init(body: .json(.init()))) case let .json(json): - .created(.init(body: .json(.init( - id: .randomUuid, - dateCreated: .now, - username: json.username, - rank: .CITIZEN, - avatar: "", - bio: "", - banned: false, - blocked: false, - tint: .init(r: 0x7F, g: 0x7F, b: 0x7F) - )))) + .created( + .init( + body: .json( + .init( + id: .randomUuid, + dateCreated: .now, + username: json.username, + rank: .CITIZEN, + avatar: "", + bio: "", + banned: false, + blocked: false, + tint: .init(r: 0x7F, g: 0x7F, b: 0x7F) + )))) } } - func deleteCurrentUser(_: Operations.deleteCurrentUser.Input) async throws -> Operations.deleteCurrentUser.Output { + func deleteCurrentUser(_: Operations.deleteCurrentUser.Input) async throws + -> Operations.deleteCurrentUser.Output + { fatalError("Not implemented") } - func deleteCurrentUserAvatar(_: Operations.deleteCurrentUserAvatar.Input) async throws -> Operations.deleteCurrentUserAvatar.Output { + func deleteCurrentUserAvatar(_: Operations.deleteCurrentUserAvatar.Input) async throws + -> Operations.deleteCurrentUserAvatar.Output + { fatalError("Not implemented") } - func getCurrentUser(_: Operations.getCurrentUser.Input) async throws -> Operations.getCurrentUser.Output { + func getCurrentUser(_: Operations.getCurrentUser.Input) async throws + -> Operations.getCurrentUser.Output + { fatalError("Not implemented") } - func getUser(_: Operations.getUser.Input) async throws -> Operations.getUser.Output { + func getUser(_: Operations.getUser.Input) async throws + -> Operations.getUser.Output + { fatalError("Not implemented") } - func listBlockedUsers(_: Operations.listBlockedUsers.Input) async throws -> Operations.listBlockedUsers.Output { + func listBlockedUsers(_: Operations.listBlockedUsers.Input) async throws + -> Operations.listBlockedUsers.Output + { fatalError("Not implemented") } - func setCurrentUserAvatar(_: Operations.setCurrentUserAvatar.Input) async throws -> Operations.setCurrentUserAvatar.Output { + func setCurrentUserAvatar(_: Operations.setCurrentUserAvatar.Input) async throws + -> Operations.setCurrentUserAvatar.Output + { fatalError("Not implemented") } - func setCurrentUserBio(_: Operations.setCurrentUserBio.Input) async throws -> Operations.setCurrentUserBio.Output { + func setCurrentUserBio(_: Operations.setCurrentUserBio.Input) async throws + -> Operations.setCurrentUserBio.Output + { fatalError("Not implemented") } - func setUserBanned(_: Operations.setUserBanned.Input) async throws -> Operations.setUserBanned.Output { + func setUserBanned(_: Operations.setUserBanned.Input) async throws + -> Operations.setUserBanned.Output + { fatalError("Not implemented") } - func setUserBlocked(_: Operations.setUserBlocked.Input) async throws -> Operations.setUserBlocked.Output { + func setUserBlocked(_: Operations.setUserBlocked.Input) async throws + -> Operations.setUserBlocked.Output + { fatalError("Not implemented") } - func setUserReported(_: Operations.setUserReported.Input) async throws -> Operations.setUserReported.Output { + func setUserReported(_: Operations.setUserReported.Input) async throws + -> Operations.setUserReported.Output + { fatalError("Not implemented") } } diff --git a/Fyreplace/FyreplaceApp.swift b/Fyreplace/FyreplaceApp.swift index 871fd26..b9f8adc 100644 --- a/Fyreplace/FyreplaceApp.swift +++ b/Fyreplace/FyreplaceApp.swift @@ -33,7 +33,10 @@ struct FyreplaceApp: App { HelpCommands() } #if !os(macOS) - .backgroundTask(.appRefresh("app.fyreplace.Fyreplace.tokenRefresh"), action: tokenRefreshBackgroundTask) + .backgroundTask( + .appRefresh("app.fyreplace.Fyreplace.tokenRefresh"), + action: tokenRefreshBackgroundTask + ) #endif } } diff --git a/Fyreplace/Views/Forms/EnvironmentPicker.swift b/Fyreplace/Views/Forms/EnvironmentPicker.swift index 5f259ae..a759dc8 100644 --- a/Fyreplace/Views/Forms/EnvironmentPicker.swift +++ b/Fyreplace/Views/Forms/EnvironmentPicker.swift @@ -9,7 +9,8 @@ struct EnvironmentPicker: View { var body: some View { Picker("Environment.Title", selection: $selectedEnvironment) { ForEach(ServerEnvironment.allCases) { environment in - let suffix = environment == .default + let suffix = + environment == .default ? " " + .init(localized: "Environment.Default") : "" diff --git a/Fyreplace/Views/Forms/LogoHeader.swift b/Fyreplace/Views/Forms/LogoHeader.swift index f6c4bbb..6f82682 100644 --- a/Fyreplace/Views/Forms/LogoHeader.swift +++ b/Fyreplace/Views/Forms/LogoHeader.swift @@ -10,17 +10,17 @@ struct LogoHeader: View { Spacer() Image("Logo", label: Text("Logo")) .resizable() - #if os(macOS) - .frame(width: 50, height: 50) - #else - .frame(width: 80, height: 80) - #endif + #if os(macOS) + .frame(width: 50, height: 50) + #else + .frame(width: 80, height: 80) + #endif Spacer() } #if os(macOS) - .padding(.bottom) + .padding(.bottom) #else - .padding(.vertical, 40) + .padding(.vertical, 40) #endif HStack { @@ -29,9 +29,9 @@ struct LogoHeader: View { #endif Text(text) .fixedSize(horizontal: false, vertical: true) - #if os(macOS) - .font(.headline) - #endif + #if os(macOS) + .font(.headline) + #endif Spacer() } } diff --git a/Fyreplace/Views/Forms/SubmitButton.swift b/Fyreplace/Views/Forms/SubmitButton.swift index 6a856ed..2a4e5b3 100644 --- a/Fyreplace/Views/Forms/SubmitButton.swift +++ b/Fyreplace/Views/Forms/SubmitButton.swift @@ -14,15 +14,15 @@ struct SubmitButton: View { var body: some View { let button = Button(action: action) { Text(text) - #if os(macOS) - .padding(.horizontal) - .opacity(isLoading ? 0 : 1) - .overlay { - if isLoading { - ProgressView().controlSize(.small) + #if os(macOS) + .padding(.horizontal) + .opacity(isLoading ? 0 : 1) + .overlay { + if isLoading { + ProgressView().controlSize(.small) + } } - } - #endif + #endif } .animation(.default, value: isEnabled) .matchedGeometryEffect(id: "button", in: namespace) diff --git a/Fyreplace/Views/MainView.swift b/Fyreplace/Views/MainView.swift index d0e2ebb..92e5096 100644 --- a/Fyreplace/Views/MainView.swift +++ b/Fyreplace/Views/MainView.swift @@ -50,15 +50,16 @@ struct MainView: View, MainViewProtocol { await removeFailure() } } - }, message: { (failure: FailureEvent) in + }, + message: { (failure: FailureEvent) in Text(failure.text) } ) .onReceive(eventBus.events.compactMap { ($0 as? ErrorEvent)?.error }, perform: addError) .onReceive(eventBus.events.compactMap { ($0 as? FailureEvent) }, perform: addFailure) - #if os(macOS) - .task { await keepRefreshingToken() } - #endif + #if os(macOS) + .task { await keepRefreshingToken() } + #endif } private func keepRefreshingToken() async { diff --git a/Fyreplace/Views/Navigation/CompactNavigation.swift b/Fyreplace/Views/Navigation/CompactNavigation.swift index 0493bb5..a3eaea3 100644 --- a/Fyreplace/Views/Navigation/CompactNavigation.swift +++ b/Fyreplace/Views/Navigation/CompactNavigation.swift @@ -15,9 +15,13 @@ struct CompactNavigation: View, NavigationProtocol { var body: some View { TabView(selection: $selectedDestination) { - ForEach(Array(Destination.essentials.enumerated()), id: \.element.id) { i, destination in + ForEach(Array(Destination.essentials.enumerated()), id: \.element.id) { + i, destination in NavigationStack { - let content = CompactNavigationDestination(destination: destination, multiScreenChoice: $selectedChoices[i]) + let content = CompactNavigationDestination( + destination: destination, + multiScreenChoice: $selectedChoices[i] + ) if destination.canOfferAuthentication { AuthenticatingScreen(isRegistering: isRegistering) { content } diff --git a/Fyreplace/Views/Navigation/Destination.swift b/Fyreplace/Views/Navigation/Destination.swift index 00faae2..3953329 100644 --- a/Fyreplace/Views/Navigation/Destination.swift +++ b/Fyreplace/Views/Navigation/Destination.swift @@ -68,8 +68,8 @@ public enum Destination: String, Codable, Identifiable { var canOfferAuthentication: Bool { switch self { case .feed, - .login, - .register: + .login, + .register: false default: true @@ -79,9 +79,9 @@ public enum Destination: String, Codable, Identifiable { var requiresAuthentication: Bool { switch self { case .feed, - .settings, - .login, - .register: + .settings, + .login, + .register: false default: true diff --git a/Fyreplace/Views/Navigation/DynamicNavigation.swift b/Fyreplace/Views/Navigation/DynamicNavigation.swift index ec5c134..77748ee 100644 --- a/Fyreplace/Views/Navigation/DynamicNavigation.swift +++ b/Fyreplace/Views/Navigation/DynamicNavigation.swift @@ -9,7 +9,8 @@ struct DynamicNavigation: View { var body: some View { GeometryReader { geometry in - let largeEnough = geometry.size.width > geometry.size.height + let largeEnough = + geometry.size.width > geometry.size.height && horizontalSizeClass != .compact && verticalSizeClass != .compact diff --git a/Fyreplace/Views/Navigation/RegularNavigation.swift b/Fyreplace/Views/Navigation/RegularNavigation.swift index 0a18359..46d88f7 100644 --- a/Fyreplace/Views/Navigation/RegularNavigation.swift +++ b/Fyreplace/Views/Navigation/RegularNavigation.swift @@ -38,7 +38,7 @@ struct RegularNavigation: View, NavigationProtocol { .disabled(destination.requiresAuthentication && token.isEmpty) } #if os(macOS) - .navigationSplitViewColumnWidth(min: 180, ideal: 180) + .navigationSplitViewColumnWidth(min: 180, ideal: 180) #endif } detail: { NavigationStack { diff --git a/Fyreplace/Views/Screens/LoginScreen.swift b/Fyreplace/Views/Screens/LoginScreen.swift index 306b66a..3b21795 100644 --- a/Fyreplace/Views/Screens/LoginScreen.swift +++ b/Fyreplace/Views/Screens/LoginScreen.swift @@ -41,27 +41,35 @@ struct LoginScreen: View, LoginScreenProtocol { ) { EnvironmentPicker(namespace: namespace).disabled(isWaitingForRandomCode) - TextField("Login.Identifier", text: $identifier, prompt: Text("Login.Identifier.Prompt")) - .autocorrectionDisabled() - .focused($focused, equals: .identifier) - .disabled(isWaitingForRandomCode) - .onSubmit(submit) - .matchedGeometryEffect(id: "first-field", in: namespace) + TextField( + "Login.Identifier", + text: $identifier, + prompt: Text("Login.Identifier.Prompt") + ) + .autocorrectionDisabled() + .focused($focused, equals: .identifier) + .disabled(isWaitingForRandomCode) + .onSubmit(submit) + .matchedGeometryEffect(id: "first-field", in: namespace) #if !os(macOS) .keyboardType(.asciiCapable) #endif if isWaitingForRandomCode { - TextField("Account.RandomCode", text: $randomCode, prompt: Text("Account.RandomCode.Prompt")) - .textContentType(.oneTimeCode) - .autocorrectionDisabled() - .focused($focused, equals: .randomCode) - .onSubmit(submit) - .onAppear { - if randomCode.isEmpty { - focused = .randomCode - } + TextField( + "Account.RandomCode", + text: $randomCode, + prompt: Text("Account.RandomCode.Prompt") + ) + .textContentType(.oneTimeCode) + .autocorrectionDisabled() + .focused($focused, equals: .randomCode) + .onSubmit(submit) + .onAppear { + if randomCode.isEmpty { + focused = .randomCode } + } #if !os(macOS) .keyboardType(.asciiCapable) #endif diff --git a/Fyreplace/Views/Screens/LoginScreenProtocol.swift b/Fyreplace/Views/Screens/LoginScreenProtocol.swift index 1529ea4..a66615a 100644 --- a/Fyreplace/Views/Screens/LoginScreenProtocol.swift +++ b/Fyreplace/Views/Screens/LoginScreenProtocol.swift @@ -10,7 +10,7 @@ protocol LoginScreenProtocol: LoadingViewProtocol { @MainActor extension LoginScreenProtocol { var canSubmit: Bool { - !isLoading && (isWaitingForRandomCode ? randomCode.count >= 8 : 3 ... 254 ~= identifier.count) + !isLoading && (isWaitingForRandomCode ? randomCode.count >= 8 : 3...254 ~= identifier.count) } func submit() async { @@ -56,7 +56,9 @@ extension LoginScreenProtocol { } func createToken() async throws -> UnfortunateEvent? { - let response = try await api.createToken(body: .json(.init(identifier: identifier, secret: randomCode))) + let response = try await api.createToken( + body: .json(.init(identifier: identifier, secret: randomCode)) + ) switch response { case let .created(created): diff --git a/Fyreplace/Views/Screens/MultiChoiceScreen.swift b/Fyreplace/Views/Screens/MultiChoiceScreen.swift index 3778421..3c4f185 100644 --- a/Fyreplace/Views/Screens/MultiChoiceScreen.swift +++ b/Fyreplace/Views/Screens/MultiChoiceScreen.swift @@ -25,7 +25,7 @@ struct MultiChoiceScreen: View { } } #if !os(macOS) - .navigationBarTitleDisplayMode(.inline) + .navigationBarTitleDisplayMode(.inline) #endif } } diff --git a/Fyreplace/Views/Screens/RegisterScreen.swift b/Fyreplace/Views/Screens/RegisterScreen.swift index 8081f4e..8f2ea91 100644 --- a/Fyreplace/Views/Screens/RegisterScreen.swift +++ b/Fyreplace/Views/Screens/RegisterScreen.swift @@ -59,41 +59,53 @@ struct RegisterScreen: View, RegisterScreenProtocol { ) { EnvironmentPicker(namespace: namespace).disabled(isWaitingForRandomCode) - TextField("Register.Username", text: $username, prompt: usernamePrompt) - .textContentType(.username) - .autocorrectionDisabled() - .focused($focused, equals: .username) - .disabled(isWaitingForRandomCode) - .submitLabel(.next) - .onSubmit { focused = .email } - .matchedGeometryEffect(id: "first-field", in: namespace) + TextField( + "Register.Username", + text: $username, + prompt: usernamePrompt + ) + .textContentType(.username) + .autocorrectionDisabled() + .focused($focused, equals: .username) + .disabled(isWaitingForRandomCode) + .submitLabel(.next) + .onSubmit { focused = .email } + .matchedGeometryEffect(id: "first-field", in: namespace) #if !os(macOS) .keyboardType(.asciiCapable) #endif - TextField("Register.Email", text: $email, prompt: emailPrompt) - .textContentType(.email) - .autocorrectionDisabled() - .focused($focused, equals: .email) - .disabled(isWaitingForRandomCode) - .submitLabel(.done) - .onSubmit(submit) + TextField( + "Register.Email", + text: $email, + prompt: emailPrompt + ) + .textContentType(.email) + .autocorrectionDisabled() + .focused($focused, equals: .email) + .disabled(isWaitingForRandomCode) + .submitLabel(.done) + .onSubmit(submit) #if !os(macOS) .textInputAutocapitalization(.never) .keyboardType(.emailAddress) #endif if isWaitingForRandomCode { - TextField("Account.RandomCode", text: $randomCode, prompt: Text("Account.RandomCode.Prompt")) - .textContentType(.oneTimeCode) - .autocorrectionDisabled() - .focused($focused, equals: .randomCode) - .onSubmit(submit) - .onAppear { - if randomCode.isEmpty { - focused = .randomCode - } + TextField( + "Account.RandomCode", + text: $randomCode, + prompt: Text("Account.RandomCode.Prompt") + ) + .textContentType(.oneTimeCode) + .autocorrectionDisabled() + .focused($focused, equals: .randomCode) + .onSubmit(submit) + .onAppear { + if randomCode.isEmpty { + focused = .randomCode } + } #if !os(macOS) .keyboardType(.asciiCapable) #endif diff --git a/Fyreplace/Views/Screens/RegisterScreenProtocol.swift b/Fyreplace/Views/Screens/RegisterScreenProtocol.swift index 562853e..cad3535 100644 --- a/Fyreplace/Views/Screens/RegisterScreenProtocol.swift +++ b/Fyreplace/Views/Screens/RegisterScreenProtocol.swift @@ -11,10 +11,11 @@ protocol RegisterScreenProtocol: LoadingViewProtocol { @MainActor extension RegisterScreenProtocol { - var isUsernameValid: Bool { 3 ... 50 ~= username.count } - var isEmailValid: Bool { 3 ... 254 ~= email.count && email.contains("@") } + var isUsernameValid: Bool { 3...50 ~= username.count } + var isEmailValid: Bool { 3...254 ~= email.count && email.contains("@") } var canSubmit: Bool { - !isLoading && (isWaitingForRandomCode ? randomCode.count >= 8 : isUsernameValid && isEmailValid) + !isLoading + && (isWaitingForRandomCode ? randomCode.count >= 8 : isUsernameValid && isEmailValid) } func submit() async { @@ -30,7 +31,9 @@ extension RegisterScreenProtocol { } func sendEmail() async throws -> UnfortunateEvent? { - let response = try await api.createUser(body: .json(.init(email: email, username: username))) + let response = try await api.createUser( + body: .json(.init(email: email, username: username)) + ) switch response { case .created: @@ -86,7 +89,9 @@ extension RegisterScreenProtocol { } func createToken() async throws -> UnfortunateEvent? { - let response = try await api.createToken(body: .json(.init(identifier: email, secret: randomCode))) + let response = try await api.createToken( + body: .json(.init(identifier: email, secret: randomCode)) + ) switch response { case let .created(created): diff --git a/FyreplaceTests/Screens/LoginScreenTests.swift b/FyreplaceTests/Screens/LoginScreenTests.swift index a0b2729..61d5de2 100644 --- a/FyreplaceTests/Screens/LoginScreenTests.swift +++ b/FyreplaceTests/Screens/LoginScreenTests.swift @@ -1,7 +1,6 @@ import XCTest -@testable -import Fyreplace +@testable import Fyreplace final class LoginScreenTests: XCTestCase { class FakeScreen: LoginScreenProtocol { @@ -24,12 +23,12 @@ final class LoginScreenTests: XCTestCase { func testIdentifierMustHaveCorrectLength() { let screen = FakeScreen(eventBus: .init(), api: .fake()) - for i in 0 ..< 3 { + for i in 0..<3 { screen.identifier = .init(repeating: "a", count: i) XCTAssertFalse(screen.canSubmit) } - for i in 3 ... 254 { + for i in 3...254 { screen.identifier = .init(repeating: "a", count: i) XCTAssertTrue(screen.canSubmit) } diff --git a/FyreplaceTests/Screens/RegisterScreenTests.swift b/FyreplaceTests/Screens/RegisterScreenTests.swift index 1b340d5..317b403 100644 --- a/FyreplaceTests/Screens/RegisterScreenTests.swift +++ b/FyreplaceTests/Screens/RegisterScreenTests.swift @@ -1,7 +1,6 @@ import XCTest -@testable -import Fyreplace +@testable import Fyreplace final class RegisterScreenTests: XCTestCase { class FakeScreen: RegisterScreenProtocol { @@ -27,12 +26,12 @@ final class RegisterScreenTests: XCTestCase { let screen = FakeScreen(eventBus: .init(), api: .fake()) screen.email = "email@example" - for i in 0 ..< 3 { + for i in 0..<3 { screen.username = .init(repeating: "a", count: i) XCTAssertFalse(screen.canSubmit) } - for i in 3 ... 50 { + for i in 3...50 { screen.username = .init(repeating: "a", count: i) XCTAssertTrue(screen.canSubmit) } @@ -46,12 +45,12 @@ final class RegisterScreenTests: XCTestCase { let screen = FakeScreen(eventBus: .init(), api: .fake()) screen.username = "Example" - for i in 0 ..< 3 { + for i in 0..<3 { screen.email = .init(repeating: "@", count: i) XCTAssertFalse(screen.canSubmit) } - for i in 3 ... 254 { + for i in 3...254 { screen.email = .init(repeating: "@", count: i) XCTAssertTrue(screen.canSubmit) } diff --git a/FyreplaceTests/StoringEventBus.swift b/FyreplaceTests/StoringEventBus.swift index 5a3555f..c3cfe03 100644 --- a/FyreplaceTests/StoringEventBus.swift +++ b/FyreplaceTests/StoringEventBus.swift @@ -1,5 +1,4 @@ -@testable -import Fyreplace +@testable import Fyreplace class StoringEventBus: EventBus { var storedEvents: [Event] = []