forked from shadowfacts/Tusker
193 lines
7.1 KiB
Swift
193 lines
7.1 KiB
Swift
//
|
|
// ComposeAttachmentsList.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 8/19/20.
|
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct ComposeAttachmentsList: View {
|
|
private let cellHeight: CGFloat = 80
|
|
private let cellPadding: CGFloat = 12
|
|
|
|
@ObservedObject var draft: Draft
|
|
|
|
@EnvironmentObject var mastodonController: MastodonController
|
|
@EnvironmentObject var uiState: ComposeUIState
|
|
@State var isShowingAssetPickerPopover = false
|
|
@State var isShowingCreateDrawing = false
|
|
@State var rowHeights = [UUID: CGFloat]()
|
|
|
|
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
|
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
|
|
|
|
var body: some View {
|
|
List {
|
|
ForEach(draft.attachments) { (attachment) in
|
|
ComposeAttachmentRow(
|
|
draft: draft,
|
|
attachment: attachment
|
|
) { (newHeight) in
|
|
// in case height changed callback is called after atachment is removed but before view hierarchy is updated
|
|
if draft.attachments.contains(where: { $0.id == attachment.id }) {
|
|
rowHeights[attachment.id] = newHeight
|
|
}
|
|
}
|
|
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
|
.onDrag { NSItemProvider(object: attachment) }
|
|
}
|
|
.onMove(perform: self.moveAttachments)
|
|
.onDelete(perform: self.deleteAttachments)
|
|
.conditionally(canAddAttachment) {
|
|
$0.onInsert(of: CompositionAttachment.readableTypeIdentifiersForItemProvider, perform: self.insertAttachments)
|
|
}
|
|
|
|
Button(action: self.addAttachment) {
|
|
Label("Add photo or video", systemImage: addButtonImageName)
|
|
}
|
|
.disabled(!canAddAttachment)
|
|
.foregroundColor(.blue)
|
|
.frame(height: cellHeight / 2)
|
|
.popover(isPresented: $isShowingAssetPickerPopover, content: self.assetPickerPopover)
|
|
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
|
|
|
Button(action: self.createDrawing) {
|
|
Label("Draw something", systemImage: "hand.draw")
|
|
}
|
|
.disabled(!canAddAttachment)
|
|
.foregroundColor(.blue)
|
|
.frame(height: cellHeight / 2)
|
|
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
|
|
|
Button(action: self.togglePoll) {
|
|
Label(draft.poll == nil ? "Add a poll" : "Remove poll", systemImage: "chart.bar.doc.horizontal")
|
|
}
|
|
.disabled(!canAddPoll)
|
|
.foregroundColor(.blue)
|
|
.frame(height: cellHeight / 2)
|
|
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
|
}
|
|
.listStyle(PlainListStyle())
|
|
.frame(height: totalListHeight)
|
|
.onAppear(perform: self.didAppear)
|
|
.onReceive(draft.$attachments, perform: self.attachmentsChanged)
|
|
}
|
|
|
|
private var addButtonImageName: String {
|
|
switch colorScheme {
|
|
case .dark:
|
|
return "photo.fill"
|
|
case .light:
|
|
return "photo"
|
|
@unknown default:
|
|
return "photo"
|
|
}
|
|
}
|
|
|
|
private var canAddAttachment: Bool {
|
|
switch mastodonController.instance?.instanceType {
|
|
case nil:
|
|
return false
|
|
case .pleroma:
|
|
return true
|
|
case .mastodon:
|
|
return draft.attachments.count < 4 && draft.attachments.allSatisfy { $0.data.type == .image } && draft.poll == nil
|
|
}
|
|
}
|
|
|
|
private var canAddPoll: Bool {
|
|
switch mastodonController.instance?.instanceType {
|
|
case nil:
|
|
return false
|
|
case .pleroma:
|
|
return true
|
|
case .mastodon:
|
|
return draft.attachments.isEmpty
|
|
}
|
|
}
|
|
|
|
private var totalListHeight: CGFloat {
|
|
let totalRowHeights = rowHeights.values.reduce(0, +)
|
|
let totalPadding = CGFloat(draft.attachments.count) * cellPadding
|
|
let addButtonHeight = 3 * (cellHeight / 2 + cellPadding)
|
|
return totalRowHeights + totalPadding + addButtonHeight
|
|
}
|
|
|
|
private func didAppear() {
|
|
let proxy = UITableView.appearance(whenContainedInInstancesOf: [ComposeHostingController.self])
|
|
// enable drag and drop to reorder on iPhone
|
|
proxy.dragInteractionEnabled = true
|
|
proxy.isScrollEnabled = false
|
|
}
|
|
|
|
private func attachmentsChanged(attachments: [CompositionAttachment]) {
|
|
var copy = rowHeights
|
|
for k in copy.keys where !attachments.contains(where: { k == $0.id }) {
|
|
copy.removeValue(forKey: k)
|
|
}
|
|
for attachment in attachments where !copy.keys.contains(attachment.id) {
|
|
copy[attachment.id] = cellHeight
|
|
}
|
|
self.rowHeights = copy
|
|
}
|
|
|
|
private func assetPickerPopover() -> some View {
|
|
ComposeAssetPicker(draft: draft, delegate: uiState.delegate?.assetPickerDelegate)
|
|
.onDisappear {
|
|
self.isShowingAssetPickerPopover = false
|
|
}
|
|
.environment(\.colorScheme, .dark)
|
|
.edgesIgnoringSafeArea(.bottom)
|
|
}
|
|
|
|
private func addAttachment() {
|
|
if horizontalSizeClass == .regular {
|
|
isShowingAssetPickerPopover = true
|
|
} else {
|
|
uiState.delegate?.presentAssetPickerSheet()
|
|
}
|
|
}
|
|
|
|
private func moveAttachments(from source: IndexSet, to destination: Int) {
|
|
draft.attachments.move(fromOffsets: source, toOffset: destination)
|
|
}
|
|
|
|
private func deleteAttachments(at indices: IndexSet) {
|
|
draft.attachments.remove(atOffsets: indices)
|
|
}
|
|
|
|
private func insertAttachments(at offset: Int, itemProviders: [NSItemProvider]) {
|
|
for provider in itemProviders where provider.canLoadObject(ofClass: CompositionAttachment.self) {
|
|
guard canAddAttachment else { break }
|
|
|
|
provider.loadObject(ofClass: CompositionAttachment.self) { (object, error) in
|
|
guard let attachment = object as? CompositionAttachment else { return }
|
|
DispatchQueue.main.async {
|
|
self.draft.attachments.insert(attachment, at: offset)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func createDrawing() {
|
|
uiState.composeDrawingMode = .createNew
|
|
uiState.delegate?.presentComposeDrawing()
|
|
}
|
|
|
|
private func togglePoll() {
|
|
UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
|
|
|
|
withAnimation {
|
|
draft.poll = draft.poll == nil ? Draft.Poll() : nil
|
|
}
|
|
}
|
|
}
|
|
|
|
//struct ComposeAttachmentsList_Previews: PreviewProvider {
|
|
// static var previews: some View {
|
|
// ComposeAttachmentsList()
|
|
// }
|
|
//}
|