184 lines
6.3 KiB
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
|
|
}
|
|
}
|
|
}
|