Tusker/ShareExtension/ShareHostingController.swift

184 lines
6.3 KiB
Swift

//
// ShareHostingController.swift
// ShareExtension
//
// Created by Shadowfacts on 4/17/23.
// Copyright © 2023 Shadowfacts. All rights reserved.
//
import SwiftUI
import ComposeUI
import TuskerComponents
import WebURLFoundationExtras
import Combine
import TuskerPreferences
import Pachyderm
class ShareHostingController: UIHostingController<ShareHostingController.View> {
private static func fetchAvatar(_ url: URL) async -> UIImage? {
guard let (data, _) = try? await URLSession.shared.data(from: url),
let image = UIImage(data: data) else {
return nil
}
#if os(visionOS)
let size: CGFloat = 50 * 2
#else
let size = 50 * UIScreen.main.scale
#endif
return await image.byPreparingThumbnail(ofSize: CGSize(width: size, height: size)) ?? image
}
@ObservableObjectBox private var config = ComposeUIConfig()
private let accountSwitchingState: AccountSwitchingState
private let state: ComposeViewState
init(draft: Draft, mastodonContext: ShareMastodonContext) {
self.accountSwitchingState = AccountSwitchingState(mastodonContext: mastodonContext)
self.state = ComposeViewState(draft: draft)
let rootView = View(
accountSwitchingState: self.accountSwitchingState,
state: state,
config: _config
)
super.init(rootView: rootView)
updateConfig()
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
private func updateConfig() {
var config = ComposeUIConfig()
config.allowSwitchingDrafts = false
config.textSelectionStartsAtBeginning = true
config.backgroundColor = Color(uiColor: .appBackground)
config.groupedBackgroundColor = Color(uiColor: .appGroupedBackground)
config.groupedCellBackgroundColor = Color(uiColor: .appGroupedCellBackground)
config.fillColor = Color(uiColor: .appFill)
switch Preferences.shared.avatarStyle {
case .roundRect:
config.avatarStyle = .roundRect
case .circle:
config.avatarStyle = .circle
}
config.useTwitterKeyboard = Preferences.shared.useTwitterKeyboard
config.contentType = Preferences.shared.statusContentType
config.requireAttachmentDescriptions = Preferences.shared.requireAttachmentDescriptions
config.dismiss = { [unowned self] in self.dismiss(mode: $0) }
config.fetchAvatar = Self.fetchAvatar
config.displayNameLabel = { account, style, _ in
// TODO: move AccountDisplayNameView to TuskerComponents and use that here as well
AnyView(Text(account.displayName).font(.system(style)))
}
self.config = config
}
private func dismiss(mode: DismissMode) {
guard let extensionContext else { return }
switch mode {
case .cancel:
extensionContext.cancelRequest(withError: Error.cancelled)
case .post:
extensionContext.completeRequest(returningItems: nil)
}
}
struct View: SwiftUI.View {
@ObservedObject var accountSwitchingState: AccountSwitchingState
let state: ComposeViewState
@ObservedObject @ObservableObjectBox private var config: ComposeUIConfig
@State private var currentAccount: Account?
fileprivate init(
accountSwitchingState: AccountSwitchingState,
state: ComposeViewState,
config: ObservableObjectBox<ComposeUIConfig>
) {
self.accountSwitchingState = accountSwitchingState
self.state = state
self._config = ObservedObject(wrappedValue: config)
self._currentAccount = State(wrappedValue: accountSwitchingState.currentAccount)
}
var body: some SwiftUI.View {
ComposeView(
state: state,
mastodonController: accountSwitchingState.mastodonContext,
currentAccount: currentAccount,
config: config
)
.onReceive(accountSwitchingState.$mastodonContext) {
state.draft.accountID = $0.accountInfo!.id
}
.onReceive(accountSwitchingState.currentAccountPublisher) {
currentAccount = $0
}
}
}
enum Error: Swift.Error {
case cancelled
}
}
// TODO: put this somewhere instead of just copying it from ComposeHostingController
@MainActor
@propertyWrapper
private final class ObservableObjectBox<T>: ObservableObject {
@Published var wrappedValue: T
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
// todo: shouldn't just copy this from the main Colors.swift
extension UIColor {
static let appBackground = UIColor { traitCollection in
if case .dark = traitCollection.userInterfaceStyle,
!Preferences.shared.pureBlackDarkMode {
return UIColor(hue: 230/360, saturation: 23/100, brightness: 10/100, alpha: 1)
} else {
return .systemBackground
}
}
static let appGroupedBackground = UIColor { traitCollection in
if case .dark = traitCollection.userInterfaceStyle,
!Preferences.shared.pureBlackDarkMode {
if traitCollection.userInterfaceLevel == .elevated {
return UIColor(hue: 230/360, saturation: 23/100, brightness: 10/100, alpha: 1)
} else {
return UIColor(hue: 230/360, saturation: 23/100, brightness: 5/100, alpha: 1)
}
} else {
return .systemGroupedBackground
}
}
static let appGroupedCellBackground = UIColor { traitCollection in
if case .dark = traitCollection.userInterfaceStyle {
if Preferences.shared.pureBlackDarkMode {
return .secondarySystemBackground
} else {
return .appFill
}
} else {
return .systemBackground
}
}
static let appFill = UIColor { traitCollection in
if case .dark = traitCollection.userInterfaceStyle,
!Preferences.shared.pureBlackDarkMode {
return UIColor(hue: 230/360, saturation: 20/100, brightness: 17/100, alpha: 1)
} else {
return .systemFill
}
}
}