// // ComposeToolbar.swift // Tusker // // Created by Shadowfacts on 11/12/22. // Copyright © 2022 Shadowfacts. All rights reserved. // import SwiftUI import Pachyderm import TuskerComponents struct ComposeToolbar: View { static let height: CGFloat = 44 private static let visibilityOptions: [MenuPicker.Option] = Visibility.allCases.map { vis in .init(value: vis, title: vis.displayName, subtitle: vis.subtitle, image: UIImage(systemName: vis.unfilledImageName), accessibilityLabel: "Visibility: \(vis.displayName)") } @ObservedObject var draft: OldDraft @EnvironmentObject private var uiState: ComposeUIState @EnvironmentObject private var mastodonController: MastodonController @ObservedObject private var preferences = Preferences.shared @ScaledMetric(relativeTo: .body) private var imageSize: CGFloat = 22 @State private var minWidth: CGFloat? @State private var realWidth: CGFloat? var body: some View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 0) { Button("CW") { draft.contentWarningEnabled.toggle() } .accessibilityLabel(draft.contentWarningEnabled ? "Remove content warning" : "Add content warning") .padding(5) .hoverEffect() MenuPicker(selection: $draft.visibility, options: Self.visibilityOptions, buttonStyle: .iconOnly) // // the button has a bunch of extra space by default, but combined with what we add it's too much .padding(.horizontal, -8) if mastodonController.instanceFeatures.localOnlyPosts { MenuPicker(selection: $draft.localOnly, options: [ .init(value: true, title: "Local-only", subtitle: "Only \(mastodonController.accountInfo!.instanceURL.host!)", image: UIImage(named: "link.broken")), .init(value: false, title: "Federated", image: UIImage(systemName: "link")) ], buttonStyle: .iconOnly) .padding(.horizontal, -8) } if let currentInput = uiState.currentInput, currentInput.toolbarElements.contains(.emojiPicker) { Button(action: self.emojiPickerButtonPressed) { Label("Insert custom emoji", systemImage: "face.smiling") } .labelStyle(.iconOnly) .font(.system(size: imageSize)) .padding(5) .hoverEffect() .transition(.opacity.animation(.linear(duration: 0.2))) } if let currentInput = uiState.currentInput, currentInput.toolbarElements.contains(.formattingButtons), preferences.statusContentType != .plain { Spacer() ForEach(StatusFormat.allCases, id: \.rawValue) { format in Button(action: self.formatAction(format)) { if let imageName = format.imageName { Image(systemName: imageName) .font(.system(size: imageSize)) } else if let (str, attrs) = format.title { let container = try! AttributeContainer(attrs, including: \.uiKit) Text(AttributedString(str, attributes: container)) } } .accessibilityLabel(format.accessibilityLabel) .padding(5) .hoverEffect() .transition(.opacity.animation(.linear(duration: 0.2))) } } Spacer() } .padding(.horizontal, 16) .frame(minWidth: minWidth) .background(GeometryReader { proxy in Color.clear .preference(key: ToolbarWidthPrefKey.self, value: proxy.size.width) .onPreferenceChange(ToolbarWidthPrefKey.self) { width in realWidth = width } }) } .scrollDisabledIfAvailable(realWidth ?? 0 <= minWidth ?? 0) .frame(height: Self.height) .frame(maxWidth: .infinity) .background(.regularMaterial, ignoresSafeAreaEdges: .bottom) .overlay(alignment: .top) { Divider() } .background(GeometryReader { proxy in Color.clear .preference(key: ToolbarWidthPrefKey.self, value: proxy.size.width) .onPreferenceChange(ToolbarWidthPrefKey.self) { width in minWidth = width } }) } private func emojiPickerButtonPressed() { guard uiState.autocompleteState == nil else { return } uiState.shouldEmojiAutocompletionBeginExpanded = true uiState.currentInput?.beginAutocompletingEmoji() } private func formatAction(_ format: StatusFormat) -> () -> Void { { uiState.currentInput?.applyFormat(format) } } } private struct ToolbarWidthPrefKey: PreferenceKey { static var defaultValue: CGFloat? = nil static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) { value = nextValue() } } private extension View { @available(iOS, obsoleted: 16.0) @ViewBuilder func scrollDisabledIfAvailable(_ disabled: Bool) -> some View { if #available(iOS 16.0, *) { self.scrollDisabled(disabled) } else { self } } } struct ComposeToolbar_Previews: PreviewProvider { static var previews: some View { ComposeToolbar(draft: OldDraft(accountID: "")) } }