Improve share extension

This commit is contained in:
Marcin Czachursk 2023-04-11 07:37:05 +02:00
parent d4dd16698d
commit 7deae7f160
7 changed files with 161 additions and 65 deletions

View File

@ -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;

View File

@ -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
}

View File

@ -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() {

View File

@ -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>

View File

@ -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")
}
}

View File

@ -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 {

View File

@ -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
}
}