feat: add mediaView for compose scene

This commit is contained in:
CMK 2022-11-08 16:39:19 +08:00
parent 9c9edcb717
commit fc3750c377
6 changed files with 89 additions and 71 deletions

View File

@ -0,0 +1,21 @@
//
// View.swift
//
//
// Created by MainasuK on 2022/11/8.
//
import SwiftUI
extension View {
public func badgeView<Content>(_ content: Content) -> some View where Content: View {
overlay(
ZStack {
content
}
.alignmentGuide(.top) { $0.height / 2 }
.alignmentGuide(.trailing) { $0.width / 2 }
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
)
}
}

View File

@ -13,18 +13,17 @@ import AVKit
public struct AttachmentView: View {
static let size = CGSize(width: 56, height: 56)
static let cornerRadius: CGFloat = 8
@ObservedObject var viewModel: AttachmentViewModel
let action: (Action) -> Void
@State var isCaptionEditorPresented = false
@State var caption = ""
public var body: some View {
Text("Hello")
ZStack {
let image = viewModel.thumbnail ?? .placeholder(color: .secondarySystemFill)
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
}
// Menu {
// menu
// } label: {

View File

@ -22,6 +22,7 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
var observations = Set<NSKeyValueObservation>()
// input
public let authContext: AuthContext
public let input: Input
@Published var caption = ""
@Published var sizeLimit = SizeLimit()
@ -33,13 +34,19 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
@Published var error: Error?
let progress = Progress() // upload progress
public init(input: Input) {
public init(
authContext: AuthContext,
input: Input
) {
self.authContext = authContext
self.input = input
super.init()
// end init
defer {
load(input: input)
Task {
await load(input: input)
}
}
$output
@ -53,6 +60,7 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
return nil
}
}
.receive(on: DispatchQueue.main)
.assign(to: &$thumbnail)
}
@ -86,13 +94,6 @@ extension AttachmentViewModel {
case png
case jpg
}
public var twitterMediaCategory: TwitterMediaCategory {
switch self {
case .image: return .image
case .video: return .amplifyVideo
}
}
}
public struct SizeLimit {
@ -115,18 +116,13 @@ extension AttachmentViewModel {
case invalidAttachmentType
case attachmentTooLarge
}
public enum TwitterMediaCategory: String {
case image = "TWEET_IMAGE"
case GIF = "TWEET_GIF"
case video = "TWEET_VIDEO"
case amplifyVideo = "AMPLIFY_VIDEO"
}
}
extension AttachmentViewModel {
private func load(input: Input) {
@MainActor
private func load(input: Input) async {
switch input {
case .image(let image):
guard let data = image.pngData() else {
@ -135,32 +131,26 @@ extension AttachmentViewModel {
}
output = .image(data, imageKind: .png)
case .url(let url):
Task { @MainActor in
do {
let output = try await AttachmentViewModel.load(url: url)
self.output = output
} catch {
self.error = error
}
} // end Task
do {
let output = try await AttachmentViewModel.load(url: url)
self.output = output
} catch {
self.error = error
}
case .pickerResult(let pickerResult):
Task { @MainActor in
do {
let output = try await AttachmentViewModel.load(itemProvider: pickerResult.itemProvider)
self.output = output
} catch {
self.error = error
}
} // end Task
do {
let output = try await AttachmentViewModel.load(itemProvider: pickerResult.itemProvider)
self.output = output
} catch {
self.error = error
}
case .itemProvider(let itemProvider):
Task { @MainActor in
do {
let output = try await AttachmentViewModel.load(itemProvider: itemProvider)
self.output = output
} catch {
self.error = error
}
} // end Task
do {
let output = try await AttachmentViewModel.load(itemProvider: itemProvider)
self.output = output
} catch {
self.error = error
}
}
}

View File

@ -325,16 +325,10 @@ extension ComposeContentViewController: PHPickerViewControllerDelegate {
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
// TODO:
// let attachmentServices: [MastodonAttachmentService] = results.map { result in
// let service = MastodonAttachmentService(
// context: context,
// pickerResult: result,
// initialAuthenticationBox: viewModel.authenticationBox
// )
// return service
// }
// viewModel.attachmentServices = viewModel.attachmentServices + attachmentServices
let attachmentViewModels: [AttachmentViewModel] = results.map { result in
AttachmentViewModel(authContext: viewModel.authContext, input: .pickerResult(result))
}
viewModel.attachmentViewModels += attachmentViewModels
}
}

View File

@ -11,6 +11,7 @@ import MastodonAsset
import MastodonCore
import MastodonLocalization
import Stripes
import Kingfisher
public struct ComposeContentView: View {
@ -108,6 +109,9 @@ public struct ComposeContentView: View {
// poll
pollView
.padding(.horizontal, ComposeContentView.margin)
// media
mediaView
.padding(.horizontal, ComposeContentView.margin)
}
.background(
GeometryReader { proxy in
@ -194,6 +198,29 @@ extension ComposeContentView {
}
} // end VStack
}
// MARK: - media
var mediaView: some View {
VStack(spacing: 16) {
ForEach(viewModel.attachmentViewModels, id: \.self) { attachmentViewModel in
Color.clear.aspectRatio(358.0/232.0, contentMode: .fill)
.overlay(
AttachmentView(viewModel: attachmentViewModel) { action in
}
)
.clipShape(Rectangle())
.badgeView(
Button {
viewModel.attachmentViewModels.removeAll(where: { $0 === attachmentViewModel })
} label: {
Image(systemName: "minus.circle.fill")
.foregroundColor(.red)
}
)
} // end ForEach
} // end VStack
}
}
//private struct ScrollOffsetPreferenceKey: PreferenceKey {

View File

@ -77,19 +77,6 @@ struct StatusAttachmentView: View {
}
}
extension View {
func badgeView<Content>(_ content: Content) -> some View where Content: View {
overlay(
ZStack {
content
}
.alignmentGuide(.top) { $0.height / 2 }
.alignmentGuide(.trailing) { $0.width / 2 }
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
)
}
}
/// ref: https://stackoverflow.com/a/57715771/3797903
extension View {
func placeholder<Content: View>(