Files
AFFiNE-Mirror/packages/frontend/apps/ios/App/ShareExtension/ShareViewModel.swift
T
keepClamDown 329839e467 feat(ios): add share to support (#15340)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added iOS sharing for URLs, text, web pages, and images.
* Shared content can be reviewed, titled, and saved to an AFFiNE
workspace.
  * Choose destinations including workspaces, tags, and collections.
* Added previews, attachment handling, import status, retry support, and
success/error feedback.
* Pending shares are processed when AFFiNE opens or returns to the
foreground.
* **Bug Fixes**
* Improved workspace profile handling across different workspace types.
* Onboarding completion is now recorded immediately after successful
sign-in verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2026-08-27 19:09:51 +08:00

94 lines
2.5 KiB
Swift

import Foundation
import UIKit
@MainActor
final class ShareViewModel: ObservableObject {
@Published var title = ""
@Published var previewText = ""
@Published var previewImage: UIImage?
@Published var isLoading = true
@Published var isSaving = false
@Published var hasSaved = false
@Published var errorMessage: String?
var actionTitle: String {
"Open AFFiNE"
}
var canSave: Bool {
!isLoading
&& !isSaving
&& !title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
&& draft?.content != nil
}
private var draft: SharePayloadDraft?
private let store: ShareInboxStore
init(store: ShareInboxStore = .shared) {
self.store = store
}
func load(from extensionContext: NSExtensionContext?) async {
isLoading = true
defer { isLoading = false }
let items = extensionContext?.inputItems.compactMap { $0 as? NSExtensionItem } ?? []
let built = await SharePayloadBuilder.build(from: items)
draft = built
title = built.title
previewText = built.previewText
errorMessage = built.errorMessage
if let file = built.file {
previewImage = UIImage(data: file.data)?
.preparingThumbnail(of: CGSize(width: 480, height: 480))
}
}
func save() async -> Bool {
guard !isSaving, !hasSaved else { return false }
isSaving = true
defer { isSaving = false }
let trimmedTitle = title.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmedTitle.isEmpty else {
errorMessage = "Title is required."
return false
}
guard let draft, let content = draft.content else {
errorMessage = draft?.errorMessage ?? "Nothing to share."
return false
}
let itemId = UUID().uuidString
var attachments: [ShareInboxAttachment] = []
var attachmentData: [(ShareInboxAttachment, Data)] = []
if let file = draft.file {
let attachment = ShareInboxAttachment(
fileName: file.fileName,
mimeType: file.mimeType,
relativePath: "\(itemId)/\(file.fileName)"
)
attachments = [attachment]
attachmentData = [(attachment, file.data)]
}
let item = ShareInboxItem(
id: itemId,
title: trimmedTitle,
content: content,
previewText: draft.previewText,
attachments: attachments
)
do {
try store.enqueue(item, attachmentData: attachmentData)
hasSaved = true
return true
} catch {
errorMessage = "Failed to save shared content."
return false
}
}
}