Improve share extension
This commit is contained in:
parent
d4dd16698d
commit
7deae7f160
|
@ -1172,7 +1172,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1200,7 +1200,7 @@
|
|||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1227,7 +1227,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageShare/Info.plist;
|
||||
|
@ -1254,7 +1254,7 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageShare/Info.plist;
|
||||
|
@ -1403,7 +1403,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
@ -1443,7 +1443,7 @@
|
|||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 104;
|
||||
CURRENT_PROJECT_VERSION = 105;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
|
@ -530,6 +530,9 @@ struct ComposeView: View {
|
|||
self.photosAreUploading = false
|
||||
self.refreshScreenState()
|
||||
} catch {
|
||||
self.photosAreUploading = false
|
||||
self.refreshScreenState()
|
||||
|
||||
ErrorService.shared.handle(error, message: "compose.error.loadingPhotosFailed", showToastr: true)
|
||||
}
|
||||
}
|
||||
|
@ -554,8 +557,8 @@ struct ComposeView: View {
|
|||
}
|
||||
|
||||
// We are sending orginal file (not file compressed from memory).
|
||||
guard let imageFileTranseferable = photoAttachment.imageFileTranseferable,
|
||||
let data = try? Data(contentsOf: imageFileTranseferable.url),
|
||||
guard let photoUrl = photoAttachment.photoUrl,
|
||||
let data = try? Data(contentsOf: photoUrl),
|
||||
let uiImage = UIImage(data: data) else {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -469,54 +469,54 @@ struct ComposeView: View {
|
|||
}
|
||||
|
||||
private func loadPhotos() async {
|
||||
do {
|
||||
self.photosAreUploading = true
|
||||
self.publishDisabled = self.isPublishButtonDisabled()
|
||||
self.interactiveDismissDisabled = self.isInteractiveDismissDisabled()
|
||||
self.photosAreUploading = true
|
||||
self.publishDisabled = self.isPublishButtonDisabled()
|
||||
self.interactiveDismissDisabled = self.isInteractiveDismissDisabled()
|
||||
|
||||
// We have to create list with existing photos.
|
||||
var temporaryPhotosAttachment: [PhotoAttachment] = []
|
||||
// We have to create list with existing photos.
|
||||
var temporaryPhotosAttachment: [PhotoAttachment] = []
|
||||
|
||||
// Add to collection photos selected on photo picker.
|
||||
for item in self.selectedItems {
|
||||
if let photoAttachment = self.photosAttachment.first(where: { $0.photosPickerItem == item }) {
|
||||
temporaryPhotosAttachment.append(photoAttachment)
|
||||
continue
|
||||
}
|
||||
|
||||
temporaryPhotosAttachment.append(PhotoAttachment(photosPickerItem: item))
|
||||
// Add to collection photos selected on photo picker.
|
||||
for item in self.selectedItems {
|
||||
if let photoAttachment = self.photosAttachment.first(where: { $0.photosPickerItem == item }) {
|
||||
temporaryPhotosAttachment.append(photoAttachment)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add to collection photos from share sheet.
|
||||
for item in self.attachments {
|
||||
if let photoAttachment = self.photosAttachment.first(where: { $0.nsItemProvider == item }) {
|
||||
temporaryPhotosAttachment.append(photoAttachment)
|
||||
continue
|
||||
}
|
||||
|
||||
temporaryPhotosAttachment.append(PhotoAttachment(nsItemProvider: item))
|
||||
}
|
||||
|
||||
// We can show new list on the screen.
|
||||
self.photosAttachment = temporaryPhotosAttachment
|
||||
|
||||
// Now we have to get from photos images as JPEG.
|
||||
for item in self.photosAttachment.filter({ $0.photoData == nil }) {
|
||||
try await item.loadImage()
|
||||
}
|
||||
|
||||
// Open again the keyboard.
|
||||
self.focusedField = .content
|
||||
|
||||
// Upload images which hasn't been uploaded yet.
|
||||
await self.upload()
|
||||
|
||||
// Change state of the screen.
|
||||
self.photosAreUploading = false
|
||||
self.refreshScreenState()
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "compose.error.loadingPhotosFailed", showToastr: true)
|
||||
temporaryPhotosAttachment.append(PhotoAttachment(photosPickerItem: item))
|
||||
}
|
||||
|
||||
// Add to collection photos from share sheet.
|
||||
for item in self.attachments {
|
||||
if let photoAttachment = self.photosAttachment.first(where: { $0.nsItemProvider == item }) {
|
||||
temporaryPhotosAttachment.append(photoAttachment)
|
||||
continue
|
||||
}
|
||||
|
||||
temporaryPhotosAttachment.append(PhotoAttachment(nsItemProvider: item))
|
||||
}
|
||||
|
||||
// We can show new list on the screen.
|
||||
self.photosAttachment = temporaryPhotosAttachment
|
||||
|
||||
// Now we have to get from photos images as JPEG.
|
||||
for item in self.photosAttachment.filter({ $0.photoData == nil }) {
|
||||
do {
|
||||
try await item.loadImage()
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "Cannot load image from external library.")
|
||||
}
|
||||
}
|
||||
|
||||
// Open again the keyboard.
|
||||
self.focusedField = .content
|
||||
|
||||
// Upload images which hasn't been uploaded yet.
|
||||
await self.upload()
|
||||
|
||||
// Change state of the screen.
|
||||
self.photosAreUploading = false
|
||||
self.refreshScreenState()
|
||||
}
|
||||
|
||||
private func refreshScreenState() {
|
||||
|
|
|
@ -10,14 +10,6 @@
|
|||
<dict>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
<integer>4</integer>
|
||||
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
|
||||
<integer>0</integer>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<false/>
|
||||
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||
<integer>0</integer>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the Apache License 2.0.
|
||||
//
|
||||
|
||||
import AVFoundation
|
||||
import Foundation
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
import ServicesKit
|
||||
|
||||
@MainActor
|
||||
enum FileTypeSupported: String, CaseIterable {
|
||||
case image = "public.image"
|
||||
case jpeg = "public.jpeg"
|
||||
case png = "public.png"
|
||||
case tiff = "public.tiff"
|
||||
case gif = "public.gif"
|
||||
|
||||
case gif2 = "com.compuserve.gif"
|
||||
case adobeRawImage = "com.adobe.raw-image"
|
||||
case uiimage = "com.apple.uikit.image"
|
||||
|
||||
public nonisolated static var allCases: [FileTypeSupported] {
|
||||
[.image, .jpeg, .png, .tiff, .gif, .gif2, .uiimage, .adobeRawImage]
|
||||
}
|
||||
|
||||
func loadItemContent(item: NSItemProvider) async throws -> TransferedFile? {
|
||||
let result = try await item.loadItem(forTypeIdentifier: rawValue)
|
||||
|
||||
if isImageFile() {
|
||||
if let image = result as? UIImage,
|
||||
let data = image.getJpegData() {
|
||||
let fileUrl = getTmpFileUrl()
|
||||
try data.write(to: fileUrl)
|
||||
|
||||
return TransferedFile(file: data, url: fileUrl)
|
||||
} else if let imageURL = result as? URL,
|
||||
let data = await ImageCompressService.shared.compressImageFrom(url: imageURL) {
|
||||
let fileUrl = getTmpFileUrl()
|
||||
try data.write(to: fileUrl)
|
||||
|
||||
return TransferedFile(file: data, url: fileUrl)
|
||||
} else if let data = result as? Data {
|
||||
let fileUrl = getTmpFileUrl()
|
||||
try data.write(to: fileUrl)
|
||||
|
||||
return TransferedFile(file: data, url: fileUrl)
|
||||
}
|
||||
}
|
||||
|
||||
if let transferable = try await item.createImageFileTranseferable(),
|
||||
let data = await ImageCompressService.shared.compressImageFrom(url: transferable.url) {
|
||||
return TransferedFile(file: data, url: transferable.url)
|
||||
}
|
||||
|
||||
if let image = result as? UIImage,
|
||||
let data = image.getJpegData() {
|
||||
let fileUrl = getTmpFileUrl()
|
||||
try data.write(to: fileUrl)
|
||||
|
||||
return TransferedFile(file: data, url: fileUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isImageFile() -> Bool {
|
||||
return self == .jpeg || self == .png || self == .tiff || self == .image || self == .uiimage || self == .adobeRawImage
|
||||
}
|
||||
|
||||
func getTmpFileUrl() -> URL {
|
||||
return URL.temporaryDirectory.appending(path: "\(UUID().uuidString).jpeg")
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ public class PhotoAttachment: ObservableObject, Identifiable, Equatable, Hashabl
|
|||
@Published public var photoData: Data?
|
||||
|
||||
/// Property which stores orginal image file copied from Photos to tmp folder.
|
||||
@Published public var imageFileTranseferable: ImageFileTranseferable?
|
||||
@Published public var photoUrl: URL?
|
||||
|
||||
/// Property stores information after upload to Pixelfed.
|
||||
@Published public var uploadedAttachment: UploadedAttachment?
|
||||
|
@ -53,14 +53,20 @@ public extension PhotoAttachment {
|
|||
func loadImage() async throws {
|
||||
if let pickerItem = self.photosPickerItem,
|
||||
let transferable = try await pickerItem.createImageFileTranseferable() {
|
||||
self.imageFileTranseferable = transferable
|
||||
self.photoUrl = transferable.url
|
||||
self.photoData = await ImageCompressService.shared.compressImageFrom(url: transferable.url)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if let itemProvider = self.nsItemProvider,
|
||||
let transferable = try await itemProvider.createImageFileTranseferable() {
|
||||
self.imageFileTranseferable = transferable
|
||||
self.photoData = await ImageCompressService.shared.compressImageFrom(url: transferable.url)
|
||||
let identifier = itemProvider.registeredTypeIdentifiers.first,
|
||||
let handledItemType = FileTypeSupported(rawValue: identifier),
|
||||
let transferredFile = try await handledItemType.loadItemContent(item: itemProvider) {
|
||||
self.photoUrl = transferredFile.url
|
||||
self.photoData = transferredFile.file
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +92,7 @@ public extension [PhotoAttachment] {
|
|||
|
||||
func removeTmpFiles() {
|
||||
for file in self {
|
||||
if let fileUrl = file.imageFileTranseferable?.url {
|
||||
if let fileUrl = file.photoUrl {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: fileUrl)
|
||||
} catch {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct TransferedFile {
|
||||
public let file: Data
|
||||
public let url: URL
|
||||
|
||||
public init(file: Data, url: URL) {
|
||||
self.file = file
|
||||
self.url = url
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue