Use new compose UI for share extension

This commit is contained in:
Shadowfacts 2025-02-06 13:29:37 -05:00
parent c7c363782b
commit e9e08bdadd
5 changed files with 78 additions and 46 deletions

View File

@ -24,9 +24,10 @@ public struct ComposeUIConfig {
public var groupedBackgroundColor = Color(uiColor: .systemGroupedBackground)
public var groupedCellBackgroundColor = Color(uiColor: .systemBackground)
public var fillColor = Color(uiColor: .systemFill)
public var avatarStyle = AvatarImageView.Style.roundRect
// TODO: remove these in favor of @PreferenceObserving
// Preferences
public var avatarStyle = AvatarImageView.Style.roundRect
public var useTwitterKeyboard = false
public var contentType = StatusContentType.plain
public var requireAttachmentDescriptions = false

View File

@ -36,7 +36,6 @@ private struct NewMainTextViewRepresentable: UIViewRepresentable {
@Environment(\.colorScheme) private var colorScheme
@Environment(\.composeUIConfig.fillColor) private var fillColor
@Environment(\.composeUIConfig.useTwitterKeyboard) private var useTwitterKeyboard
// TODO: test textSelectionStartsAtBeginning
@Environment(\.composeUIConfig.textSelectionStartsAtBeginning) private var textSelectionStartsAtBeginning
func makeUIView(context: Context) -> UITextView {

View File

@ -12,6 +12,7 @@ import TuskerComponents
import WebURLFoundationExtras
import Combine
import TuskerPreferences
import Pachyderm
class ShareHostingController: UIHostingController<ShareHostingController.View> {
private static func fetchAvatar(_ url: URL) async -> UIImage? {
@ -27,48 +28,21 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
return await image.byPreparingThumbnail(ofSize: CGSize(width: size, height: size)) ?? image
}
private let controller: ComposeController
private var mastodonContextPublisher: CurrentValueSubject<ShareMastodonContext, Never>
private var cancellables = Set<AnyCancellable>()
@ObservableObjectBox private var config = ComposeUIConfig()
private let accountSwitchingState: AccountSwitchingState
private let state: ComposeViewState
init(draft: Draft, mastodonContext: ShareMastodonContext) {
let mastodonContextPublisher = CurrentValueSubject<ShareMastodonContext, Never>(mastodonContext)
self.mastodonContextPublisher = mastodonContextPublisher
controller = ComposeController(
draft: draft,
config: ComposeUIConfig(),
mastodonController: mastodonContext,
fetchAvatar: Self.fetchAvatar,
fetchAttachment: { _ in fatalError("edits aren't allowed in share sheet, can't fetch existing attachment") },
fetchStatus: { _ in fatalError("replies aren't allowed in share sheet") },
displayNameLabel: { account, style, _ in AnyView(Text(account.displayName).font(.system(style))) },
currentAccountContainerView: { AnyView(SwitchAccountContainerView(content: $0, mastodonContextPublisher: mastodonContextPublisher)) },
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")
})
}
self.accountSwitchingState = AccountSwitchingState(mastodonContext: mastodonContext)
self.state = ComposeViewState(draft: draft)
let rootView = View(
accountSwitchingState: self.accountSwitchingState,
state: state,
config: _config
)
super.init(rootView: View(controller: controller))
super.init(rootView: rootView)
updateConfig()
mastodonContextPublisher
.sink { [unowned self] in
self.controller.mastodonController = $0
self.controller.draft.accountID = $0.accountInfo!.id
}
.store(in: &cancellables)
mastodonContextPublisher
.flatMap { $0.$ownAccount }
.sink { [unowned self] in self.controller.currentAccount = $0 }
.store(in: &cancellables)
}
required init?(coder aDecoder: NSCoder) {
@ -95,8 +69,13 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
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)))
}
controller.config = config
self.config = config
}
private func dismiss(mode: DismissMode) {
@ -110,10 +89,35 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
}
struct View: SwiftUI.View {
let controller: ComposeController
@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 {
ControllerView(controller: { controller })
ComposeView(
state: state,
mastodonController: accountSwitchingState.mastodonContext,
currentAccount: currentAccount,
config: config
)
.onReceive(accountSwitchingState.$mastodonContext) {
state.draft.accountID = $0.accountInfo!.id
}
.onReceive(accountSwitchingState.currentAccountPublisher) {
currentAccount = $0
}
}
}
@ -122,6 +126,16 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
}
}
// 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

View File

@ -13,9 +13,28 @@ import Pachyderm
import Combine
import ComposeUI
@MainActor
class AccountSwitchingState: ObservableObject {
@Published var mastodonContext: ShareMastodonContext
var currentAccount: Account? {
mastodonContext.ownAccount
}
var currentAccountPublisher: some Publisher<Account, Never> {
$mastodonContext
.flatMap { $0.$ownAccount }
.compactMap { $0 }
}
init(mastodonContext: ShareMastodonContext) {
self.mastodonContext = mastodonContext
}
}
struct SwitchAccountContainerView: View {
let content: AnyView
let mastodonContextPublisher: CurrentValueSubject<ShareMastodonContext, Never>
@ObservedObject var state: AccountSwitchingState
var accounts: [UserAccountInfo] {
UserAccountsManager.shared.accounts
@ -50,7 +69,7 @@ struct SwitchAccountContainerView: View {
}
private func selectAccount(_ account: UserAccountInfo) {
mastodonContextPublisher.send(ShareMastodonContext(accountInfo: account))
state.mastodonContext = ShareMastodonContext(accountInfo: account)
}
}

View File

@ -45,8 +45,7 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
self.mastodonController = mastodonController
self.config = ComposeUIConfig()
self.currentAccount = mastodonController.account
let state = ComposeViewState(draft: draft)
self.state = state
self.state = ComposeViewState(draft: draft)
let rootView = View(
state: state,