mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-14 05:21:12 +00:00
Improve media selection on the status editor. (#1722)
* show menu buttons on media item * fix media preparing logic - not removing photo pickers when removing media on the post editor - pickers don't have identifiers after being selected - preparing tasks (creating containers, uploading media) don't run in parallel - re-preparing the whole media list every time adding new ones * remove measurement code * rename variables * fix MainActor mutation
This commit is contained in:
parent
9fe5994bb2
commit
774ba834bd
4 changed files with 252 additions and 165 deletions
|
@ -57,9 +57,11 @@ struct StatusEditorAccessoryView: View {
|
|||
}
|
||||
}
|
||||
.photosPicker(isPresented: $isPhotosPickerPresented,
|
||||
selection: $viewModel.selectedMedias,
|
||||
selection: $viewModel.mediaPickers,
|
||||
maxSelectionCount: 4,
|
||||
matching: .any(of: [.images, .videos]))
|
||||
matching: .any(of: [.images, .videos]),
|
||||
photoLibrary: .shared()
|
||||
)
|
||||
.fileImporter(isPresented: $isFileImporterPresented,
|
||||
allowedContentTypes: [.image, .video],
|
||||
allowsMultipleSelection: true)
|
||||
|
|
|
@ -5,7 +5,7 @@ import SwiftUI
|
|||
import UIKit
|
||||
|
||||
struct StatusEditorMediaContainer: Identifiable {
|
||||
let id = UUID().uuidString
|
||||
let id: String
|
||||
let image: UIImage?
|
||||
let movieTransferable: MovieFileTranseferable?
|
||||
let gifTransferable: GifFileTranseferable?
|
||||
|
|
|
@ -17,25 +17,26 @@ struct StatusEditorMediaView: View {
|
|||
var body: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(viewModel.mediasImages) { container in
|
||||
ForEach(viewModel.mediaContainers) { container in
|
||||
Menu {
|
||||
makeImageMenu(container: container)
|
||||
} label: {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
if let attachement = container.mediaAttachment {
|
||||
makeLazyImage(mediaAttachement: attachement)
|
||||
} else if container.image != nil {
|
||||
makeLocalImage(container: container)
|
||||
} else if container.movieTransferable != nil || container.gifTransferable != nil {
|
||||
makeVideoAttachement(container: container)
|
||||
} else if let error = container.error as? ServerError {
|
||||
makeErrorView(error: error)
|
||||
}
|
||||
if container.mediaAttachment?.description?.isEmpty == false {
|
||||
altMarker
|
||||
}
|
||||
if let attachement = container.mediaAttachment {
|
||||
makeLazyImage(mediaAttachement: attachement)
|
||||
} else if container.image != nil {
|
||||
makeLocalImage(container: container)
|
||||
} else if container.movieTransferable != nil || container.gifTransferable != nil {
|
||||
makeVideoAttachement(container: container)
|
||||
} else if let error = container.error as? ServerError {
|
||||
makeErrorView(error: error)
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
makeAltMarker(container: container)
|
||||
}
|
||||
.overlay(alignment: .topTrailing) {
|
||||
makeDiscardMarker(container: container)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, .layoutPadding)
|
||||
|
@ -122,7 +123,13 @@ struct StatusEditorMediaView: View {
|
|||
|
||||
Button(role: .destructive) {
|
||||
withAnimation {
|
||||
viewModel.mediasImages.removeAll(where: { $0.id == container.id })
|
||||
viewModel.mediaPickers.removeAll(where: {
|
||||
if let id = $0.itemIdentifier {
|
||||
return id == container.id
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
}
|
||||
} label: {
|
||||
Label("action.delete", systemImage: "trash")
|
||||
|
@ -141,14 +148,37 @@ struct StatusEditorMediaView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private var altMarker: some View {
|
||||
Button {} label: {
|
||||
private func makeAltMarker(container: StatusEditorMediaContainer) -> some View {
|
||||
Button {
|
||||
editingContainer = container
|
||||
} label: {
|
||||
Text("status.image.alt-text.abbreviation")
|
||||
.font(.caption2)
|
||||
}
|
||||
.padding(4)
|
||||
.background(.thinMaterial)
|
||||
.cornerRadius(8)
|
||||
.padding(4)
|
||||
}
|
||||
|
||||
private func makeDiscardMarker(container: StatusEditorMediaContainer) -> some View {
|
||||
Button(role: .destructive) {
|
||||
withAnimation {
|
||||
viewModel.mediaPickers.removeAll(where: {
|
||||
if let id = $0.itemIdentifier {
|
||||
return id == container.id
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.tint)
|
||||
.padding(4)
|
||||
.background(Circle().fill(.thinMaterial))
|
||||
}
|
||||
.padding(4)
|
||||
}
|
||||
|
||||
private var placeholderView: some View {
|
||||
|
|
|
@ -15,7 +15,7 @@ import SwiftUI
|
|||
var currentAccount: Account? {
|
||||
didSet {
|
||||
if let itemsProvider {
|
||||
mediasImages = []
|
||||
mediaContainers = []
|
||||
processItemsProvider(items: itemsProvider)
|
||||
}
|
||||
}
|
||||
|
@ -84,19 +84,30 @@ import SwiftUI
|
|||
var spoilerText: String = ""
|
||||
|
||||
var isPosting: Bool = false
|
||||
var selectedMedias: [PhotosPickerItem] = [] {
|
||||
var mediaPickers: [PhotosPickerItem] = [] {
|
||||
didSet {
|
||||
if selectedMedias.count > 4 {
|
||||
selectedMedias = selectedMedias.prefix(4).map { $0 }
|
||||
if mediaPickers.count > 4 {
|
||||
mediaPickers = mediaPickers.prefix(4).map { $0 }
|
||||
}
|
||||
|
||||
let removedIDs = oldValue
|
||||
.filter { !mediaPickers.contains($0) }
|
||||
.compactMap { $0.itemIdentifier }
|
||||
mediaContainers.removeAll { removedIDs.contains($0.id) }
|
||||
|
||||
let newPickerItems = mediaPickers.filter { !oldValue.contains($0) }
|
||||
if !newPickerItems.isEmpty {
|
||||
isMediasLoading = true
|
||||
for item in newPickerItems {
|
||||
prepareToPost(for: item)
|
||||
}
|
||||
}
|
||||
isMediasLoading = true
|
||||
inflateSelectedMedias()
|
||||
}
|
||||
}
|
||||
|
||||
var isMediasLoading: Bool = false
|
||||
|
||||
var mediasImages: [StatusEditorMediaContainer] = []
|
||||
private(set) var mediaContainers: [StatusEditorMediaContainer] = []
|
||||
var replyToStatus: Status?
|
||||
var embeddedStatus: Status?
|
||||
|
||||
|
@ -106,11 +117,11 @@ import SwiftUI
|
|||
var showPostingErrorAlert: Bool = false
|
||||
|
||||
var canPost: Bool {
|
||||
statusText.length > 0 || !mediasImages.isEmpty
|
||||
statusText.length > 0 || !mediaContainers.isEmpty
|
||||
}
|
||||
|
||||
var shouldDisablePollButton: Bool {
|
||||
!selectedMedias.isEmpty
|
||||
!mediaPickers.isEmpty
|
||||
}
|
||||
|
||||
var shouldDisplayDismissWarning: Bool {
|
||||
|
@ -137,7 +148,6 @@ import SwiftUI
|
|||
|
||||
private var mentionString: String?
|
||||
|
||||
private var uploadTask: Task<Void, Never>?
|
||||
private var suggestedTask: Task<Void, Never>?
|
||||
|
||||
init(mode: Mode) {
|
||||
|
@ -182,7 +192,7 @@ import SwiftUI
|
|||
visibility: visibility,
|
||||
inReplyToId: mode.replyToStatus?.id,
|
||||
spoilerText: spoilerOn ? spoilerText : nil,
|
||||
mediaIds: mediasImages.compactMap { $0.mediaAttachment?.id },
|
||||
mediaIds: mediaContainers.compactMap { $0.mediaAttachment?.id },
|
||||
poll: pollData,
|
||||
language: selectedLanguage,
|
||||
mediaAttributes: mediaAttributes)
|
||||
|
@ -278,11 +288,15 @@ import SwiftUI
|
|||
spoilerOn = !status.spoilerText.asRawText.isEmpty
|
||||
spoilerText = status.spoilerText.asRawText
|
||||
visibility = status.visibility
|
||||
mediasImages = status.mediaAttachments.map { .init(image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: $0,
|
||||
error: nil) }
|
||||
mediaContainers = status.mediaAttachments.map {
|
||||
StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: $0,
|
||||
error: nil)
|
||||
}
|
||||
case let .quote(status):
|
||||
embeddedStatus = status
|
||||
if let url = embeddedStatusURL {
|
||||
|
@ -370,12 +384,14 @@ import SwiftUI
|
|||
}
|
||||
|
||||
func processCameraPhoto(image: UIImage) {
|
||||
mediasImages.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
processMediasToUpload()
|
||||
let container = StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
prepareToPost(for: container)
|
||||
}
|
||||
|
||||
private func processItemsProvider(items: [NSItemProvider]) {
|
||||
|
@ -391,32 +407,44 @@ import SwiftUI
|
|||
if let text = content as? String {
|
||||
initialText += "\(text) "
|
||||
} else if let image = content as? UIImage {
|
||||
mediasImages.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
let container = StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
prepareToPost(for: container)
|
||||
} else if let content = content as? ImageFileTranseferable,
|
||||
let compressedData = await compressor.compressImageFrom(url: content.url),
|
||||
let image = UIImage(data: compressedData)
|
||||
{
|
||||
mediasImages.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
let container = StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
prepareToPost(for: container)
|
||||
} else if let video = content as? MovieFileTranseferable {
|
||||
mediasImages.append(.init(image: nil,
|
||||
movieTransferable: video,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
let container = StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: nil,
|
||||
movieTransferable: video,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
prepareToPost(for: container)
|
||||
} else if let gif = content as? GifFileTranseferable {
|
||||
mediasImages.append(.init(image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: gif,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
let container = StatusEditorMediaContainer(
|
||||
id: UUID().uuidString,
|
||||
image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: gif,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
prepareToPost(for: container)
|
||||
}
|
||||
} catch {
|
||||
isMediasLoading = false
|
||||
|
@ -427,9 +455,6 @@ import SwiftUI
|
|||
statusText = .init(string: initialText)
|
||||
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
|
||||
}
|
||||
if !mediasImages.isEmpty {
|
||||
processMediasToUpload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,93 +557,111 @@ import SwiftUI
|
|||
// MARK: - Media related function
|
||||
|
||||
private func indexOf(container: StatusEditorMediaContainer) -> Int? {
|
||||
mediasImages.firstIndex(where: { $0.id == container.id })
|
||||
mediaContainers.firstIndex(where: { $0.id == container.id })
|
||||
}
|
||||
|
||||
func inflateSelectedMedias() {
|
||||
mediasImages = []
|
||||
|
||||
Task {
|
||||
var medias: [StatusEditorMediaContainer] = []
|
||||
for media in selectedMedias {
|
||||
var file: (any Transferable)?
|
||||
|
||||
if file == nil {
|
||||
file = try? await media.loadTransferable(type: GifFileTranseferable.self)
|
||||
}
|
||||
if file == nil {
|
||||
file = try? await media.loadTransferable(type: MovieFileTranseferable.self)
|
||||
}
|
||||
if file == nil {
|
||||
file = try? await media.loadTransferable(type: ImageFileTranseferable.self)
|
||||
}
|
||||
|
||||
let compressor = StatusEditorCompressor()
|
||||
if let imageFile = file as? ImageFileTranseferable,
|
||||
let compressedData = await compressor.compressImageFrom(url: imageFile.url),
|
||||
let image = UIImage(data: compressedData)
|
||||
{
|
||||
medias.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
} else if let videoFile = file as? MovieFileTranseferable {
|
||||
medias.append(.init(image: nil,
|
||||
movieTransferable: videoFile,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
} else if let gifFile = file as? GifFileTranseferable {
|
||||
medias.append(.init(image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: gifFile,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.mediasImages = medias
|
||||
self?.processMediasToUpload()
|
||||
func prepareToPost(for pickerItem: PhotosPickerItem) {
|
||||
Task(priority: .high) {
|
||||
if let container = await makeMediaContainer(from: pickerItem) {
|
||||
self.mediaContainers.append(container)
|
||||
await upload(container: container)
|
||||
self.isMediasLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func processMediasToUpload() {
|
||||
isMediasLoading = false
|
||||
uploadTask?.cancel()
|
||||
let mediasCopy = mediasImages
|
||||
uploadTask = Task {
|
||||
for media in mediasCopy {
|
||||
if !Task.isCancelled {
|
||||
await upload(container: media)
|
||||
func prepareToPost(for container: StatusEditorMediaContainer) {
|
||||
Task(priority: .high) {
|
||||
self.mediaContainers.append(container)
|
||||
await upload(container: container)
|
||||
self.isMediasLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
func makeMediaContainer(from pickerItem: PhotosPickerItem) async -> StatusEditorMediaContainer? {
|
||||
await withTaskGroup(of: StatusEditorMediaContainer?.self, returning: StatusEditorMediaContainer?.self) { taskGroup in
|
||||
taskGroup.addTask(priority: .high) { await Self.makeImageContainer(from: pickerItem) }
|
||||
taskGroup.addTask(priority: .high) { await Self.makeGifContainer(from: pickerItem) }
|
||||
taskGroup.addTask(priority: .high) { await Self.makeMovieContainer(from: pickerItem) }
|
||||
|
||||
for await container in taskGroup {
|
||||
if let container {
|
||||
taskGroup.cancelAll()
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private static func makeGifContainer(from pickerItem: PhotosPickerItem) async -> StatusEditorMediaContainer? {
|
||||
guard let gifFile = try? await pickerItem.loadTransferable(type: GifFileTranseferable.self) else { return nil }
|
||||
|
||||
return StatusEditorMediaContainer(
|
||||
id: pickerItem.itemIdentifier ?? UUID().uuidString,
|
||||
image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: gifFile,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
}
|
||||
|
||||
private static func makeMovieContainer(from pickerItem: PhotosPickerItem) async -> StatusEditorMediaContainer? {
|
||||
guard let movieFile = try? await pickerItem.loadTransferable(type: MovieFileTranseferable.self) else { return nil }
|
||||
|
||||
return StatusEditorMediaContainer(
|
||||
id: pickerItem.itemIdentifier ?? UUID().uuidString,
|
||||
image: nil,
|
||||
movieTransferable: movieFile,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
}
|
||||
|
||||
private static func makeImageContainer(from pickerItem: PhotosPickerItem) async -> StatusEditorMediaContainer? {
|
||||
guard let imageFile = try? await pickerItem.loadTransferable(type: ImageFileTranseferable.self) else { return nil }
|
||||
|
||||
let compressor = StatusEditorCompressor()
|
||||
|
||||
guard let compressedData = await compressor.compressImageFrom(url: imageFile.url),
|
||||
let image = UIImage(data: compressedData)
|
||||
else { return nil }
|
||||
|
||||
return StatusEditorMediaContainer(
|
||||
id: pickerItem.itemIdentifier ?? UUID().uuidString,
|
||||
image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
}
|
||||
|
||||
func upload(container: StatusEditorMediaContainer) async {
|
||||
if let index = indexOf(container: container) {
|
||||
let originalContainer = mediasImages[index]
|
||||
let originalContainer = mediaContainers[index]
|
||||
guard originalContainer.mediaAttachment == nil else { return }
|
||||
let newContainer = StatusEditorMediaContainer(image: originalContainer.image,
|
||||
movieTransferable: originalContainer.movieTransferable,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
mediasImages[index] = newContainer
|
||||
let newContainer = StatusEditorMediaContainer(
|
||||
id: originalContainer.id,
|
||||
image: originalContainer.image,
|
||||
movieTransferable: originalContainer.movieTransferable,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil)
|
||||
mediaContainers[index] = newContainer
|
||||
do {
|
||||
let compressor = StatusEditorCompressor()
|
||||
if let image = originalContainer.image {
|
||||
let imageData = try await compressor.compressImageForUpload(image)
|
||||
let uploadedMedia = try await uploadMedia(data: imageData, mimeType: "image/jpeg")
|
||||
if let index = indexOf(container: newContainer) {
|
||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: originalContainer.id,
|
||||
image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
}
|
||||
if let uploadedMedia, uploadedMedia.url == nil {
|
||||
scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia)
|
||||
|
@ -629,11 +672,13 @@ import SwiftUI
|
|||
{
|
||||
let uploadedMedia = try await uploadMedia(data: data, mimeType: compressedVideoURL.mimeType())
|
||||
if let index = indexOf(container: newContainer) {
|
||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: originalContainer.movieTransferable,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: originalContainer.id,
|
||||
image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: originalContainer.movieTransferable,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
}
|
||||
if let uploadedMedia, uploadedMedia.url == nil {
|
||||
scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia)
|
||||
|
@ -641,11 +686,13 @@ import SwiftUI
|
|||
} else if let gifData = originalContainer.gifTransferable?.data {
|
||||
let uploadedMedia = try await uploadMedia(data: gifData, mimeType: "image/gif")
|
||||
if let index = indexOf(container: newContainer) {
|
||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: originalContainer.gifTransferable,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: originalContainer.id,
|
||||
image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: originalContainer.gifTransferable,
|
||||
mediaAttachment: uploadedMedia,
|
||||
error: nil)
|
||||
}
|
||||
if let uploadedMedia, uploadedMedia.url == nil {
|
||||
scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia)
|
||||
|
@ -653,11 +700,13 @@ import SwiftUI
|
|||
}
|
||||
} catch {
|
||||
if let index = indexOf(container: newContainer) {
|
||||
mediasImages[index] = .init(image: originalContainer.image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: error)
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: originalContainer.id,
|
||||
image: originalContainer.image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,21 +716,23 @@ import SwiftUI
|
|||
Task {
|
||||
repeat {
|
||||
if let client,
|
||||
let index = mediasImages.firstIndex(where: { $0.mediaAttachment?.id == mediaAttachement.id })
|
||||
let index = mediaContainers.firstIndex(where: { $0.mediaAttachment?.id == mediaAttachement.id })
|
||||
{
|
||||
guard mediasImages[index].mediaAttachment?.url == nil else {
|
||||
guard mediaContainers[index].mediaAttachment?.url == nil else {
|
||||
return
|
||||
}
|
||||
do {
|
||||
let newAttachement: MediaAttachment = try await client.get(endpoint: Media.media(id: mediaAttachement.id,
|
||||
json: .init(description: nil)))
|
||||
if newAttachement.url != nil {
|
||||
let oldContainer = mediasImages[index]
|
||||
mediasImages[index] = .init(image: oldContainer.image,
|
||||
movieTransferable: oldContainer.movieTransferable,
|
||||
gifTransferable: oldContainer.gifTransferable,
|
||||
mediaAttachment: newAttachement,
|
||||
error: nil)
|
||||
let oldContainer = mediaContainers[index]
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: mediaAttachement.id,
|
||||
image: oldContainer.image,
|
||||
movieTransferable: oldContainer.movieTransferable,
|
||||
gifTransferable: oldContainer.gifTransferable,
|
||||
mediaAttachment: newAttachement,
|
||||
error: nil)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
@ -696,11 +747,13 @@ import SwiftUI
|
|||
do {
|
||||
let media: MediaAttachment = try await client.put(endpoint: Media.media(id: attachment.id,
|
||||
json: .init(description: description)))
|
||||
mediasImages[index] = .init(image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: media,
|
||||
error: nil)
|
||||
mediaContainers[index] = StatusEditorMediaContainer(
|
||||
id: container.id,
|
||||
image: nil,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
mediaAttachment: media,
|
||||
error: nil)
|
||||
} catch { print(error) }
|
||||
}
|
||||
}
|
||||
|
@ -727,22 +780,22 @@ import SwiftUI
|
|||
|
||||
func fetchCustomEmojis() async {
|
||||
typealias EmojiContainer = StatusEditorCategorizedEmojiContainer
|
||||
|
||||
|
||||
guard let client else { return }
|
||||
do {
|
||||
let customEmojis: [Emoji] = try await client.get(endpoint: CustomEmojis.customEmojis) ?? []
|
||||
var emojiContainers: [EmojiContainer] = []
|
||||
|
||||
|
||||
customEmojis.reduce([String: [Emoji]]()) { currentDict, emoji in
|
||||
var dict = currentDict
|
||||
let category = emoji.category ?? "Uncategorized"
|
||||
|
||||
|
||||
if let emojis = dict[category] {
|
||||
dict[category] = emojis + [emoji]
|
||||
} else {
|
||||
dict[category] = [emoji]
|
||||
}
|
||||
|
||||
|
||||
return dict
|
||||
}.sorted(by: { lhs, rhs in
|
||||
if rhs.key == "Uncategorized" { return false }
|
||||
|
@ -751,7 +804,7 @@ import SwiftUI
|
|||
}).forEach { key, value in
|
||||
emojiContainers.append(.init(categoryName: key, emojis: value))
|
||||
}
|
||||
|
||||
|
||||
customEmojiContainer = emojiContainers
|
||||
} catch {}
|
||||
}
|
||||
|
@ -785,3 +838,5 @@ extension StatusEditorViewModel: UITextPasteDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotosPickerItem: @unchecked Sendable {}
|
||||
|
|
Loading…
Reference in a new issue