diff --git a/WireAPI/Package.resolved b/WireAPI/Package.resolved new file mode 100644 index 00000000000..9468d9f24da --- /dev/null +++ b/WireAPI/Package.resolved @@ -0,0 +1,42 @@ +{ + "originHash" : "2ba11e1655444cb2c2454eeba5934b2d91ed9ad986846082e63f9d8aa5888465", + "pins" : [ + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-plugin", + "state" : { + "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", + "version" : "1.4.3" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", + "version" : "1.17.6" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + } + ], + "version" : 3 +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPI.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPI.swift new file mode 100644 index 00000000000..eeb37c4d904 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPI.swift @@ -0,0 +1,28 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +public protocol AccountsAPI { + + /// Convert the account from individual to team. + /// + /// + + func upgradeToTeam(teamName: String) async throws -> UpgradedAccountTeam +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIBuilder.swift new file mode 100644 index 00000000000..80b569282a7 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIBuilder.swift @@ -0,0 +1,58 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +/// A builder for `AccountsAPI`. + +public struct AccountsAPIBuilder { + + let apiService: any APIServiceProtocol + + /// Create a new builder. + /// + /// - Parameter apiService: An api service. + + public init(apiService: any APIServiceProtocol) { + self.apiService = apiService + } + + /// Make a versioned `AccountsAPI`. + /// + /// - Parameter version: An api version. + /// - Returns: A versioned `AccountsAPI`. + + public func makeAPI(for version: APIVersion) -> any AccountsAPI { + switch version { + case .v0: + AccountsAPIV0(apiService: apiService) + case .v1: + AccountsAPIV1(apiService: apiService) + case .v2: + AccountsAPIV2(apiService: apiService) + case .v3: + AccountsAPIV3(apiService: apiService) + case .v4: + AccountsAPIV4(apiService: apiService) + case .v5: + AccountsAPIV5(apiService: apiService) + case .v6: + AccountsAPIV6(apiService: apiService) + case .v7: + AccountsAPIV7(apiService: apiService) + } + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIError.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIError.swift new file mode 100644 index 00000000000..a810383b5bc --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIError.swift @@ -0,0 +1,35 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +/// Errors originating from `AccountsAPI`. +public enum AccountsAPIError: Error { + /// An error occurred while encoding the request body. + case invalidRequestBody + + /// A request url is invalid. + case invalidURL + + /// Unsupported endpoint for API version + case unsupportedEndpointForAPIVersion + + /// The user is already in a team + case userAlreadyInATeam + + /// The user could not be found + case userNotFound +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV0.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV0.swift new file mode 100644 index 00000000000..5eb8b4f538c --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV0.swift @@ -0,0 +1,35 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class AccountsAPIV0: AccountsAPI, VersionedAPI { + let apiService: any APIServiceProtocol + + init(apiService: any APIServiceProtocol) { + self.apiService = apiService + } + + var apiVersion: APIVersion { + .v0 + } + + func upgradeToTeam(teamName: String) async throws -> UpgradedAccountTeam { + throw AccountsAPIError.unsupportedEndpointForAPIVersion + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV1.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV1.swift new file mode 100644 index 00000000000..6411912d905 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV1.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV1: AccountsAPIV0 { + override var apiVersion: APIVersion { + .v1 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV2.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV2.swift new file mode 100644 index 00000000000..49cc3d9c6f0 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV2.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV2: AccountsAPIV1 { + override var apiVersion: APIVersion { + .v2 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV3.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV3.swift new file mode 100644 index 00000000000..8fcb079fa88 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV3.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV3: AccountsAPIV2 { + override var apiVersion: APIVersion { + .v3 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV4.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV4.swift new file mode 100644 index 00000000000..ef4b73a1adc --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV4.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV4: AccountsAPIV3 { + override var apiVersion: APIVersion { + .v4 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV5.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV5.swift new file mode 100644 index 00000000000..07ebb79890d --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV5.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV5: AccountsAPIV4 { + override var apiVersion: APIVersion { + .v5 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV6.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV6.swift new file mode 100644 index 00000000000..dbc3b60c022 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV6.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class AccountsAPIV6: AccountsAPIV5 { + override var apiVersion: APIVersion { + .v6 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV7.swift new file mode 100644 index 00000000000..502bb71674c --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/AccountsAPIV7.swift @@ -0,0 +1,60 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class AccountsAPIV7: AccountsAPIV6 { + override var apiVersion: APIVersion { + .v7 + } + + override func upgradeToTeam(teamName: String) async throws -> UpgradedAccountTeam { + let components = URLComponents(string: "upgrade-personal-to-team") + + guard let url = components?.url else { + assertionFailure("generated an invalid url") + throw AccountsAPIError.invalidURL + } + + let body = UpgradeToTeamRequestBodyV7(name: teamName) + + let encodedJSON: Data + do { + encodedJSON = try JSONEncoder().encode(body) + } catch { + assertionFailure("failed to encode body") + throw AccountsAPIError.invalidRequestBody + } + + let request = URLRequestBuilder(url: url) + .withBody(encodedJSON, contentType: .json) + .withMethod(.post) + .build() + + let (data, response) = try await apiService.executeRequest( + request, + requiringAccessToken: true + ) + + return try ResponseParser() + .success(code: .ok, type: UpgradeToTeamResponseV7.self) + .failure(code: .forbidden, label: "user-already-in-a-team", error: AccountsAPIError.userAlreadyInATeam) + .failure(code: .notFound, label: "not-found", error: AccountsAPIError.userNotFound) + .parse(code: response.statusCode, data: data) + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Requests/UpgradeToTeamRequestBodyV7.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Requests/UpgradeToTeamRequestBodyV7.swift new file mode 100644 index 00000000000..47316b2d4db --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Requests/UpgradeToTeamRequestBodyV7.swift @@ -0,0 +1,38 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +struct UpgradeToTeamRequestBodyV7: Codable, Sendable { + let currency: String? + let icon: String + let icon_key: String? + let name: String + + init( + currency: String? = nil, + icon: String = "default", + icon_key: String? = nil, + name: String + ) { + self.currency = currency + self.icon = icon + self.icon_key = icon_key + self.name = name + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Responses/UpgradeToTeamResponseV7.swift b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Responses/UpgradeToTeamResponseV7.swift new file mode 100644 index 00000000000..4f620dd7a94 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/AccountsAPI/Responses/UpgradeToTeamResponseV7.swift @@ -0,0 +1,32 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +struct UpgradeToTeamResponseV7: Decodable, ToAPIModelConvertible, Sendable { + + /// The team's ID. + public let teamId: UUID + + /// The team's name. + public let teamName: String + + func toAPIModel() -> UpgradedAccountTeam { + UpgradedAccountTeam(teamId: teamId, teamName: teamName) + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIBuilder.swift index 143079d58a9..0e82fda2d8e 100644 --- a/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIBuilder.swift @@ -53,6 +53,8 @@ public struct ConnectionsAPIBuilder { ConnectionsAPIV5(httpClient: httpClient) case .v6: ConnectionsAPIV6(httpClient: httpClient) + case .v7: + ConnectionsAPIV7(httpClient: httpClient) } } diff --git a/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIV7.swift new file mode 100644 index 00000000000..3570cf4328b --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/ConnectionsAPI/ConnectionsAPIV7.swift @@ -0,0 +1,24 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class ConnectionsAPIV7: ConnectionsAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIBuilder.swift index 64aa9c0820a..ced67191ca9 100644 --- a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIBuilder.swift @@ -51,6 +51,8 @@ public struct ConversationsAPIBuilder { ConversationsAPIV5(apiService: apiService) case .v6: ConversationsAPIV6(apiService: apiService) + case .v7: + ConversationsAPIV7(apiService: apiService) } } diff --git a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIError.swift b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIError.swift index f42887f66b7..adf98cd43e4 100644 --- a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIError.swift +++ b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIError.swift @@ -19,7 +19,7 @@ /// Errors originating from `ConversationsAPI`. public enum ConversationsAPIError: Error { - /// A request url is not invalid. + /// A request url is invalid. case invalidURL /// Failure if functionality has not been implemented. diff --git a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV6.swift b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV6.swift index dc3beee99c6..9072f6ff151 100644 --- a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV6.swift +++ b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV6.swift @@ -16,6 +16,6 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -final class ConversationsAPIV6: ConversationsAPIV5 { +class ConversationsAPIV6: ConversationsAPIV5 { override var apiVersion: APIVersion { .v6 } } diff --git a/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV7.swift new file mode 100644 index 00000000000..dc46903a819 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/ConversationsAPI/ConversationsAPIV7.swift @@ -0,0 +1,21 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +class ConversationsAPIV7: ConversationsAPIV6 { + override var apiVersion: APIVersion { .v7 } +} diff --git a/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift index 7f990077ad9..62f5bce7ad6 100644 --- a/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift @@ -53,7 +53,8 @@ public struct FeatureConfigsAPIBuilder { FeatureConfigsAPIV5(httpClient: httpClient) case .v6: FeatureConfigsAPIV6(httpClient: httpClient) + case .v7: + FeatureConfigsAPIV7(httpClient: httpClient) } } - } diff --git a/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIV7.swift new file mode 100644 index 00000000000..232703e6289 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIV7.swift @@ -0,0 +1,26 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class FeatureConfigsAPIV7: FeatureConfigsAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIBuilder.swift index d072e1c234a..a64207d1bc3 100644 --- a/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIBuilder.swift @@ -53,6 +53,8 @@ public struct SelfUserAPIBuilder { SelfUserAPIV5(httpClient: httpClient) case .v6: SelfUserAPIV6(httpClient: httpClient) + case .v7: + SelfUserAPIV7(httpClient: httpClient) } } diff --git a/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIV7.swift new file mode 100644 index 00000000000..58c21682055 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/SelfUserAPI/SelfUserAPIV7.swift @@ -0,0 +1,26 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class SelfUserAPIV7: SelfUserAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIBuilder.swift index b37717cec57..fe4ce3da859 100644 --- a/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIBuilder.swift @@ -53,7 +53,8 @@ public struct TeamsAPIBuilder { TeamsAPIV5(httpClient: httpClient) case .v6: TeamsAPIV6(httpClient: httpClient) + case .v7: + TeamsAPIV7(httpClient: httpClient) } } - } diff --git a/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIV7.swift new file mode 100644 index 00000000000..883c2b08cdb --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/TeamsAPI/TeamsAPIV7.swift @@ -0,0 +1,27 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class TeamsAPIV7: TeamsAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } + +} diff --git a/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIBuilder.swift index 4a41613b19e..65caf64ed4a 100644 --- a/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIBuilder.swift @@ -53,6 +53,8 @@ public struct UpdateEventsAPIBuilder { UpdateEventsAPIV5(apiService: apiService) case .v6: UpdateEventsAPIV6(apiService: apiService) + case .v7: + UpdateEventsAPIV7(apiService: apiService) } } diff --git a/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIError.swift b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIError.swift index 9989e915635..4745ff3f764 100644 --- a/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIError.swift +++ b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIError.swift @@ -22,7 +22,7 @@ import Foundation public enum UpdateEventsAPIError: Error { - /// A request url is not invalid. + /// A request url is invalid. case invalidURL diff --git a/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIV7.swift new file mode 100644 index 00000000000..80bfece4ff9 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/UpdateEventsAPI/UpdateEventsAPIV7.swift @@ -0,0 +1,26 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class UpdateEventsAPIV7: UpdateEventsAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } +} diff --git a/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIBuilder.swift index 2d2366c52fb..b15d5045af4 100644 --- a/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIBuilder.swift @@ -53,6 +53,8 @@ public struct UserClientsAPIBuilder { UserClientsAPIV5(apiService: apiService) case .v6: UserClientsAPIV6(apiService: apiService) + case .v7: + UserClientsAPIV7(apiService: apiService) } } diff --git a/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIError.swift b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIError.swift index 0dce6180081..01017288662 100644 --- a/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIError.swift +++ b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIError.swift @@ -22,7 +22,7 @@ import Foundation public enum UserClientsAPIError: Error { - /// A request url is not invalid. + /// A request url is invalid. case invalidURL } diff --git a/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIV7.swift new file mode 100644 index 00000000000..c4984a86e84 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/UserClientsAPI/UserClientsAPIV7.swift @@ -0,0 +1,27 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class UserClientsAPIV7: UserClientsAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } + +} diff --git a/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIBuilder.swift index 64bea8fe1c5..c31d910dee6 100644 --- a/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIBuilder.swift @@ -53,6 +53,8 @@ public struct UserPropertiesBuilder { UserPropertiesAPIV5(httpClient: httpClient) case .v6: UserPropertiesAPIV6(httpClient: httpClient) + case .v7: + UserPropertiesAPIV7(httpClient: httpClient) } } diff --git a/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIV7.swift new file mode 100644 index 00000000000..3aaf54c04b4 --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/UserPropertiesAPI/UserPropertiesAPIV7.swift @@ -0,0 +1,27 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class UserPropertiesAPIV7: UserPropertiesAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } + +} diff --git a/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIBuilder.swift index d4fe40803c7..66325e9ff44 100644 --- a/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIBuilder.swift @@ -53,7 +53,8 @@ public struct UsersAPIBuilder { UsersAPIV5(httpClient: httpClient) case .v6: UsersAPIV6(httpClient: httpClient) + case .v7: + UsersAPIV7(httpClient: httpClient) } } - } diff --git a/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIV7.swift b/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIV7.swift new file mode 100644 index 00000000000..cafd2c658fc --- /dev/null +++ b/WireAPI/Sources/WireAPI/APIs/UsersAPI/UsersAPIV7.swift @@ -0,0 +1,27 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +class UsersAPIV7: UsersAPIV6 { + + override var apiVersion: APIVersion { + .v7 + } + +} diff --git a/WireAPI/Sources/WireAPI/Assembly.swift b/WireAPI/Sources/WireAPI/Assembly.swift index 56d655009b4..e20630da944 100644 --- a/WireAPI/Sources/WireAPI/Assembly.swift +++ b/WireAPI/Sources/WireAPI/Assembly.swift @@ -19,7 +19,7 @@ import Foundation import WireFoundation -final class Assembly { +public final class Assembly { let userID: UUID let clientID: String @@ -28,7 +28,7 @@ final class Assembly { let minTLSVersion: TLSVersion let cookieEncryptionKey: Data - init( + public init( userID: UUID, clientID: String, backendURL: URL, @@ -52,7 +52,7 @@ final class Assembly { authenticationManager: authenticationManager ) - private lazy var apiNetworkService: NetworkService = { + public lazy var apiNetworkService: NetworkService = { let service = NetworkService(baseURL: backendURL) let config = urlSessionConfigurationFactory.makeRESTAPISessionConfiguration() let session = URLSession(configuration: config, delegate: service, delegateQueue: nil) @@ -73,7 +73,7 @@ final class Assembly { return service }() - private lazy var authenticationManager: some AuthenticationManagerProtocol = AuthenticationManager( + public lazy var authenticationManager: some AuthenticationManagerProtocol = AuthenticationManager( clientID: clientID, cookieStorage: cookieStorage, networkService: apiNetworkService diff --git a/WireAPI/Sources/WireAPI/Authentication/AuthenticationManager.swift b/WireAPI/Sources/WireAPI/Authentication/AuthenticationManager.swift index 98e24c933ae..e81fdf1e26c 100644 --- a/WireAPI/Sources/WireAPI/Authentication/AuthenticationManager.swift +++ b/WireAPI/Sources/WireAPI/Authentication/AuthenticationManager.swift @@ -20,7 +20,7 @@ import Foundation import WireFoundation // sourcery: AutoMockable -protocol AuthenticationManagerProtocol { +public protocol AuthenticationManagerProtocol { func getValidAccessToken() async throws -> AccessToken func refreshAccessToken() async throws -> AccessToken diff --git a/WireAPI/Sources/WireAPI/Components/ResponseParser.swift b/WireAPI/Sources/WireAPI/Components/ResponseParser.swift index dcc210b6c44..2b42776eab5 100644 --- a/WireAPI/Sources/WireAPI/Components/ResponseParser.swift +++ b/WireAPI/Sources/WireAPI/Components/ResponseParser.swift @@ -73,7 +73,7 @@ struct ResponseParser { func failure( code: HTTPStatusCode, label: String = "", - error: any Error + error: some Error ) -> ResponseParser { var copy = self copy.parseBlocks.append { _, data in diff --git a/WireAPI/Sources/WireAPI/Models/API/APIVersion.swift b/WireAPI/Sources/WireAPI/Models/API/APIVersion.swift index 294168b52a8..abc4dcc47d5 100644 --- a/WireAPI/Sources/WireAPI/Models/API/APIVersion.swift +++ b/WireAPI/Sources/WireAPI/Models/API/APIVersion.swift @@ -31,6 +31,7 @@ public enum APIVersion: UInt, CaseIterable, Comparable { case v4 case v5 case v6 + case v7 /// API versions considered production ready by the client. /// diff --git a/WireAPI/Sources/WireAPI/Models/Accounts/UpgradedAccountTeam.swift b/WireAPI/Sources/WireAPI/Models/Accounts/UpgradedAccountTeam.swift new file mode 100644 index 00000000000..f7060f87d5e --- /dev/null +++ b/WireAPI/Sources/WireAPI/Models/Accounts/UpgradedAccountTeam.swift @@ -0,0 +1,45 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation + +/// The upgraded account's team information. + +public struct UpgradedAccountTeam: Equatable, Sendable { + + /// The team's ID. + public let teamId: UUID + + /// The team's name. + public let teamName: String + + /// Create a new `UpgradedAccountTeam`. + /// + /// - Parameters: + /// - teamId: The team's ID. + /// - teamName: The team's name. + + public init( + teamId: UUID, + teamName: String + ) { + self.teamId = teamId + self.teamName = teamName + } + +} diff --git a/WireAPI/Sources/WireAPI/Models/Network/TLSVersion.swift b/WireAPI/Sources/WireAPI/Models/Network/TLSVersion.swift index 101460de87c..81aa816f5f5 100644 --- a/WireAPI/Sources/WireAPI/Models/Network/TLSVersion.swift +++ b/WireAPI/Sources/WireAPI/Models/Network/TLSVersion.swift @@ -30,6 +30,23 @@ public enum TLSVersion { case v1_3 + public static func minVersionFrom(_ string: String?) -> TLSVersion { + string.flatMap(TLSVersion.init) ?? .v1_2 + } + + public init?(_ string: String) { + switch string { + case "1.2": + self = .v1_2 + + case "1.3": + self = .v1_3 + + default: + return nil + } + } + var secValue: tls_protocol_version_t { switch self { case .v1_2: diff --git a/WireAPI/Sources/WireAPI/Network/APIService/APIService.swift b/WireAPI/Sources/WireAPI/Network/APIService/APIService.swift index 7b6dc3a5605..70b0e334897 100644 --- a/WireAPI/Sources/WireAPI/Network/APIService/APIService.swift +++ b/WireAPI/Sources/WireAPI/Network/APIService/APIService.swift @@ -53,7 +53,7 @@ public final class APIService: APIServiceProtocol { private let networkService: NetworkService private let authenticationManager: any AuthenticationManagerProtocol - init( + public init( networkService: NetworkService, authenticationManager: any AuthenticationManagerProtocol ) { diff --git a/WireAPI/Sources/WireAPI/Network/NetworkService/NetworkService.swift b/WireAPI/Sources/WireAPI/Network/NetworkService/NetworkService.swift index c9f8087bbec..51012e8ece1 100644 --- a/WireAPI/Sources/WireAPI/Network/NetworkService/NetworkService.swift +++ b/WireAPI/Sources/WireAPI/Network/NetworkService/NetworkService.swift @@ -18,7 +18,7 @@ import Foundation -final class NetworkService: NSObject { +public final class NetworkService: NSObject { private let baseURL: URL private var urlSession: URLSession? diff --git a/WireDomain/Package.resolved b/WireDomain/Package.resolved new file mode 100644 index 00000000000..3b2eb2263f3 --- /dev/null +++ b/WireDomain/Package.resolved @@ -0,0 +1,42 @@ +{ + "originHash" : "b0b8539ea418da22b07d0a75a40c21caa3f909f7b2c177680c5fdc645fc1d64a", + "pins" : [ + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-plugin", + "state" : { + "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", + "version" : "1.4.3" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", + "version" : "1.17.6" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + } + ], + "version" : 3 +} diff --git a/WireDomain/Sources/WireDomain/UseCases/IndividualToTeamMigrationUseCaseImplementation.swift b/WireDomain/Sources/WireDomain/UseCases/IndividualToTeamMigrationUseCaseImplementation.swift new file mode 100644 index 00000000000..d1b25dbf5b2 --- /dev/null +++ b/WireDomain/Sources/WireDomain/UseCases/IndividualToTeamMigrationUseCaseImplementation.swift @@ -0,0 +1,52 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import Foundation +import WireAPI +import WireSystem + +public struct IndividualToTeamMigrationResult { + public let teamID: UUID + public let teamName: String + + public init(teamID: UUID, teamName: String) { + self.teamID = teamID + self.teamName = teamName + } +} + +public struct IndividualToTeamMigrationUseCase { + private let accountsAPI: AccountsAPI + private let logger: WireLogger = .individualToTeamMigration + + public init(apiService: APIServiceProtocol) { + self.accountsAPI = AccountsAPIBuilder(apiService: apiService).build() + } + + public func invoke(teamName: String) async throws -> IndividualToTeamMigrationResult { + logger.debug("Migrating individual account to team account") + do { + let upgradeResult = try await accountsAPI.upgradeToTeam(teamName: teamName) + logger.debug("Individual account migrated to team account") + return IndividualToTeamMigrationResult(teamID: upgradeResult.teamId, teamName: upgradeResult.teamName) + } catch { + logger.error("Failed to migrate individual account to team account") + throw error + } + } +} diff --git a/WireFoundation/Sources/WireFoundation/Utilities/AttributedString+localized.swift b/WireFoundation/Sources/WireFoundation/Utilities/AttributedString+localized.swift index 8fdd6778d19..1f53e161f2b 100644 --- a/WireFoundation/Sources/WireFoundation/Utilities/AttributedString+localized.swift +++ b/WireFoundation/Sources/WireFoundation/Utilities/AttributedString+localized.swift @@ -19,7 +19,11 @@ public import Foundation public extension AttributedString { - static func formattedMarkdown(key: String.LocalizationValue, bundle: Bundle? = nil, _ arguments: any CVarArg...) -> AttributedString { + static func formattedMarkdown( + key: String.LocalizationValue, + bundle: Bundle? = nil, + _ arguments: any CVarArg... + ) -> AttributedString { let string: String = .formated(key: key, bundle: bundle, arguments) return .markdown(from: string) } @@ -30,6 +34,6 @@ public extension AttributedString { } static func markdown(from string: String) -> AttributedString { - return (try? AttributedString(markdown: string)) ?? AttributedString(string) + (try? AttributedString(markdown: string)) ?? AttributedString(string) } } diff --git a/WireFoundation/Sources/WireFoundation/Utilities/String+localized.swift b/WireFoundation/Sources/WireFoundation/Utilities/String+localized.swift index 6c875b13920..a324978b0cf 100644 --- a/WireFoundation/Sources/WireFoundation/Utilities/String+localized.swift +++ b/WireFoundation/Sources/WireFoundation/Utilities/String+localized.swift @@ -27,19 +27,17 @@ public extension String { String(localized: key, table: "Accessibility", bundle: bundle) } - /** - * - * Returns a localized string from the specified table. - * - parameter key: The key for the localized string. - * - parameter bundle: The bundle where the localized string is located. (default: .module) - * - returns: The localized string. - * - * Example: - * ``` - * let localizedString: String = .localized(key: "key") - * ``` - * - note: The default value for `bundle` is `.module`. - */ + /// + /// Returns a localized string from the specified table. + /// - parameter key: The key for the localized string. + /// - parameter bundle: The bundle where the localized string is located. (default: .module) + /// - returns: The localized string. + /// + /// Example: + /// ``` + /// let localizedString: String = .localized(key: "key") + /// ``` + /// - note: The default value for `bundle` is `.module`. static func localized(key: String.LocalizationValue, bundle: Bundle? = nil) -> String { String(localized: key, table: "Localizable", bundle: bundle) } diff --git a/WireUI/Sources/WireAccountImageUI/AccountImageView/AccountImageViewRepresentable.swift b/WireUI/Sources/WireAccountImageUI/AccountImageView/AccountImageViewRepresentable.swift index b6d693d9941..04e317b1c90 100644 --- a/WireUI/Sources/WireAccountImageUI/AccountImageView/AccountImageViewRepresentable.swift +++ b/WireUI/Sources/WireAccountImageUI/AccountImageView/AccountImageViewRepresentable.swift @@ -201,4 +201,3 @@ struct AvailabilityIndicatorBackgroundColorViewModifier: ViewModifier { private struct AvailabilityIndicatorBackgroundViewColorKey: EnvironmentKey { static let defaultValue = AvailabilityIndicatorView.Defaults.backgroundViewColor } - diff --git a/WireUI/Sources/WireDesign/Buttons/ButtonStyles.swift b/WireUI/Sources/WireDesign/Buttons/ButtonStyles.swift index 7a684dfa236..39637145d29 100644 --- a/WireUI/Sources/WireDesign/Buttons/ButtonStyles.swift +++ b/WireUI/Sources/WireDesign/Buttons/ButtonStyles.swift @@ -31,9 +31,9 @@ enum ButtonState: CaseIterable { struct WireButtonStyleModifier: ViewModifier { let wireButtonStyle: WireButtonStyle - - - @ViewBuilder private func applyStyle(_ wireButtonStyle: WireButtonStyle, content: () -> some View) -> some View { + + @ViewBuilder + private func applyStyle(_ wireButtonStyle: WireButtonStyle, content: () -> some View) -> some View { switch wireButtonStyle { case .primary: content().buttonStyle(PrimaryButtonStyle()) @@ -51,7 +51,7 @@ struct WireButtonStyleModifier: ViewModifier { public extension View { func wireButtonStyle(_ wireButtonStyle: WireButtonStyle) -> some View { - self.modifier(WireButtonStyleModifier(wireButtonStyle: wireButtonStyle)) + modifier(WireButtonStyleModifier(wireButtonStyle: wireButtonStyle)) } } @@ -59,7 +59,7 @@ public extension View { struct Buttons_Previews: PreviewProvider { static var previews: some View { - + ForEach(ButtonState.allCases, id: \.self) { state in Group { ForEach(WireButtonStyle.allCases, id: \.rawValue) { style in diff --git a/WireUI/Sources/WireDesign/Buttons/PrimaryButtonStyle.swift b/WireUI/Sources/WireDesign/Buttons/PrimaryButtonStyle.swift index 10ad2ef9d74..15a5e48db4d 100644 --- a/WireUI/Sources/WireDesign/Buttons/PrimaryButtonStyle.swift +++ b/WireUI/Sources/WireDesign/Buttons/PrimaryButtonStyle.swift @@ -24,17 +24,16 @@ struct PrimaryButtonStyle: SwiftUI.ButtonStyle { @Environment(\.isFocused) var isFocused typealias PrimaryTheme = ColorTheme.Buttons.Primary - + func makeBody(configuration: Configuration) -> some View { - configuration.label - .lineLimit(1) - .padding() - .frame(maxWidth: .infinity) - .background(isEnabled ? PrimaryTheme.enabled.color : PrimaryTheme.disabled.color) - .foregroundStyle(isEnabled ? PrimaryTheme.onEnabled.color : PrimaryTheme.onDisabled.color) - .wireTextStyle(.buttonBig) - .clipShape(.rect(cornerRadius: 16)) - } - + configuration.label + .lineLimit(1) + .padding() + .frame(maxWidth: .infinity) + .background(isEnabled ? PrimaryTheme.enabled.color : PrimaryTheme.disabled.color) + .foregroundStyle(isEnabled ? PrimaryTheme.onEnabled.color : PrimaryTheme.onDisabled.color) + .wireTextStyle(.buttonBig) + .clipShape(.rect(cornerRadius: 16)) + } } diff --git a/WireUI/Sources/WireDesign/Buttons/SecondaryButtonStyle.swift b/WireUI/Sources/WireDesign/Buttons/SecondaryButtonStyle.swift index 5214ae99281..6a91a2399c2 100644 --- a/WireUI/Sources/WireDesign/Buttons/SecondaryButtonStyle.swift +++ b/WireUI/Sources/WireDesign/Buttons/SecondaryButtonStyle.swift @@ -24,19 +24,19 @@ struct SecondaryButtonStyle: SwiftUI.ButtonStyle { @Environment(\.isFocused) var isFocused typealias Theme = ColorTheme.Buttons.Secondary - + func makeBody(configuration: Configuration) -> some View { - configuration.label - .lineLimit(1) - .padding() - .frame(maxWidth: .infinity) - .background(isEnabled ? Theme.enabled.color : Theme.disabled.color) - .foregroundStyle(isEnabled ? Theme.onEnabled.color : Theme.onDisabled.color) - .wireTextStyle(.buttonBig) - .overlay { - RoundedRectangle(cornerRadius: 16) - .stroke(isEnabled ? Theme.enabledOutline.color: Theme.disabledOutline.color, lineWidth: 1) - } - .clipShape(.rect(cornerRadius: 16)) - } + configuration.label + .lineLimit(1) + .padding() + .frame(maxWidth: .infinity) + .background(isEnabled ? Theme.enabled.color : Theme.disabled.color) + .foregroundStyle(isEnabled ? Theme.onEnabled.color : Theme.onDisabled.color) + .wireTextStyle(.buttonBig) + .overlay { + RoundedRectangle(cornerRadius: 16) + .stroke(isEnabled ? Theme.enabledOutline.color : Theme.disabledOutline.color, lineWidth: 1) + } + .clipShape(.rect(cornerRadius: 16)) + } } diff --git a/WireUI/Sources/WireDesign/Buttons/TertiaryButtonStyle.swift b/WireUI/Sources/WireDesign/Buttons/TertiaryButtonStyle.swift index 84bf6ee41bf..22419ce2c1a 100644 --- a/WireUI/Sources/WireDesign/Buttons/TertiaryButtonStyle.swift +++ b/WireUI/Sources/WireDesign/Buttons/TertiaryButtonStyle.swift @@ -23,7 +23,7 @@ struct TertiaryButtonStyle: SwiftUI.ButtonStyle { @Environment(\.isFocused) var isFocused typealias Theme = ColorTheme.Buttons.Tertiary - + func makeBody(configuration: Configuration) -> some View { let colors = colors(for: configuration.isPressed, enabled: isEnabled) configuration.label @@ -42,25 +42,28 @@ struct TertiaryButtonStyle: SwiftUI.ButtonStyle { } .clipShape(.rect(cornerRadius: 16)) } - - func colors(for pressed: Bool, enabled: Bool) -> (underline: Color, background: Color, foreground: Color, border: Color?) { + + func colors( + for pressed: Bool, + enabled: Bool + ) -> (underline: Color, background: Color, foreground: Color, border: Color?) { switch (enabled, pressed) { case (false, _): - return ( + ( underline: Theme.onDisabled.color, background: Theme.disabled.color, foreground: Theme.onDisabled.color, border: nil ) case (_, false): - return ( + ( underline: Theme.onEnabled.color, background: Theme.enabled.color, foreground: Theme.onEnabled.color, border: nil ) case (_, true): - return ( + ( underline: Theme.onSelected.color, background: Theme.selected.color, foreground: Theme.onSelected.color, diff --git a/WireUI/Sources/WireDesign/Colors/ColorTheme.swift b/WireUI/Sources/WireDesign/Colors/ColorTheme.swift index 58b191da754..3b446d4a9a3 100644 --- a/WireUI/Sources/WireDesign/Colors/ColorTheme.swift +++ b/WireUI/Sources/WireDesign/Colors/ColorTheme.swift @@ -21,9 +21,8 @@ import UIKit // The structure of this type corresponds to the Wire design system. - public enum ColorTheme { - case base(Base) + case base(Base) public enum Base { public static let primary = UIColor(light: .blue500Light, dark: .blue500Dark) diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/IndividualToTeamMigrationViewController.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/IndividualToTeamMigrationViewController.swift index c074f3bca9f..2982f71e031 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/IndividualToTeamMigrationViewController.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/IndividualToTeamMigrationViewController.swift @@ -38,22 +38,34 @@ public class IndividualToTeamMigrationViewController: UIViewController { var title: String { switch self { case .teamPlanSelection, .teamName, .confirmation: - return .localized(key: titleStringKey, bundle: .module) - case .completion(let profileName, _): - return .formated(key: titleStringKey, bundle: .module, profileName) + .localized(key: titleStringKey, bundle: .module) + case let .completion(profileName, _): + .formated(key: titleStringKey, bundle: .module, profileName) } } var closeButtonAccessibilityLabel: String { switch self { case .teamPlanSelection: - return .localizedAccessibilityLabel(key: "individualToTeam.planSelection.closeButton.accessibilityLabel", bundle: .module) + .localizedAccessibilityLabel( + key: "individualToTeam.planSelection.closeButton.accessibilityLabel", + bundle: .module + ) case .teamName: - return .localizedAccessibilityLabel(key: "individualToTeam.teamName.closeButton.accessibilityLabel", bundle: .module) + .localizedAccessibilityLabel( + key: "individualToTeam.teamName.closeButton.accessibilityLabel", + bundle: .module + ) case .confirmation: - return .localizedAccessibilityLabel(key: "individualToTeam.confirmation.closeButton.accessibilityLabel", bundle: .module) + .localizedAccessibilityLabel( + key: "individualToTeam.confirmation.closeButton.accessibilityLabel", + bundle: .module + ) case .completion: - return .localizedAccessibilityLabel(key: "individualToTeam.completion.closeButton.accessibilityLabel", bundle: .module) + .localizedAccessibilityLabel( + key: "individualToTeam.completion.closeButton.accessibilityLabel", + bundle: .module + ) } } @@ -106,7 +118,7 @@ public class IndividualToTeamMigrationViewController: UIViewController { fatalError() } - override public func viewDidLoad() { + public override func viewDidLoad() { super.viewDidLoad() addChild(childController) view.addSubview(childController.view) @@ -124,14 +136,15 @@ public class IndividualToTeamMigrationViewController: UIViewController { self?.actionCallback(.cancel) }, onContinue: { [weak self] in self?.transition(to: .dismissCancellationAlert) - }) + } + ) childController.present(alert, animated: true) case .dismissCancellationAlert: childController.dismiss(animated: true) case .toPlans: let vc = hostedView( for: .teamPlanSelection(features: features), - stepIndex: (childController.viewControllers.count + 1), + stepIndex: childController.viewControllers.count + 1, stepCount: 4, onTransition: { @MainActor [weak self] in self?.transition(to: $0) } ) @@ -139,7 +152,7 @@ public class IndividualToTeamMigrationViewController: UIViewController { case .toTeamName: let vc = hostedView( for: .teamName, - stepIndex: (childController.viewControllers.count + 1), + stepIndex: childController.viewControllers.count + 1, stepCount: 4, onTransition: { @MainActor [weak self] in self?.transition(to: $0) } ) @@ -147,7 +160,7 @@ public class IndividualToTeamMigrationViewController: UIViewController { case .toConfirmation: let vc = hostedView( for: .confirmation, - stepIndex: (childController.viewControllers.count + 1), + stepIndex: childController.viewControllers.count + 1, stepCount: 4, onTransition: { @MainActor [weak self] in self?.transition(to: $0) } ) @@ -155,7 +168,7 @@ public class IndividualToTeamMigrationViewController: UIViewController { case .toCompletion: let vc = hostedView( for: .completion(profileName: "Profile Name", teamName: "Some Team"), - stepIndex: (childController.viewControllers.count + 1), + stepIndex: childController.viewControllers.count + 1, stepCount: 4, onTransition: { @MainActor [weak self] in self?.transition(to: $0) } ) @@ -173,9 +186,13 @@ private func hostedView( for step: IndividualToTeamMigrationViewController.Step, stepIndex: Int, stepCount: Int, - onTransition transitionCallback: @escaping @MainActor @Sendable (IndividualToTeamMigrationViewController.Transition) -> Void + onTransition transitionCallback: @escaping @MainActor @Sendable ( + IndividualToTeamMigrationViewController + .Transition + ) -> Void ) -> UIViewController { - let vc = UIHostingController(rootView: + let vc = UIHostingController( + rootView: PageContainer( content: { viewFor( step: step, @@ -200,15 +217,20 @@ private func hostedView( vc.navigationItem.rightBarButtonItem?.tintColor = ColorTheme.Backgrounds.onBackground return vc } + @MainActor -@ViewBuilder func viewFor( +@ViewBuilder +func viewFor( step: IndividualToTeamMigrationViewController.Step, stepIndex: Int, stepCount: Int, - onTransition transitionCallback: @escaping @MainActor @Sendable (IndividualToTeamMigrationViewController.Transition) -> Void + onTransition transitionCallback: @escaping @MainActor @Sendable ( + IndividualToTeamMigrationViewController + .Transition + ) -> Void ) -> some View { switch step { - case .teamPlanSelection(let features): + case let .teamPlanSelection(features): TeamPlanSelectionView(features: features) { action in switch action { case .goToPlans: @@ -218,20 +240,20 @@ private func hostedView( } } case .teamName: - TeamNameView() { action in + TeamNameView { action in switch action { - case .continue(let teamName): + case let .continue(teamName): transitionCallback(.toConfirmation) } } case .confirmation: - ConfirmationView() { action in + ConfirmationView { action in switch action { case .continue: transitionCallback(.toCompletion) } } - case .completion(let profileName, let teamName): + case let .completion(profileName, teamName): CompletionView(profileName: profileName, teamName: teamName) { action in switch action { case .goBack: diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CancellationSheet.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CancellationSheet.swift index 75ac960c694..b978874ea45 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CancellationSheet.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CancellationSheet.swift @@ -20,7 +20,7 @@ import SwiftUI import WireFoundation @MainActor -internal func cancellationSheetFactory( +func cancellationSheetFactory( onLeave: @escaping @MainActor @Sendable () -> Void, onContinue: @escaping @MainActor @Sendable () -> Void ) -> UIAlertController { diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CompletionView.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CompletionView.swift index 86dac64a8c5..53781d8abde 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CompletionView.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/CompletionView.swift @@ -51,7 +51,10 @@ struct CompletionView: View { Button( action: { actionCallback(.goToTeamManagement) }, - label: { Text(String.localized(key: "individualToTeam.completion.button.teamManagement", bundle: .module)) } + label: { Text(String.localized( + key: "individualToTeam.completion.button.teamManagement", + bundle: .module + )) } ) .wireButtonStyle(.primary) } diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/ConfirmationPreview.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/ConfirmationPreview.swift index c5d45eae2c6..dd8b081476e 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/ConfirmationPreview.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/ConfirmationPreview.swift @@ -23,7 +23,7 @@ import WireFoundation #Preview { PageContainer( content: { - ConfirmationView() { _ in } + ConfirmationView { _ in } }, step: 3, stepCount: 4, diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamNamePreview.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamNamePreview.swift index b6cbc123dd0..00fdd2d6963 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamNamePreview.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamNamePreview.swift @@ -23,7 +23,7 @@ import WireFoundation #Preview { PageContainer( content: { - TeamNameView() { _ in } + TeamNameView { _ in } }, step: 2, stepCount: 4, diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamPlanSelectionPreview.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamPlanSelectionPreview.swift index e2b25a7f1e9..c58e89baf9f 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamPlanSelectionPreview.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/TeamPlanSelectionPreview.swift @@ -27,23 +27,38 @@ import WireFoundation features: [ .init( id: "console", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.adminConsole", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.adminConsole", + bundle: .module + ) ), .init( id: "collaboration", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.collaboration", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.collaboration", + bundle: .module + ) ), .init( id: "meetings", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.meetings", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.meetings", + bundle: .module + ) ), .init( id: "status", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.status", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.status", + bundle: .module + ) ), .init( id: "enterprise", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.enterprise", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.enterprise", + bundle: .module + ) ) ] ) { _ in } diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/_FeaturePreview.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/_FeaturePreview.swift index 6c58febcb06..6b071f7d524 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/_FeaturePreview.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/Previews/_FeaturePreview.swift @@ -26,14 +26,14 @@ struct MockUseCase: IndividualToTeamMigrationUseCase { } } -fileprivate struct FeaturePreviewContainer: UIViewControllerRepresentable { +private struct FeaturePreviewContainer: UIViewControllerRepresentable { typealias UIViewControllerType = IndividualToTeamMigrationViewController let features: [TeamPlanFeature] func makeUIViewController(context: Context) -> IndividualToTeamMigrationViewController { - return IndividualToTeamMigrationViewController( + IndividualToTeamMigrationViewController( features: features, useCase: MockUseCase(), actionCallback: { _ in } @@ -53,7 +53,10 @@ fileprivate struct FeaturePreviewContainer: UIViewControllerRepresentable { ), .init( id: "collaboration", - description: .localizedMarkdown(key: "individualToTeam.planSelection.feature.collaboration", bundle: .module) + description: .localizedMarkdown( + key: "individualToTeam.planSelection.feature.collaboration", + bundle: .module + ) ), .init( id: "meetings", diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/SelfProfileViewCallToActionBanner.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/SelfProfileViewCallToActionBanner.swift index 9c9c6822115..b0d908ccd8e 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/SelfProfileViewCallToActionBanner.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/SelfProfileViewCallToActionBanner.swift @@ -43,7 +43,8 @@ public struct SelfProfileViewCallToActionBanner: View { } @MainActor -@ViewBuilder fileprivate func contentView( +@ViewBuilder +private func contentView( actionCallback: @escaping @Sendable (SelfProfileViewCallToActionBanner.Action) -> Void ) -> some View { VStack(alignment: .leading, spacing: 12) { @@ -67,9 +68,9 @@ public struct SelfProfileViewCallToActionBanner: View { } } -fileprivate extension View { +private extension View { func bannerBackground() -> some View { - self.background { + background { if #available(iOS 17.0, *) { RoundedRectangle(cornerRadius: 8) .stroke(ColorTheme.Banners.border.color, lineWidth: 1) @@ -86,7 +87,7 @@ fileprivate extension View { public class SelfProfileViewCallToActionBannerHostingController: UIHostingController { public init(actionCallback: @escaping @Sendable (SelfProfileViewCallToActionBanner.Action) -> Void) { super.init(rootView: SelfProfileViewCallToActionBanner(actionCallback: actionCallback)) - self.view.backgroundColor = .clear + view.backgroundColor = .clear } @available(*, unavailable) @@ -95,9 +96,7 @@ public class SelfProfileViewCallToActionBannerHostingController: UIHostingContro } } - @available(iOS 17.0, *) #Preview { SelfProfileViewCallToActionBanner(actionCallback: { _ in }) } - diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamNameView.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamNameView.swift index 03d462184f8..7049e6d866a 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamNameView.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamNameView.swift @@ -37,9 +37,12 @@ struct TeamNameView: View { .frame(height: 24) Text(String.localized(key: "individualToTeam.teamName.field.title", bundle: .module)) .wireTextStyle(.h4) - TextField(String.localized(key: "individualToTeam.teamName.field.placeholder", bundle: .module), text: $teamName) - .textFieldStyle(.roundedBorder) - .wireTextStyle(.body1) + TextField( + String.localized(key: "individualToTeam.teamName.field.placeholder", bundle: .module), + text: $teamName + ) + .textFieldStyle(.roundedBorder) + .wireTextStyle(.body1) Spacer() Button( diff --git a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamPlanSelectionView.swift b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamPlanSelectionView.swift index 10395ec85f1..7af879ee742 100644 --- a/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamPlanSelectionView.swift +++ b/WireUI/Sources/WireIndividualToTeamMigrationUI/Views/TeamPlanSelectionView.swift @@ -52,15 +52,15 @@ struct TeamPlanSelectionView: View { } } } - // TODO: Add a "link" Text Style? Button( - action: { }, + action: {}, label: { Text(String.localized(key: "individualToTeam.planSelection.url", bundle: .module)) - .tint(.primary) - .underline() +// .tint(.primary) +// .underline() } ) + .wireButtonStyle(.tertiary) .padding(.top, 4) Spacer() Button( diff --git a/WireUI/Tests/WireIndividualToTeamMigrationUITests/Views/TeamPlanSelectionViewSnapshotTest.swift b/WireUI/Tests/WireIndividualToTeamMigrationUITests/Views/TeamPlanSelectionViewSnapshotTest.swift index 7b099a81dda..5e303c2201a 100644 --- a/WireUI/Tests/WireIndividualToTeamMigrationUITests/Views/TeamPlanSelectionViewSnapshotTest.swift +++ b/WireUI/Tests/WireIndividualToTeamMigrationUITests/Views/TeamPlanSelectionViewSnapshotTest.swift @@ -15,4 +15,3 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. // - diff --git a/WireUI/Tests/WireIndividualToTeamMigrationUITests/WireIndividualToTeamMigrationTests.swift b/WireUI/Tests/WireIndividualToTeamMigrationUITests/WireIndividualToTeamMigrationTests.swift index ea1fc3e6937..c5790d4a14a 100644 --- a/WireUI/Tests/WireIndividualToTeamMigrationUITests/WireIndividualToTeamMigrationTests.swift +++ b/WireUI/Tests/WireIndividualToTeamMigrationUITests/WireIndividualToTeamMigrationTests.swift @@ -16,9 +16,9 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -@testable import WireIndividualToTeamMigration import XCTest +@testable import WireIndividualToTeamMigration class WireIndividualToTeamMigrationTests: XCTestCase { - func testExample() { } + func testExample() {} } diff --git a/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift b/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift index fbe5a0227b0..88ab2e366fe 100644 --- a/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift +++ b/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift @@ -17,6 +17,7 @@ // import avs +import WireAPI import WireDataModel open class AuthenticatedSessionFactory { @@ -60,6 +61,25 @@ open class AuthenticatedSessionFactory { sharedUserDefaults: UserDefaults, isDeveloperModeEnabled: Bool ) -> ZMUserSession? { + + let apiServiceFactory = { [environment, minTLSVersion] (clientID: String, userID: UUID) in + let wireAssembly = WireAPI.Assembly( + userID: userID, + clientID: clientID, + backendURL: environment.backendURL, + backendWebSocketURL: environment.backendWSURL, + minTLSVersion: WireAPI.TLSVersion.minVersionFrom(minTLSVersion), + cookieEncryptionKey: UserDefaults.cookiesKey() + ) + + let authenticationManager = wireAssembly.authenticationManager + let networkService = wireAssembly.apiNetworkService + + return APIService( + networkService: networkService, + authenticationManager: authenticationManager + ) + } let transportSession = ZMTransportSession( environment: environment, proxyUsername: proxyUsername, @@ -74,6 +94,7 @@ open class AuthenticatedSessionFactory { var userSessionBuilder = ZMUserSessionBuilder() userSessionBuilder.withAllDependencies( + apiServiceFactory: apiServiceFactory, appVersion: appVersion, application: application, cryptoboxMigrationManager: CryptoboxMigrationManager(), diff --git a/wire-ios-sync-engine/Source/Synchronization/Strategies/RegistationCredentialVerificationStrategy.swift b/wire-ios-sync-engine/Source/Synchronization/Strategies/RegistationCredentialVerificationStrategy.swift index cbc2785e67a..cb263ce5961 100644 --- a/wire-ios-sync-engine/Source/Synchronization/Strategies/RegistationCredentialVerificationStrategy.swift +++ b/wire-ios-sync-engine/Source/Synchronization/Strategies/RegistationCredentialVerificationStrategy.swift @@ -84,7 +84,10 @@ extension RegistationCredentialVerificationStrategy: ZMSingleRequestTranscoder { // We can end up here because more than one request can be sent for a single action/phase. // This is an issue in some other part of SyncEngine but as a quick fix we will log and abort here. let phaseString = registrationStatus.phase.map { "\($0)" } ?? "" - WireLogger.authentication.error("Recieved unsuccessful response for invalid phase (\(phaseString))", attributes: .safePublic) + WireLogger.authentication.error( + "Recieved unsuccessful response for invalid phase (\(phaseString))", + attributes: .safePublic + ) return assertionFailure("Error occurs for invalid phase: \(phaseString)") } registrationStatus.handleError(error) diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index 389fad27e67..537616e15fc 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -19,6 +19,7 @@ import Combine import Foundation import WireAnalytics +import WireAPI import WireDataModel import WireRequestStrategy import WireSystem @@ -40,6 +41,8 @@ public final class ZMUserSession: NSObject { private(set) var isNetworkOnline = true private(set) var coreDataStack: CoreDataStack! + private let apiServiceFactory: (_ clientID: String, _ userID: UUID) -> APIServiceProtocol + private(set) var apiService: APIServiceProtocol? let application: ZMApplication let flowManager: FlowManagerType private(set) var mediaManager: MediaManagerType @@ -193,7 +196,7 @@ public final class ZMUserSession: NSObject { } // swiftlint:disable:next todo_requires_jira_link - public var selfUserClient: UserClient? { // TODO: jacob we don't want this to be public + public var selfUserClient: WireDataModel.UserClient? { // TODO: jacob we don't want this to be public ZMUser.selfUser(in: managedObjectContext).selfClient() } @@ -361,6 +364,7 @@ public final class ZMUserSession: NSObject { transportSession: any TransportSessionType, mediaManager: any MediaManagerType, flowManager: any FlowManagerType, + apiServiceFactory: @escaping @Sendable (_ clientID: String, _ userID: UUID) -> APIServiceProtocol, application: ZMApplication, appVersion: String, coreDataStack: CoreDataStack, @@ -379,6 +383,7 @@ public final class ZMUserSession: NSObject { recurringActionService: any RecurringActionServiceInterface, dependencies: UserSessionDependencies ) { + self.apiServiceFactory = apiServiceFactory self.application = application self.appVersion = appVersion self.flowManager = flowManager @@ -731,7 +736,7 @@ public final class ZMUserSession: NSObject { // MARK: Access Token - private func renewAccessTokenIfNeeded(for userClient: UserClient) { + private func renewAccessTokenIfNeeded(for userClient: WireDataModel.UserClient) { guard let apiVersion = BackendInfo.apiVersion, apiVersion > .v2, @@ -1050,14 +1055,14 @@ extension ZMUserSession: ZMSyncStateDelegate { } } - public func didRegisterSelfUserClient(_ userClient: UserClient) { + public func didRegisterSelfUserClient(_ userClient: WireDataModel.UserClient) { // If during registration user allowed notifications, // The push token can only be registered after client registration transportSession.pushChannel.clientID = userClient.remoteIdentifier registerCurrentPushToken() renewAccessTokenIfNeeded(for: userClient) - UserClient.triggerSelfClientCapabilityUpdate(syncContext) + WireDataModel.UserClient.triggerSelfClientCapabilityUpdate(syncContext) managedObjectContext.performGroupedBlock { [weak self] in guard @@ -1081,6 +1086,7 @@ extension ZMUserSession: ZMSyncStateDelegate { let clientId = userClient.safeRemoteIdentifier.safeForLoggingDescription WireLogger.authentication.addTag(.selfClientId, value: clientId) + apiService = apiServiceFactory(clientId, selfUser.remoteIdentifier) } public func didFailToRegisterSelfUserClient(error: Error) { diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift index cfe19773887..d2cb4c18fc6 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift @@ -17,6 +17,7 @@ // import Foundation +import WireAPI import WireDataModel import WireRequestStrategy import WireUtilities @@ -25,6 +26,7 @@ struct ZMUserSessionBuilder { // MARK: - Properties + private var apiServiceFactory: (@Sendable (_ clientID: String, _ userID: UUID) -> APIServiceProtocol)? private var appVersion: String? private var appLock: (any AppLockType)? private var application: (any ZMApplication)? @@ -55,6 +57,7 @@ struct ZMUserSessionBuilder { func build() -> ZMUserSession { guard + let apiServiceFactory, let appVersion, let appLock, let application, @@ -85,6 +88,7 @@ struct ZMUserSessionBuilder { transportSession: transportSession, mediaManager: mediaManager, flowManager: flowManager, + apiServiceFactory: apiServiceFactory, application: application, appVersion: appVersion, coreDataStack: coreDataStack, @@ -108,6 +112,7 @@ struct ZMUserSessionBuilder { // MARK: - Setup Dependencies mutating func withAllDependencies( + apiServiceFactory: @escaping @Sendable (_ clientID: String, _ userID: UUID) -> APIServiceProtocol, appVersion: String, application: any ZMApplication, cryptoboxMigrationManager: any CryptoboxMigrationManagerInterface, @@ -195,6 +200,7 @@ struct ZMUserSessionBuilder { // setup builder + self.apiServiceFactory = apiServiceFactory self.appVersion = appVersion self.appLock = appLock self.application = application diff --git a/wire-ios-system/Source/Logging/LoggerProtocol.swift b/wire-ios-system/Source/Logging/LoggerProtocol.swift index 2a692fc5794..612e5476f40 100644 --- a/wire-ios-system/Source/Logging/LoggerProtocol.swift +++ b/wire-ios-system/Source/Logging/LoggerProtocol.swift @@ -31,9 +31,9 @@ public protocol LoggerProtocol { func addTag(_ key: LogAttributesKey, value: String?) } -extension LoggerProtocol { +public extension LoggerProtocol { - public func attributesDescription(from attributes: LogAttributes) -> String { + func attributesDescription(from attributes: LogAttributes) -> String { var logAttributes = attributes // drop attributes used for visibility and category @@ -55,7 +55,7 @@ extension LoggerProtocol { /// helper method to transform attributes array to single LogAttributes /// - note: if same key is contained accross multiple attributes, the latest one is taken - public func flattenArray(_ attributes: [LogAttributes]) -> LogAttributes { + func flattenArray(_ attributes: [LogAttributes]) -> LogAttributes { var mergedAttributes: LogAttributes = [:] attributes.forEach { mergedAttributes.merge($0) { _, new in new } diff --git a/wire-ios-system/Source/Logging/WireLogger+Instances.swift b/wire-ios-system/Source/Logging/WireLogger+Instances.swift index a3b7a9beef7..32e3de0c5b6 100644 --- a/wire-ios-system/Source/Logging/WireLogger+Instances.swift +++ b/wire-ios-system/Source/Logging/WireLogger+Instances.swift @@ -34,6 +34,7 @@ public extension WireLogger { static let ear = WireLogger(tag: "encryption-at-rest") static let environment = WireLogger(tag: "environment") static let featureConfigs = WireLogger(tag: "feature-configurations") + static let individualToTeamMigration = WireLogger(tag: "individual-to-team-migration") static let keychain = WireLogger(tag: "keychain") static let localStorage = WireLogger(tag: "local-storage") static let mainCoordinator = WireLogger(tag: "main-coordinator") diff --git a/wire-ios-system/Source/Logging/WireLogger.swift b/wire-ios-system/Source/Logging/WireLogger.swift index a8f4cd2f1de..a5d937ae7fb 100644 --- a/wire-ios-system/Source/Logging/WireLogger.swift +++ b/wire-ios-system/Source/Logging/WireLogger.swift @@ -27,7 +27,7 @@ public struct WireLogger: LoggerProtocol, Sendable { provider = AggregatedLogger(loggers: loggers) } - private static nonisolated(unsafe) var provider: (any LoggerProtocol)? + private nonisolated(unsafe) static var provider: (any LoggerProtocol)? public let tag: String diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/CustomAppLock/Setup/PasscodeSetupViewController.swift b/wire-ios/Wire-iOS/Sources/UserInterface/CustomAppLock/Setup/PasscodeSetupViewController.swift index cfd4cd4b262..1cce1206a22 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/CustomAppLock/Setup/PasscodeSetupViewController.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/CustomAppLock/Setup/PasscodeSetupViewController.swift @@ -97,7 +97,8 @@ final class PasscodeSetupViewController: UIViewController { private lazy var infoLabel: UILabel = { let label = DynamicFontLabel( fontSpec: .normalRegularFont, - color: ColorTheme.Backgrounds.onSurfaceVariant) + color: ColorTheme.Backgrounds.onSurfaceVariant + ) label.textAlignment = .center label.configMultipleLineLabel() return label @@ -222,8 +223,14 @@ final class PasscodeSetupViewController: UIViewController { contentView.widthAnchor.constraint(lessThanOrEqualToConstant: 375), contentView.topAnchor.constraint(equalTo: contentLayoutGuide.topAnchor), contentView.bottomAnchor.constraint(equalTo: contentLayoutGuide.bottomAnchor), - contentView.leadingAnchor.constraint(greaterThanOrEqualTo: contentLayoutGuide.leadingAnchor, constant: contentPadding), - contentView.trailingAnchor.constraint(lessThanOrEqualTo: contentLayoutGuide.trailingAnchor, constant: -contentPadding), + contentView.leadingAnchor.constraint( + greaterThanOrEqualTo: contentLayoutGuide.leadingAnchor, + constant: contentPadding + ), + contentView.trailingAnchor.constraint( + lessThanOrEqualTo: contentLayoutGuide.trailingAnchor, + constant: -contentPadding + ), contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor), // stack view diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/SelfProfileViewController.swift b/wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/SelfProfileViewController.swift index b54d877a73f..34e97e74279 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/SelfProfileViewController.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/SelfProfileViewController.swift @@ -20,6 +20,7 @@ // - alert that new devices have been added // - alert about read receipts enabled +import SwiftUI import UIKit import WireCommonComponents import WireDesign @@ -28,7 +29,6 @@ import WireMainNavigationUI import WireReusableUIComponents import WireSettingsUI import WireSyncEngine -import SwiftUI /// The first page of the user settings. final class SelfProfileViewController: UIViewController { @@ -44,7 +44,7 @@ final class SelfProfileViewController: UIViewController { private let profileHeaderViewController: ProfileHeaderViewController private let profileImagePicker = ProfileImagePickerManager() private lazy var teamMigrationBanner: UIViewController = SelfProfileViewCallToActionBannerHostingController( - actionCallback: { [weak self] action in + actionCallback: { [weak self] _ in self?.userDidTapCreateTeam() } ) @@ -199,7 +199,8 @@ final class SelfProfileViewController: UIViewController { profileHeaderViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), profileHeaderViewController.view.topAnchor.constraint(greaterThanOrEqualTo: profileLayoutGuide.topAnchor), profileHeaderViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), - profileHeaderViewController.view.bottomAnchor.constraint(lessThanOrEqualTo: profileLayoutGuide.bottomAnchor), + profileHeaderViewController.view.bottomAnchor + .constraint(lessThanOrEqualTo: profileLayoutGuide.bottomAnchor), profileHeaderViewController.view.centerYAnchor.constraint(equalTo: profileLayoutGuide.centerYAnchor), // settingsControllerView