From ac062e361cd3931e419b74379b1b4441167e9316 Mon Sep 17 00:00:00 2001 From: Jacek Krasiukianis Date: Tue, 15 Apr 2025 13:58:12 +0200 Subject: [PATCH] ET-638 Share extension --- .../FileManager+AttachmentHelpers.swift | 2 +- .../UIKitComponents/Models/MockDraft.swift | 5 +- .../Composer/Views/Attachments/Jpeg.swift | 4 +- Modules/InboxCore/Sources/AppLogger.swift | 1 + .../ApiEnvId+PaymentAvailability.swift} | 3 +- .../InboxTesting/Sources/MailSessionSpy.swift | 4 +- .../Sources/ApiEnvId+Current.swift | 31 ++++ .../Sources/ShareViewController.swift | 64 +++++++ Modules/TestableShareExtension/Package.swift | 25 +++ .../Sources/Draft/DraftPrecomposer.swift | 94 ++++++++++ .../Sources/Draft/SharedItemsParser.swift | 50 ++++++ .../Payments/UpsellConfiguration.swift | 27 +++ .../Sources/UI/ErrorScreen.swift | 87 +++++++++ .../Sources/UI/ShareScreen/ShareScreen.swift | 74 ++++++++ .../UI/ShareScreen/ShareScreenModel.swift | 168 ++++++++++++++++++ .../Sources/Uniffi/SessionHolder.swift | 64 +++++++ .../Sources/Utils/L10n.swift | 31 ++++ .../Utils/NSItemProvider+Extensions.swift | 54 ++++++ .../Tests/Doubles/ExtensionContextSpy.swift | 35 ++++ .../Tests/Draft/DraftPrecomposerTests.swift | 164 +++++++++++++++++ .../Tests/Draft/SharedItemsParserTests.swift | 108 +++++++++++ .../Tests/TestDataFactory.swift | 109 ++++++++++++ .../Tests/TestError.swift | 23 +++ .../Tests/UI/ErrorViewSnapshotTests.swift | 38 ++++ .../ShareScreen/ShareScreenModelTests.swift | 124 +++++++++++++ .../withButtonToOpenApp.dark.png | Bin 0 -> 93097 bytes .../withButtonToOpenApp.light.png | Bin 0 -> 92354 bytes .../withoutButtonToOpenApp.dark.png | Bin 0 -> 75728 bytes .../withoutButtonToOpenApp.light.png | Bin 0 -> 75471 bytes ShareExtension.entitlements | 10 ++ TestPlans/AllUnitAndSnapshotTests.xctestplan | 41 +++-- fastlane/Fastfile | 4 +- project.yml | 80 ++++++--- 33 files changed, 1480 insertions(+), 44 deletions(-) rename Modules/{App/Sources/Payments/Environment+PaymentAvailability.swift => InboxCore/Sources/ProtonMailUniffi/ApiEnvId+PaymentAvailability.swift} (96%) create mode 100644 Modules/ShareExtension/Sources/ApiEnvId+Current.swift create mode 100644 Modules/ShareExtension/Sources/ShareViewController.swift create mode 100644 Modules/TestableShareExtension/Package.swift create mode 100644 Modules/TestableShareExtension/Sources/Draft/DraftPrecomposer.swift create mode 100644 Modules/TestableShareExtension/Sources/Draft/SharedItemsParser.swift create mode 100644 Modules/TestableShareExtension/Sources/Payments/UpsellConfiguration.swift create mode 100644 Modules/TestableShareExtension/Sources/UI/ErrorScreen.swift create mode 100644 Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreen.swift create mode 100644 Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreenModel.swift create mode 100644 Modules/TestableShareExtension/Sources/Uniffi/SessionHolder.swift create mode 100644 Modules/TestableShareExtension/Sources/Utils/L10n.swift create mode 100644 Modules/TestableShareExtension/Sources/Utils/NSItemProvider+Extensions.swift create mode 100644 Modules/TestableShareExtension/Tests/Doubles/ExtensionContextSpy.swift create mode 100644 Modules/TestableShareExtension/Tests/Draft/DraftPrecomposerTests.swift create mode 100644 Modules/TestableShareExtension/Tests/Draft/SharedItemsParserTests.swift create mode 100644 Modules/TestableShareExtension/Tests/TestDataFactory.swift create mode 100644 Modules/TestableShareExtension/Tests/TestError.swift create mode 100644 Modules/TestableShareExtension/Tests/UI/ErrorViewSnapshotTests.swift create mode 100644 Modules/TestableShareExtension/Tests/UI/ShareScreen/ShareScreenModelTests.swift create mode 100644 Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.dark.png create mode 100644 Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.light.png create mode 100644 Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withoutButtonToOpenApp.dark.png create mode 100644 Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withoutButtonToOpenApp.light.png create mode 100644 ShareExtension.entitlements diff --git a/Modules/InboxComposer/Sources/Composer/Extensions/FileManager+AttachmentHelpers.swift b/Modules/InboxComposer/Sources/Composer/Extensions/FileManager+AttachmentHelpers.swift index d15f07e375..d0af3cc999 100644 --- a/Modules/InboxComposer/Sources/Composer/Extensions/FileManager+AttachmentHelpers.swift +++ b/Modules/InboxComposer/Sources/Composer/Extensions/FileManager+AttachmentHelpers.swift @@ -37,7 +37,7 @@ extension FileManager { /** Moves the given `file` to `destinationFolder`. If a file with the same name exists, it creates a unique file name using `uniqueFileNameURL(in folder:,baseName:,fileExtension:)` */ - func moveToUniqueURL(file: URL, to destinationFolder: URL) throws -> URL { + public func moveToUniqueURL(file: URL, to destinationFolder: URL) throws -> URL { let uniqueURL = uniqueFileNameURL( in: destinationFolder, baseName: file.deletingPathExtension().lastPathComponent, diff --git a/Modules/InboxComposer/Sources/Composer/UIKitComponents/Models/MockDraft.swift b/Modules/InboxComposer/Sources/Composer/UIKitComponents/Models/MockDraft.swift index 4538715b46..f91ff2eed5 100644 --- a/Modules/InboxComposer/Sources/Composer/UIKitComponents/Models/MockDraft.swift +++ b/Modules/InboxComposer/Sources/Composer/UIKitComponents/Models/MockDraft.swift @@ -178,7 +178,10 @@ final class MockDraft: AppDraftProtocol, @unchecked Sendable { mockSender } - func setBody(body: String) -> VoidDraftSaveResult { .ok } + func setBody(body: String) -> VoidDraftSaveResult { + mockBody = body + return .ok + } func setSubject(subject: String) -> VoidDraftSaveResult { mockSubject = subject diff --git a/Modules/InboxComposer/Sources/Composer/Views/Attachments/Jpeg.swift b/Modules/InboxComposer/Sources/Composer/Views/Attachments/Jpeg.swift index cb18f22c90..983d7f2322 100644 --- a/Modules/InboxComposer/Sources/Composer/Views/Attachments/Jpeg.swift +++ b/Modules/InboxComposer/Sources/Composer/Views/Attachments/Jpeg.swift @@ -15,6 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Proton Mail. If not, see https://www.gnu.org/licenses/. -enum JPEG { - static let compressionQuality = 0.8 +public enum JPEG { + public static let compressionQuality = 0.8 } diff --git a/Modules/InboxCore/Sources/AppLogger.swift b/Modules/InboxCore/Sources/AppLogger.swift index ead83c54ca..a6067e5240 100644 --- a/Modules/InboxCore/Sources/AppLogger.swift +++ b/Modules/InboxCore/Sources/AppLogger.swift @@ -140,6 +140,7 @@ extension AppLogger { case rustLibrary case search case send + case shareExtension case snooze case thirtySecondsBackgroundTask case userSessions diff --git a/Modules/App/Sources/Payments/Environment+PaymentAvailability.swift b/Modules/InboxCore/Sources/ProtonMailUniffi/ApiEnvId+PaymentAvailability.swift similarity index 96% rename from Modules/App/Sources/Payments/Environment+PaymentAvailability.swift rename to Modules/InboxCore/Sources/ProtonMailUniffi/ApiEnvId+PaymentAvailability.swift index 321d589b40..850d620961 100644 --- a/Modules/App/Sources/Payments/Environment+PaymentAvailability.swift +++ b/Modules/InboxCore/Sources/ProtonMailUniffi/ApiEnvId+PaymentAvailability.swift @@ -1,3 +1,4 @@ +// // Copyright (c) 2025 Proton Technologies AG // // This file is part of Proton Mail. @@ -20,7 +21,7 @@ import proton_app_uniffi extension ApiEnvId { /// Payments are not available for sandbox users in production environment. - var arePaymentsEnabled: Bool { + public var arePaymentsEnabled: Bool { return !(isAppInstalledThroughTestFlight && self == .prod) } diff --git a/Modules/InboxTesting/Sources/MailSessionSpy.swift b/Modules/InboxTesting/Sources/MailSessionSpy.swift index 3fc5cff14c..ceaf23755e 100644 --- a/Modules/InboxTesting/Sources/MailSessionSpy.swift +++ b/Modules/InboxTesting/Sources/MailSessionSpy.swift @@ -21,6 +21,8 @@ public final class MailSessionSpy: MailSessionProtocol { public var onPrimaryAccountChanged: (@Sendable (String) -> Void)? public var appProtectionStub: AppProtection = .none + public var primaryUserSessionStub: MailUserSession? + public var storedSessions: [StoredSessionStub] = [] { didSet { Task { @@ -242,7 +244,7 @@ public final class MailSessionSpy: MailSessionProtocol { } public func toPrimaryUserSession() async -> MailSessionToPrimaryUserSessionResult { - fatalError(#function) + .ok(primaryUserSessionStub!) } public func toUserSession(ffiFlow: LoginFlow) async -> MailSessionToUserSessionResult { diff --git a/Modules/ShareExtension/Sources/ApiEnvId+Current.swift b/Modules/ShareExtension/Sources/ApiEnvId+Current.swift new file mode 100644 index 0000000000..c466a9a2e1 --- /dev/null +++ b/Modules/ShareExtension/Sources/ApiEnvId+Current.swift @@ -0,0 +1,31 @@ +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation +import proton_app_uniffi + +extension ApiEnvId { + static let current: Self = { + #if QA || DEBUG + if let dynamicDomain = UserDefaults.appGroup.string(forKey: "DYNAMIC_DOMAIN") { + return .init(dynamicDomain: dynamicDomain) + } + #endif + + return .prod + }() +} diff --git a/Modules/ShareExtension/Sources/ShareViewController.swift b/Modules/ShareExtension/Sources/ShareViewController.swift new file mode 100644 index 0000000000..8b6b7018d5 --- /dev/null +++ b/Modules/ShareExtension/Sources/ShareViewController.swift @@ -0,0 +1,64 @@ +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Combine +import InboxCore +import InboxCoreUI +import SwiftUI +import TestableShareExtension + +final class ShareViewController: UINavigationController { + private var cancellables = Set() + + override func beginRequest(with context: NSExtensionContext) { + super.beginRequest(with: context) + + let model = ShareScreenModel(apiEnvId: .current, extensionContext: context) + showMainScreen(basedOn: model) + setUpBindings(observing: model) + } + + override func viewDidLoad() { + DynamicFontSize.capSupportedSizeCategories() + + super.viewDidLoad() + + isNavigationBarHidden = true + } + + private func showMainScreen(basedOn model: ShareScreenModel) { + let screen = ShareScreen(model: model) + let hostingController = UIHostingController(rootView: screen) + setViewControllers([hostingController], animated: false) + } + + private func setUpBindings(observing model: ShareScreenModel) { + model.$alert.sink { [weak self] message in + guard let self else { return } + + if presentedViewController != nil { + dismiss(animated: true) + } + + if let message { + let alert = UIAlertController(title: message, message: nil, preferredStyle: .alert) + present(alert, animated: true) + } + } + .store(in: &cancellables) + } +} diff --git a/Modules/TestableShareExtension/Package.swift b/Modules/TestableShareExtension/Package.swift new file mode 100644 index 0000000000..1f2777cc33 --- /dev/null +++ b/Modules/TestableShareExtension/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "TestableShareExtension", + platforms: [.iOS(.v17), .macOS(.v14)], + products: [ + .library(name: "TestableShareExtension", targets: ["TestableShareExtension"]) + ], + dependencies: [ + .package(path: "../InboxComposer"), + .package(path: "../InboxKeychain"), + .package(path: "../InboxSnapshotTesting"), + .package(path: "../InboxTesting"), + ], + targets: [ + .target(name: "TestableShareExtension", dependencies: ["InboxComposer", "InboxKeychain"]), + .testTarget( + name: "ShareExtensionTests", + dependencies: ["InboxSnapshotTesting", "InboxTesting", "TestableShareExtension"] + ), + ] +) diff --git a/Modules/TestableShareExtension/Sources/Draft/DraftPrecomposer.swift b/Modules/TestableShareExtension/Sources/Draft/DraftPrecomposer.swift new file mode 100644 index 0000000000..b25e5d3b1d --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Draft/DraftPrecomposer.swift @@ -0,0 +1,94 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxComposer +import proton_app_uniffi +import UIKit +import UniformTypeIdentifiers + +enum DraftPrecomposer { + static func populate(draft: AppDraftProtocol, with sharedContent: SharedContent) async throws { + if let subject = sharedContent.subject { + try draft.setSubject(subject: subject).get() + } + + if let inlineImageHTML = try await add(attachments: sharedContent.attachments, to: draft.attachmentList()) { + try draft.prependToBody(text: inlineImageHTML.content) + } + + if let sharedBody = sharedContent.body { + try draft.prependToBody(text: sharedBody) + } + } + + private static func add(attachments: [NSItemProvider], to attachmentList: AttachmentListProtocol) async throws -> InlineImageHTML? { + let uploadFolder: URL = URL(fileURLWithPath: attachmentList.attachmentUploadDirectory()) + + var cids: [String] = [] + + for attachment in attachments { + let url = try await saveFileRepresentation(of: attachment, intoDirectory: uploadFolder) + let path = url.path(percentEncoded: false) + + if attachment.hasImageRepresentation { + if try url.isScreenshotInPlistFormat() { + try await extractImageContent(of: attachment, into: url) + } + + let cid = try await attachmentList.addInline(path: path, filenameOverride: nil).get() + cids.append(cid) + } else { + try await attachmentList.add(path: path, filenameOverride: nil).get() + } + } + + return cids.isEmpty ? nil : InlineImageHTML(cids: cids) + } + + private static func saveFileRepresentation(of attachment: NSItemProvider, intoDirectory persistentDirectory: URL) async throws -> URL { + try await attachment.performOnFileRepresentation { shortLivedURL in + try FileManager.default.moveToUniqueURL(file: shortLivedURL, to: persistentDirectory) + } + } + + private static func extractImageContent(of attachment: NSItemProvider, into url: URL) async throws { + let image = try await attachment.loadItem(forTypeIdentifier: UTType.image.identifier) as? UIImage + try image?.jpegData(compressionQuality: JPEG.compressionQuality)?.write(to: url) + } +} + +private extension AppDraftProtocol { + func prependToBody(text: String) throws { + let currentBody = body() + try setBody(body: text + currentBody).get() + } +} + +private extension URL { + func isScreenshotInPlistFormat() throws -> Bool { + let plistFileSignature = "bplist00".data(using: .ascii)! + let handle = try FileHandle(forReadingFrom: self) + + defer { + try? handle.close() + } + + let fileSignature = try handle.read(upToCount: plistFileSignature.count) + return fileSignature == plistFileSignature + } +} diff --git a/Modules/TestableShareExtension/Sources/Draft/SharedItemsParser.swift b/Modules/TestableShareExtension/Sources/Draft/SharedItemsParser.swift new file mode 100644 index 0000000000..b9d83cb341 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Draft/SharedItemsParser.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation +import InboxCore +import UniformTypeIdentifiers + +struct SharedContent { + let subject: String? + let body: String? + let attachments: [NSItemProvider] +} + +enum SharedItemsParser { + static func parse(extensionItems: [NSExtensionItem]) async throws -> SharedContent { + for extensionItem in extensionItems { + guard let attachments = extensionItem.attachments else { continue } + + let registeredTypeIdentifiers = attachments.flatMap(\.registeredTypeIdentifiers) + let isSharingSafariPage = registeredTypeIdentifiers == [UTType.url.identifier] + let isSharingTextFromSelection = registeredTypeIdentifiers == [UTType.plainText.identifier] + + if isSharingSafariPage { + let link = try await attachments[0].loadString() + let body = "\(link)" + return .init(subject: extensionItem.attributedContentText?.string, body: body, attachments: []) + } else if isSharingTextFromSelection { + return .init(subject: nil, body: extensionItem.attributedContentText?.string, attachments: []) + } + } + + let allAttachments = extensionItems.compactMap(\.attachments).flatMap(\.self) + return .init(subject: nil, body: nil, attachments: allAttachments) + } +} diff --git a/Modules/TestableShareExtension/Sources/Payments/UpsellConfiguration.swift b/Modules/TestableShareExtension/Sources/Payments/UpsellConfiguration.swift new file mode 100644 index 0000000000..fef2845e02 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Payments/UpsellConfiguration.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxIAP +import proton_app_uniffi + +extension UpsellConfiguration { + /// The upsell screen should always show this particular plan. + static func mail(apiEnvId: ApiEnvId) -> Self { + .init(planName: "mail2022", arePaymentsEnabled: apiEnvId.arePaymentsEnabled) + } +} diff --git a/Modules/TestableShareExtension/Sources/UI/ErrorScreen.swift b/Modules/TestableShareExtension/Sources/UI/ErrorScreen.swift new file mode 100644 index 0000000000..d6e24046f1 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/UI/ErrorScreen.swift @@ -0,0 +1,87 @@ +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxCore +import InboxCoreUI +import InboxDesignSystem +import proton_app_uniffi +import SwiftUI + +struct ErrorScreen: View { + @Environment(\.openURL) private var openURL + + let error: Error + let dismissExtension: () -> Void + + init(error: any Error, dismissExtension: @escaping () -> Void) { + self.error = error + self.dismissExtension = dismissExtension + } + + var body: some View { + VStack(spacing: DS.Spacing.extraLarge) { + Spacer() + + Text(errorMessage) + .multilineTextAlignment(.center) + + Spacer() + + if error.shouldPromptToSignIn { + Button(L10n.openApp.string) { + openURL(URL(string: "\(Bundle.URLScheme.protonmail):")!) + dismissExtension() + } + } + + Button(CommonL10n.cancel.string) { + dismissExtension() + } + } + .buttonStyle(BigButtonStyle()) + .padding(DS.Spacing.huge) + .padding(.vertical, DS.Spacing.extraLarge) + .background(DS.Color.BackgroundInverted.norm) + } + + private var errorMessage: String { + if error.shouldPromptToSignIn { + L10n.needToSignIn.string + } else { + error.localizedDescription + } + } +} + +private extension Error { + var shouldPromptToSignIn: Bool { + switch self as? UserSessionError { + case .reason(.userSessionNotInitialized): + true + default: + false + } + } +} + +#Preview("with sign-in option") { + ErrorScreen(error: UserSessionError.reason(.userSessionNotInitialized), dismissExtension: {}) +} + +#Preview("without sign-in option") { + ErrorScreen(error: NSError(domain: "", code: 0), dismissExtension: {}) +} diff --git a/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreen.swift b/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreen.swift new file mode 100644 index 0000000000..1d4af81e5b --- /dev/null +++ b/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreen.swift @@ -0,0 +1,74 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxComposer +import InboxCoreUI +import proton_app_uniffi +import SwiftUI + +public struct ShareScreen: View { + @ObservedObject private var model: ShareScreenModel + @StateObject private var toastStateStore = ToastStateStore(initialState: .initial) + + public init(model: ShareScreenModel) { + self.model = model + } + + public var body: some View { + switch model.state { + case .preparing: + Color.clear + .task { + await model.prepare() + } + case .locked(let lockScreenType, let mailSession): + LockScreen( + state: .init(type: lockScreenType), + mailSession: mailSession as! LockScreen.MailSessionType, + dismissLock: { + Task { + await model.onAppUnlocked() + } + } + ) + .padding(.top) + case .composing(let draft, let dependencies, let upsellCoordinator): + ComposerScreen( + draft: draft, + draftOrigin: .new, + dependencies: dependencies, + isAddingAttachmentsEnabled: false, + onDismiss: { reason in + model.onComposerDismissed(reason: reason) + } + ) + .overlay { + ToastSceneView() + } + .environmentObject(toastStateStore) + .environmentObject(upsellCoordinator) + case .error(let error): + ErrorScreen( + error: error, + dismissExtension: { + model.dismissShareExtension(error: error) + }, + ) + } + } +} diff --git a/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreenModel.swift b/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreenModel.swift new file mode 100644 index 0000000000..218911d05e --- /dev/null +++ b/Modules/TestableShareExtension/Sources/UI/ShareScreen/ShareScreenModel.swift @@ -0,0 +1,168 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxComposer +import InboxCore +import InboxCoreUI +import InboxIAP +import proton_app_uniffi +import SwiftUI + +@MainActor +public final class ShareScreenModel: ObservableObject { + typealias MakeNewDraft = (MailUserSession, DraftCreateMode) async throws -> AppDraftProtocol + + enum ViewState { + case preparing + case composing(AppDraftProtocol, ComposerScreen.Dependencies, UpsellCoordinator) + case locked(LockScreenState.LockScreenType, MailSessionProtocol) + case error(Error) + } + + @Published private(set) var state: ViewState = .preparing + @Published private(set) public var alert: String? + + private let extensionContext: NSExtensionContext + private let makeNewDraft: MakeNewDraft + private let sessionHolder: SessionHolder + private let upsellConfiguration: UpsellConfiguration + + public convenience init(apiEnvId: ApiEnvId, extensionContext: NSExtensionContext) { + self.init( + apiEnvId: apiEnvId, + extensionContext: extensionContext, + makeMailSession: { try createMailSession(params: $0, keyChain: $1, hvNotifier: $2, deviceInfoProvider: $3).get() }, + makeNewDraft: { try await newDraft(session: $0, createMode: $1).get() } + ) + } + + init( + apiEnvId: ApiEnvId, + extensionContext: NSExtensionContext, + makeMailSession: @escaping SessionHolder.MakeMailSession, + makeNewDraft: @escaping MakeNewDraft + ) { + self.extensionContext = extensionContext + self.makeNewDraft = makeNewDraft + sessionHolder = .init(apiEnvId: apiEnvId, makeMailSession: makeMailSession) + upsellConfiguration = .mail(apiEnvId: apiEnvId) + } + + func prepare() async { + do { + let mailSession = try sessionHolder.mailSession() + let appProtection = try await mailSession.appProtection().get() + + if let lockScreenType = appProtection.lockScreenType { + state = .locked(lockScreenType, mailSession) + } else { + await onAppUnlocked() + } + } catch { + AppLogger.log(error: error, category: .shareExtension) + state = .error(error) + } + } + + func onAppUnlocked() async { + do { + let userSession = try await sessionHolder.primaryUserSession() + let draft = try await prepareDraft(userSession: userSession) + + let dependencies = ComposerScreen.Dependencies( + contactProvider: .productionInstance(session: userSession), + userSession: userSession + ) + + let upsellCoordinator = UpsellCoordinator(mailUserSession: userSession, configuration: upsellConfiguration) + + state = .composing(draft, dependencies, upsellCoordinator) + } catch { + AppLogger.log(error: error, category: .shareExtension) + state = .error(error) + } + } + + func onComposerDismissed(reason: ComposerDismissReason) { + switch reason { + case .dismissedManually, .draftDiscarded: + dismissShareExtension(error: NSError.userCancelled) + case .messageScheduled(let messageID), .messageSent(let messageID): + alert = L10n.Sending.sendingInProgress.string + + Task { + do { + try await waitUntilMessageSendingIsFinished(messageID: messageID) + alert = L10n.Sending.messageSent.string + } catch { + AppLogger.log(error: error, category: .shareExtension) + alert = error.localizedDescription + } + + try? await Task.sleep(for: .seconds(2)) + + dismissShareExtension(error: nil) + } + } + } + + func dismissShareExtension(error: Error?) { + if let error { + AppLogger.log(message: "Sharing cancelled", category: .shareExtension) + extensionContext.cancelRequest(withError: error) + } else { + extensionContext.completeRequest(returningItems: nil) { expired in + if expired { + AppLogger.log(message: "Sharing interrupted", category: .shareExtension, isError: true) + } else { + AppLogger.log(message: "Sharing completed", category: .shareExtension) + } + } + } + } + + private func prepareDraft(userSession: MailUserSession) async throws -> AppDraftProtocol { + let draft = try await makeNewDraft(userSession, .empty) + + let inputItems = extensionContext.inputItems.map { $0 as! NSExtensionItem } + let sharedContent = try await SharedItemsParser.parse(extensionItems: inputItems) + try await DraftPrecomposer.populate(draft: draft, with: sharedContent) + + return draft + } + + private func waitUntilMessageSendingIsFinished(messageID: ID) async throws { + let userSession = try await sessionHolder.primaryUserSession() + let sendResultPublisher = SendResultPublisher(userSession: userSession) + + for await sendResultInfo in sendResultPublisher.results.values where sendResultInfo.messageId == messageID { + switch sendResultInfo.type { + case .scheduling, .sending: + break + case .scheduled, .sent: + return + case .error(let error): + throw error + } + } + } +} + +private extension NSError { + static let userCancelled = NSError(domain: Bundle.main.bundleIdentifier!, code: NSUserCancelledError) +} diff --git a/Modules/TestableShareExtension/Sources/Uniffi/SessionHolder.swift b/Modules/TestableShareExtension/Sources/Uniffi/SessionHolder.swift new file mode 100644 index 0000000000..0c1b2c7b89 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Uniffi/SessionHolder.swift @@ -0,0 +1,64 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import AccountChallenge +import InboxCore +import InboxKeychain +import proton_app_uniffi + +/** + The purpose of this class is to guarantee that MailSession and the primary MailUserSession are: + - only created once + - retained for the entire lifetime of the Share extension + */ +final class SessionHolder { + typealias MakeMailSession = (MailSessionParams, OsKeyChain, ChallengeNotifier?, DeviceInfoProvider?) throws -> MailSessionProtocol + + private let apiEnvId: ApiEnvId + private let makeMailSession: MakeMailSession + + private var cachedMailSession: MailSessionProtocol? + private var cachedUserSession: MailUserSession? + + init(apiEnvId: ApiEnvId, makeMailSession: @escaping MakeMailSession) { + self.apiEnvId = apiEnvId + self.makeMailSession = makeMailSession + } + + func mailSession() throws -> MailSessionProtocol { + if let cachedMailSession { + return cachedMailSession + } else { + let apiConfig = ApiConfig(envId: apiEnvId) + let params = MailSessionParamsFactory.make(origin: .iosShareExt, apiConfig: apiConfig) + let newMailSession = try makeMailSession(params, KeychainSDKWrapper(), nil, ChallengePayloadProvider()) + cachedMailSession = newMailSession + return newMailSession + } + } + + func primaryUserSession() async throws -> MailUserSession { + if let cachedUserSession { + return cachedUserSession + } else { + let newUserSession = try await mailSession().toPrimaryUserSession().get() + cachedUserSession = newUserSession + return newUserSession + } + } +} diff --git a/Modules/TestableShareExtension/Sources/Utils/L10n.swift b/Modules/TestableShareExtension/Sources/Utils/L10n.swift new file mode 100644 index 0000000000..0cbbf36b89 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Utils/L10n.swift @@ -0,0 +1,31 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation + +enum L10n { + enum Sending { + static let sendingInProgress = LocalizedStringResource("Sending...", bundle: .module, comment: "Alert shown while the message is being sent") + + static let messageSent = LocalizedStringResource("Message sent!", bundle: .module, comment: "Alert shown after the message has been sent") + } + + static let needToSignIn = LocalizedStringResource("You need to sign-in to Proton Mail to share content.", bundle: .module, comment: "Error message when attempting to use the Share extension without being logged in") + + static let openApp = LocalizedStringResource("Open Proton Mail", bundle: .module, comment: "Button to open the main app from the Share extension") +} diff --git a/Modules/TestableShareExtension/Sources/Utils/NSItemProvider+Extensions.swift b/Modules/TestableShareExtension/Sources/Utils/NSItemProvider+Extensions.swift new file mode 100644 index 0000000000..315d77fff5 --- /dev/null +++ b/Modules/TestableShareExtension/Sources/Utils/NSItemProvider+Extensions.swift @@ -0,0 +1,54 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import UIKit +import UniformTypeIdentifiers + +extension NSItemProvider { + var hasImageRepresentation: Bool { + hasItemConformingToTypeIdentifier(UTType.image.identifier) + } + + func loadString() async throws -> String { + try await withCheckedThrowingContinuation { continuation in + _ = loadObject(ofClass: String.self) { value, error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: value!) + } + } + } + } + + func performOnFileRepresentation(block: @escaping (URL) throws -> T) async throws -> T { + try await withCheckedThrowingContinuation { continuation in + _ = loadFileRepresentation(for: .data) { shortLivedURL, _, error in + continuation.resume( + with: .init { + guard let shortLivedURL else { + throw error! + } + + return try block(shortLivedURL) + } + ) + } + } + } +} diff --git a/Modules/TestableShareExtension/Tests/Doubles/ExtensionContextSpy.swift b/Modules/TestableShareExtension/Tests/Doubles/ExtensionContextSpy.swift new file mode 100644 index 0000000000..52febbcdbf --- /dev/null +++ b/Modules/TestableShareExtension/Tests/Doubles/ExtensionContextSpy.swift @@ -0,0 +1,35 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation + +final class ExtensionContextSpy: NSExtensionContext { + var stubbedExpirationFlag = false + + private(set) var cancelRequestInvocations: [Error] = [] + private(set) var completeRequestInvocations: [[Any]?] = [] + + override func cancelRequest(withError error: any Error) { + cancelRequestInvocations.append(error) + } + + override func completeRequest(returningItems items: [Any]?, completionHandler: ((Bool) -> Void)? = nil) { + completeRequestInvocations.append(items) + completionHandler?(stubbedExpirationFlag) + } +} diff --git a/Modules/TestableShareExtension/Tests/Draft/DraftPrecomposerTests.swift b/Modules/TestableShareExtension/Tests/Draft/DraftPrecomposerTests.swift new file mode 100644 index 0000000000..ab5b3b41a9 --- /dev/null +++ b/Modules/TestableShareExtension/Tests/Draft/DraftPrecomposerTests.swift @@ -0,0 +1,164 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation +import Testing +import UIKit +import UniformTypeIdentifiers + +@testable import InboxComposer +@testable import TestableShareExtension + +final class DraftPrecomposerTests { + private let sut = DraftPrecomposer.self + private let draft = MockDraft.emptyMockDraft + private let fileManager = FileManager.default + private let testDir: URL + private let attachmentSourceDir: URL + private let attachmentUploadDir: URL + + init() throws { + testDir = fileManager.temporaryDirectory.appending(path: UUID().uuidString, directoryHint: .isDirectory) + attachmentSourceDir = testDir.appending(path: "attachment_source", directoryHint: .isDirectory) + attachmentUploadDir = testDir.appending(path: "attachment_upload", directoryHint: .isDirectory) + + for dir in [attachmentSourceDir, attachmentUploadDir] { + try fileManager.createDirectory(at: dir, withIntermediateDirectories: true) + } + + draft.mockAttachmentList.attachmentUploadDirectoryURL = attachmentUploadDir + } + + deinit { + try! fileManager.removeItem(at: testDir) + } + + @Test + func populatingDraftWithoutAttachments() async throws { + let sharedContent = SharedContent( + subject: "A subject", + body: "Some body", + attachments: [] + ) + + try await sut.populate(draft: draft, with: sharedContent) + + #expect(draft.subject() == "A subject") + #expect(draft.body() == "Some body") + #expect(draft.mockAttachmentList.capturedAddCalls.count == 0) + } + + @Test + func movesNonInlineAttachmentsToUploadDirectoryBeforeAdding() async throws { + let sourceURLs = (0..<3).map { index in + attachmentSourceDir.appending(path: "data-\(index).txt") + } + + let sharedContent = SharedContent( + subject: nil, + body: nil, + attachments: try TestDataFactory.stubShortLivedData(in: sourceURLs) + ) + + try await sut.populate(draft: draft, with: sharedContent) + + let expectedAttachmentPaths = sourceURLs.map { + attachmentUploadDir.appending(path: $0.lastPathComponent).path() + } + + #expect(Set(draft.mockAttachmentList.capturedAddCalls.map(\.path)) == Set(expectedAttachmentPaths)) + + for path in expectedAttachmentPaths { + #expect(FileManager.default.fileExists(atPath: path)) + } + } + + @Test + func insertsInlineAttachmentReferencesInBody() async throws { + let sourceURLs = (0..<3).map { index in + attachmentSourceDir.appending(path: "image-\(index).png") + } + + let sharedContent = SharedContent( + subject: nil, + body: "Some body", + attachments: try TestDataFactory.stubImages(in: sourceURLs) + ) + + try await sut.populate(draft: draft, with: sharedContent) + + let expectedBody = """ + Some body


+ """ + #expect(draft.body() == expectedBody) + + let expectedAttachmentPaths = sourceURLs.map { + attachmentUploadDir.appending(path: $0.lastPathComponent).path() + } + + #expect(Set(draft.mockAttachmentList.capturedAddInlineCalls.map(\.path)) == Set(expectedAttachmentPaths)) + } + + @Test + func extractsImagesFromScreenshotPlists() async throws { + let sourceURLs = (0..<3).map { index in + attachmentSourceDir.appending(path: "image-\(index).png") + } + + let sharedContent = SharedContent( + subject: nil, + body: "Some body", + attachments: try TestDataFactory.stubScreenshots(in: sourceURLs) + ) + + try await sut.populate(draft: draft, with: sharedContent) + + let expectedBody = """ + Some body


+ """ + #expect(draft.body() == expectedBody) + + let expectedAttachmentPaths = sourceURLs.map { + attachmentUploadDir.appending(path: $0.lastPathComponent) + } + + #expect(expectedAttachmentPaths.count == 3) + + for url in expectedAttachmentPaths { + let data = try Data(contentsOf: url) + #expect(UIImage(data: data) != nil) + } + } + + @Test + func handlesAttachmentLoadingFailures() async throws { + let sharedContent = SharedContent( + subject: nil, + body: "Some body", + attachments: [TestDataFactory.stubError()] + ) + + await #expect(throws: NSError.self) { + try await self.sut.populate(draft: self.draft, with: sharedContent) + } + + #expect(draft.body() == .empty) + #expect(draft.mockAttachmentList.capturedAddCalls.count == 0) + #expect(draft.mockAttachmentList.capturedAddInlineCalls.count == 0) + } +} diff --git a/Modules/TestableShareExtension/Tests/Draft/SharedItemsParserTests.swift b/Modules/TestableShareExtension/Tests/Draft/SharedItemsParserTests.swift new file mode 100644 index 0000000000..8077bf4848 --- /dev/null +++ b/Modules/TestableShareExtension/Tests/Draft/SharedItemsParserTests.swift @@ -0,0 +1,108 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Testing +import UniformTypeIdentifiers + +@testable import TestableShareExtension + +final class SharedItemsParserTests { + private let sut = SharedItemsParser.self + + @Test + func testSharingSeveralImagesFromPhotosApp() async throws { + let extensionItems = emulateSharingSeveralImagesFromPhotosApp(count: 5) + + let sharedContent = try await sut.parse(extensionItems: extensionItems) + + #expect(sharedContent.subject == nil) + #expect(sharedContent.body == nil) + #expect(sharedContent.attachments == extensionItems[0].attachments) + } + + @Test + func testSharingSeveralImagesAndFilesFromFilesApp() async throws { + let extensionItems = emulateSharingSeveralImagesAndFilesFromFilesApp(imageCount: 3, fileCount: 2) + + let sharedContent = try await sut.parse(extensionItems: extensionItems) + + #expect(sharedContent.subject == nil) + #expect(sharedContent.body == nil) + #expect(sharedContent.attachments == extensionItems[0].attachments) + } + + @Test + func testSharingSafariPage() async throws { + let extensionItems = emulateSharingSafariPage( + url: URL(string: "https://example.com")!, + pageTitle: "An example webpage" + ) + + let sharedContent = try await sut.parse(extensionItems: extensionItems) + + #expect(sharedContent.subject == "An example webpage") + #expect(sharedContent.body == #"https://example.com"#) + #expect(sharedContent.attachments == []) + } + + @Test + func testSharingSelectedText() async throws { + let extensionItems = emulateSharing(selectedText: "Lorem ipsum") + + let sharedContent = try await sut.parse(extensionItems: extensionItems) + + #expect(sharedContent.subject == nil) + #expect(sharedContent.body == "Lorem ipsum") + #expect(sharedContent.attachments == []) + } + + // MARK: realistic scenarios + + private func emulateSharingSeveralImagesFromPhotosApp(count: UInt) -> [NSExtensionItem] { + let extensionItem = NSExtensionItem() + extensionItem.attachments = TestDataFactory.makeItemProviders(types: [.jpeg, .heic], count: count) + return [extensionItem] + } + + private func emulateSharingSeveralImagesAndFilesFromFilesApp(imageCount: UInt, fileCount: UInt) -> [NSExtensionItem] { + let images = TestDataFactory.makeItemProviders(types: [.heic, .fileURL], count: imageCount) + let files = TestDataFactory.makeItemProviders(types: [.plainText, .fileURL], count: fileCount) + + let extensionItem = NSExtensionItem() + extensionItem.attachments = images + files + return [extensionItem] + } + + private func emulateSharingSafariPage(url: URL, pageTitle: String) -> [NSExtensionItem] { + let extensionItem = NSExtensionItem() + extensionItem.attributedContentText = .init(string: pageTitle) + extensionItem.attachments = [ + .init(item: url as NSSecureCoding, typeIdentifier: UTType.url.identifier) + ] + return [extensionItem] + } + + private func emulateSharing(selectedText: String) -> [NSExtensionItem] { + let extensionItem = NSExtensionItem() + extensionItem.attributedContentText = .init(string: selectedText) + extensionItem.attachments = [ + .init(item: "this is irrelevant" as NSSecureCoding, typeIdentifier: UTType.plainText.identifier) + ] + return [extensionItem] + } +} diff --git a/Modules/TestableShareExtension/Tests/TestDataFactory.swift b/Modules/TestableShareExtension/Tests/TestDataFactory.swift new file mode 100644 index 0000000000..97614b8eca --- /dev/null +++ b/Modules/TestableShareExtension/Tests/TestDataFactory.swift @@ -0,0 +1,109 @@ +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation +import UIKit +import UniformTypeIdentifiers + +enum TestDataFactory { + static func makeItemProviders(types: [UTType], count: UInt) -> [NSItemProvider] { + (0.. [NSItemProvider] { + let data = Data("foo".utf8) + + return try urls.map { url in + try data.write(to: url) + + let itemProvider = NSItemProvider() + + itemProvider.registerFileRepresentation(for: .data) { completion in + completion(url, false, nil) + try! FileManager.default.removeItem(at: url) + return nil + } + + return itemProvider + } + } + + static func stubImages(in urls: [URL]) throws -> [NSItemProvider] { + let image = UIImage(systemName: "checkmark")! + + return try urls.map { url in + try image.pngData()!.write(to: url) + + let itemProvider = NSItemProvider() + + itemProvider.registerFileRepresentation(for: .image) { completion in + completion(url, true, nil) + return nil + } + + return itemProvider + } + } + + static func stubScreenshots(in urls: [URL]) throws -> [NSItemProvider] { + let image = UIImage(systemName: "checkmark")! + + let plist: [String: Any] = ["foo": "bar"] + let plistData = try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0) + + return try urls.map { url in + try plistData.write(to: url) + + let itemProvider = NSItemProvider() + + itemProvider.registerFileRepresentation(for: .data) { completion in + completion(url, false, nil) + return nil + } + + itemProvider.registerItem(forTypeIdentifier: UTType.image.identifier) { completion, _, _ in + completion!(image, nil) + } + + return itemProvider + } + } + + static func stubError() -> NSItemProvider { + let itemProvider = NSItemProvider() + + itemProvider.registerFileRepresentation(for: .data) { completion in + completion(nil, false, TestError()) + return nil + } + + return itemProvider + } +} diff --git a/Modules/TestableShareExtension/Tests/TestError.swift b/Modules/TestableShareExtension/Tests/TestError.swift new file mode 100644 index 0000000000..cc8ac869d4 --- /dev/null +++ b/Modules/TestableShareExtension/Tests/TestError.swift @@ -0,0 +1,23 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import Foundation + +struct TestError: LocalizedError, Equatable { + let errorDescription: String? = "Something failed." +} diff --git a/Modules/TestableShareExtension/Tests/UI/ErrorViewSnapshotTests.swift b/Modules/TestableShareExtension/Tests/UI/ErrorViewSnapshotTests.swift new file mode 100644 index 0000000000..62030c4a1e --- /dev/null +++ b/Modules/TestableShareExtension/Tests/UI/ErrorViewSnapshotTests.swift @@ -0,0 +1,38 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxSnapshotTesting +import proton_app_uniffi +import Testing + +@testable import TestableShareExtension + +@MainActor +struct ErrorScreenSnapshotTests { + @Test + func withButtonToOpenApp() { + let sut = ErrorScreen(error: UserSessionError.reason(.userSessionNotInitialized), dismissExtension: {}) + assertSnapshotsOnIPhoneX(of: sut) + } + + @Test + func withoutButtonToOpenApp() { + let sut = ErrorScreen(error: TestError(), dismissExtension: {}) + assertSnapshotsOnIPhoneX(of: sut) + } +} diff --git a/Modules/TestableShareExtension/Tests/UI/ShareScreen/ShareScreenModelTests.swift b/Modules/TestableShareExtension/Tests/UI/ShareScreen/ShareScreenModelTests.swift new file mode 100644 index 0000000000..e7cc92aa41 --- /dev/null +++ b/Modules/TestableShareExtension/Tests/UI/ShareScreen/ShareScreenModelTests.swift @@ -0,0 +1,124 @@ +// +// Copyright (c) 2025 Proton Technologies AG +// +// This file is part of Proton Mail. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail. If not, see https://www.gnu.org/licenses/. + +import InboxTesting +import proton_app_uniffi +import Testing + +@testable import InboxComposer +@testable import TestableShareExtension + +@MainActor +final class ShareScreenModelTests { + private let extensionContext = ExtensionContextSpy() + private let mailSession = MailSessionSpy() + private var stubbedMailSessionResult: Result + private var stubbedNewDraftResult: Result + + private lazy var sut = ShareScreenModel( + apiEnvId: .atlas, + extensionContext: extensionContext, + makeMailSession: { [unowned self] _, _, _, _ in + try stubbedMailSessionResult.get() + }, + makeNewDraft: { [unowned self] _, _ in + try stubbedNewDraftResult.get() + } + ) + + init() { + mailSession.primaryUserSessionStub = MailUserSessionSpy(id: "") + stubbedMailSessionResult = .success(mailSession) + stubbedNewDraftResult = .success(MockDraft.emptyMockDraft) + } + + @Test(arguments: [AppProtection.biometrics, .pin]) + func showsLockScreenIfAppProtectionIsSet(appProtection: AppProtection) async { + mailSession.appProtectionStub = appProtection + + await sut.prepare() + + switch sut.state { + case .locked(appProtection.lockScreenType, _): + break + default: + Issue.record("unexpected state: \(sut.state)") + } + } + + @Test + func showsComposerIfAppProtectionIsNotSet() async { + mailSession.appProtectionStub = .none + + await sut.prepare() + + switch sut.state { + case .composing: + break + default: + Issue.record("unexpected state: \(sut.state)") + } + } + + @Test + func showsErrorScreenIfInitialSetupFails() async throws { + stubbedMailSessionResult = .failure(TestError()) + + await sut.prepare() + + switch sut.state { + case .error(_ as TestError): + break + default: + Issue.record("unexpected state: \(sut.state)") + } + } + + @Test + func showsErrorScreenIfPreparingComposerScreenFails() async throws { + stubbedNewDraftResult = .failure(TestError()) + + await sut.prepare() + + switch sut.state { + case .error(_ as TestError): + break + default: + Issue.record("unexpected state: \(sut.state)") + } + } + + @Test + func dismissingWithErrorCancelsTheRequestOfTheContext() { + sut.dismissShareExtension(error: TestError()) + + #expect(extensionContext.cancelRequestInvocations.count == 1) + #expect(extensionContext.cancelRequestInvocations.first is TestError) + #expect(extensionContext.completeRequestInvocations.count == 0) + } + + @Test(arguments: [true, false]) + func dismissingWithoutErrorCompletesTheRequestOfTheContext(expirationFlag: Bool) { + extensionContext.stubbedExpirationFlag = expirationFlag + + sut.dismissShareExtension(error: nil) + + #expect(extensionContext.completeRequestInvocations.count == 1) + #expect(extensionContext.cancelRequestInvocations.count == 0) + } +} diff --git a/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.dark.png b/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.dark.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b7e9cf6531340478e53ae953c3467473cd7ed8 GIT binary patch literal 93097 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%0A~DXXr2I`25@ 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%0A6Gq-yNZlkzLs(4tM>X{G$>it$N{f3p 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 literal 0 HcmV?d00001 diff --git a/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.light.png b/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withButtonToOpenApp.light.png new file mode 100644 index 0000000000000000000000000000000000000000..2883c085ba035c69b3593d3fdb8fee8891b2424a GIT binary patch literal 92354 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 literal 0 HcmV?d00001 diff --git a/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withoutButtonToOpenApp.dark.png b/Modules/TestableShareExtension/Tests/UI/__Snapshots__/ErrorViewSnapshotTests/withoutButtonToOpenApp.dark.png new file mode 100644 index 0000000000000000000000000000000000000000..dd058371aa0f56543f60ab94663deb132831994d GIT binary patch literal 75728 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 KpUXO@geCx@i;)-r literal 0 HcmV?d00001 diff --git a/ShareExtension.entitlements b/ShareExtension.entitlements new file mode 100644 index 0000000000..c73861420b --- /dev/null +++ b/ShareExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.me.proton.mail + + + diff --git a/TestPlans/AllUnitAndSnapshotTests.xctestplan b/TestPlans/AllUnitAndSnapshotTests.xctestplan index df9bf82a48..4b98b8e4e0 100644 --- a/TestPlans/AllUnitAndSnapshotTests.xctestplan +++ b/TestPlans/AllUnitAndSnapshotTests.xctestplan @@ -20,6 +20,13 @@ "testTimeoutsEnabled" : true }, "testTargets" : [ + { + "target" : { + "containerPath" : "container:Modules\/InboxIAP", + "identifier" : "InboxIAPTests", + "name" : "InboxIAPTests" + } + }, { "target" : { "containerPath" : "container:Modules\/InboxCoreUI", @@ -29,9 +36,16 @@ }, { "target" : { - "containerPath" : "container:Modules\/InboxComposer", - "identifier" : "InboxComposerTests", - "name" : "InboxComposerTests" + "containerPath" : "container:Modules\/TestableShareExtension", + "identifier" : "ShareExtensionTests", + "name" : "ShareExtensionTests" + } + }, + { + "target" : { + "containerPath" : "container:Modules\/TestableNotificationService", + "identifier" : "NotificationServiceTests", + "name" : "NotificationServiceTests" } }, { @@ -41,6 +55,13 @@ "name" : "InboxRSVPTests" } }, + { + "target" : { + "containerPath" : "container:Modules\/InboxComposer", + "identifier" : "InboxComposerTests", + "name" : "InboxComposerTests" + } + }, { "target" : { "containerPath" : "container:Modules\/InboxKeychain", @@ -55,20 +76,6 @@ "name" : "ProtonMailTest" } }, - { - "target" : { - "containerPath" : "container:Modules\/InboxIAP", - "identifier" : "InboxIAPTests", - "name" : "InboxIAPTests" - } - }, - { - "target" : { - "containerPath" : "container:Modules\/TestableNotificationService", - "identifier" : "NotificationServiceTests", - "name" : "NotificationServiceTests" - } - }, { "target" : { "containerPath" : "container:Modules\/InboxContacts", diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 6989262137..84b4906d38 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -4,6 +4,7 @@ default_platform(:ios) APP_IDENTIFIER = "ch.protonmail.protonmail" NOTIFICATION_EXTENSION_IDENTIFIER = "ch.protonmail.protonmail.notifications" +SHARE_EXTENSION_IDENTIFIER = "ch.protonmail.protonmail.Share" DEVELOPER_KEYCHAIN_NAME = "PROTONMAIL_IOS_CERTIFICATE_KEYCHAIN" DEVELOPER_KEYCHAIN_PASSWORD = "QrniqyS3LWTH3Ji" CERTIFICATE_PATH = "fastlane/Certificates.p12" @@ -54,7 +55,8 @@ platform :ios do def get_xcode_profile ids = [ APP_IDENTIFIER, - NOTIFICATION_EXTENSION_IDENTIFIER + NOTIFICATION_EXTENSION_IDENTIFIER, + SHARE_EXTENSION_IDENTIFIER ] ids.each do |id| diff --git a/project.yml b/project.yml index 89dbd3ad3a..f143012bd0 100644 --- a/project.yml +++ b/project.yml @@ -74,6 +74,9 @@ packages: TestableNotificationService: path: Modules/TestableNotificationService group: Modules + TestableShareExtension: + path: Modules/TestableShareExtension + group: Modules TryCatch: path: Modules/TryCatch group: Modules @@ -100,6 +103,33 @@ settings: QA: SWIFT_ACTIVE_COMPILATION_CONDITIONS: "QA" +targetTemplates: + Extension: + type: app-extension + platform: iOS + scheme: {} + sources: + - path: Modules/${target_name}/Sources + settings: + base: + CODE_SIGN_IDENTITY: "iPhone Distribution: Proton AG (2SB5Z68H26)" + CODE_SIGN_ENTITLEMENTS: ${target_name}.entitlements + PRODUCT_BUNDLE_IDENTIFIER: ${bundle_identifier} + PROVISIONING_PROFILE_SPECIFIER: ${release_provisioning_profile} + configs: + Debug: + CODE_SIGN_IDENTITY: "Apple Development" + CODE_SIGN_STYLE: Automatic + PROVISIONING_PROFILE_SPECIFIER: "" + info: + path: Modules/${target_name}/Sources/SupportingFiles/Info.plist + properties: + CFBundleDisplayName: "$(BUNDLE_DISPLAY_NAME)" + CFBundleShortVersionString: "$(MARKETING_VERSION)" + NSExtension: + NSExtensionPointIdentifier: ${point_identifier} + NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).${principal_class} + targets: ProtonMail: type: application @@ -179,6 +209,7 @@ targets: - AccountManager - AccountPassword - package: Scrypt + - target: ShareExtension - package: SwiftUIIntrospect preBuildScripts: - name: swift-format @@ -227,31 +258,40 @@ targets: scheme: ProtonMailUITest NotificationService: - type: app-extension - platform: iOS + templates: + - Extension + templateAttributes: + bundle_identifier: ch.protonmail.protonmail.notifications + principal_class: NotificationService + point_identifier: com.apple.usernotifications.service + release_provisioning_profile: "Mail - Notification Service Distribution" dependencies: - package: TestableNotificationService - scheme: {} - sources: - - path: Modules/NotificationService/Sources - settings: - base: - CODE_SIGN_ENTITLEMENTS: "NotificationService.entitlements" - CODE_SIGN_IDENTITY: "iPhone Distribution: Proton AG (2SB5Z68H26)" - PRODUCT_BUNDLE_IDENTIFIER: "ch.protonmail.protonmail.notifications" - PROVISIONING_PROFILE_SPECIFIER: "Mail - Notification Service Distribution" - configs: - Debug: - CODE_SIGN_IDENTITY: "Apple Development" - CODE_SIGN_STYLE: Automatic - PROVISIONING_PROFILE_SPECIFIER: "" + + ShareExtension: + templates: + - Extension + templateAttributes: + bundle_identifier: ch.protonmail.protonmail.Share + principal_class: ShareViewController + point_identifier: com.apple.share-services + release_provisioning_profile: "Protonmail share release" + dependencies: + - package: TestableShareExtension info: - path: Modules/NotificationService/Sources/SupportingFiles/Info.plist properties: - CFBundleDisplayName: "$(BUNDLE_DISPLAY_NAME)" NSExtension: - NSExtensionPointIdentifier: 'com.apple.usernotifications.service' - NSExtensionPrincipalClass: '$(PRODUCT_MODULE_NAME).NotificationService' + NSExtensionAttributes: + NSExtensionActivationRule: > + SUBQUERY ( + extensionItems, + $extensionItem, + SUBQUERY ( + $extensionItem.attachments, + $attachment, + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data" + ).@count > 0 + ).@count > 0 schemes: ProtonMailUITest: