2023-04-19 01:55:14 +00:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2023-10-20 02:50:20 +00:00
|
|
|
#if os(visionOS)
|
|
|
|
let size: CGFloat = 50 * 2
|
|
|
|
#else
|
2023-04-23 01:48:12 +00:00
|
|
|
let size = 50 * UIScreen.main.scale
|
2023-10-20 02:50:20 +00:00
|
|
|
#endif
|
2023-04-23 01:48:12 +00:00
|
|
|
return await image.byPreparingThumbnail(ofSize: CGSize(width: size, height: size)) ?? image
|
2023-04-19 01:55:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private let controller: ComposeController
|
|
|
|
|
2023-04-20 02:20:05 +00:00
|
|
|
private var mastodonContextPublisher: CurrentValueSubject<ShareMastodonContext, Never>
|
2023-04-19 01:55:14 +00:00
|
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
|
|
|
|
init(draft: Draft, mastodonContext: ShareMastodonContext) {
|
2023-04-20 02:20:05 +00:00
|
|
|
let mastodonContextPublisher = CurrentValueSubject<ShareMastodonContext, Never>(mastodonContext)
|
|
|
|
self.mastodonContextPublisher = mastodonContextPublisher
|
2023-04-19 01:55:14 +00:00
|
|
|
controller = ComposeController(
|
|
|
|
draft: draft,
|
|
|
|
config: ComposeUIConfig(),
|
|
|
|
mastodonController: mastodonContext,
|
|
|
|
fetchAvatar: Self.fetchAvatar,
|
2023-05-11 13:59:57 +00:00
|
|
|
fetchAttachment: { _ in fatalError("edits aren't allowed in share sheet, can't fetch existing attachment") },
|
2023-04-19 01:55:14 +00:00
|
|
|
fetchStatus: { _ in fatalError("replies aren't allowed in share sheet") },
|
|
|
|
displayNameLabel: { account, style, _ in AnyView(Text(account.displayName).font(.system(style))) },
|
2023-04-20 02:20:05 +00:00
|
|
|
currentAccountContainerView: { AnyView(SwitchAccountContainerView(content: $0, mastodonContextPublisher: mastodonContextPublisher)) },
|
2023-04-19 01:55:14 +00:00
|
|
|
replyContentView: { _, _ in fatalError("replies aren't allowed in share sheet") },
|
|
|
|
emojiImageView: {
|
|
|
|
AnyView(AsyncImage(url: URL($0.url)!) {
|
|
|
|
$0
|
|
|
|
.resizable()
|
|
|
|
.aspectRatio(contentMode: .fit)
|
|
|
|
} placeholder: {
|
|
|
|
Image(systemName: "smiley.fill")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
)
|
|
|
|
super.init(rootView: View(controller: controller))
|
|
|
|
|
|
|
|
updateConfig()
|
|
|
|
|
2023-04-20 02:20:05 +00:00
|
|
|
mastodonContextPublisher
|
|
|
|
.sink { [unowned self] in
|
|
|
|
self.controller.mastodonController = $0
|
|
|
|
self.controller.draft.accountID = $0.accountInfo!.id
|
|
|
|
}
|
|
|
|
.store(in: &cancellables)
|
|
|
|
mastodonContextPublisher
|
|
|
|
.flatMap { $0.$ownAccount }
|
2023-04-19 01:55:14 +00:00
|
|
|
.sink { [unowned self] in self.controller.currentAccount = $0 }
|
|
|
|
.store(in: &cancellables)
|
|
|
|
}
|
|
|
|
|
|
|
|
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) }
|
|
|
|
|
|
|
|
controller.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 {
|
|
|
|
let controller: ComposeController
|
|
|
|
|
|
|
|
var body: some SwiftUI.View {
|
|
|
|
ControllerView(controller: { controller })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Error: Swift.Error {
|
|
|
|
case cancelled
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|