mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-31 21:59:10 +08:00
329839e467
<!-- 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>
94 lines
2.5 KiB
Swift
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
|
|
}
|
|
}
|
|
}
|