From cc2f7f0b8cda33bdceb0774b1cb3dd5845665a33 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Tue, 4 Jul 2023 16:08:11 +0200 Subject: [PATCH] Hide activity indicator in case of no emojis (#1088) --- Mastodon.xcodeproj/project.pbxproj | 4 - .../Compose/View/ComposeToolbarView.swift | 384 ------------------ .../Model/Compose/ComposeStatusSection.swift | 58 --- .../EmojiService+CustomEmojiViewModel.swift | 19 +- .../ComposeContentViewController.swift | 6 +- .../ComposeContentViewModel+DataSource.swift | 4 +- .../View/Utility/ViewLayoutFrame.swift | 6 - 7 files changed, 18 insertions(+), 463 deletions(-) delete mode 100644 Mastodon/Scene/Compose/View/ComposeToolbarView.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 0db030614..f8743b3da 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -395,7 +395,6 @@ DB9F58EC26EF435000E7BBE9 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9F58EB26EF435000E7BBE9 /* AccountViewController.swift */; }; DB9F58EF26EF491E00E7BBE9 /* AccountListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9F58EE26EF491E00E7BBE9 /* AccountListViewModel.swift */; }; DB9F58F126EF512300E7BBE9 /* AccountListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9F58F026EF512300E7BBE9 /* AccountListTableViewCell.swift */; }; - DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; }; DBA4B0F626C269880077136E /* Intents.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DBA4B0F926C269880077136E /* Intents.stringsdict */; }; DBA4B0F726C269880077136E /* Intents.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DBA4B0F926C269880077136E /* Intents.stringsdict */; }; DBA5A53126F08EF000CACBAA /* DragIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5A53026F08EF000CACBAA /* DragIndicatorView.swift */; }; @@ -1103,7 +1102,6 @@ DB9F58EB26EF435000E7BBE9 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; DB9F58EE26EF491E00E7BBE9 /* AccountListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountListViewModel.swift; sourceTree = ""; }; DB9F58F026EF512300E7BBE9 /* AccountListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountListTableViewCell.swift; sourceTree = ""; }; - DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = ""; }; DBA4B0D326BD10AC0077136E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Intents.strings"; sourceTree = ""; }; DBA4B0D626BD10AD0077136E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; DBA4B0D726BD10F40077136E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Intents.strings; sourceTree = ""; }; @@ -2166,7 +2164,6 @@ DB55D32225FB4D320002F825 /* View */ = { isa = PBXGroup; children = ( - DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */, DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */, DB9A486B26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift */, DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */, @@ -3839,7 +3836,6 @@ DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */, DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */, DBEFCD74282A130400C0ABEA /* ReportReasonViewModel.swift in Sources */, - DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */, DBFEEC96279BDC67004F81DD /* ProfileAboutViewController.swift in Sources */, DB63F74F2799405600455B82 /* SearchHistoryViewModel+Diffable.swift in Sources */, DB0EF72B26FDB1D200347686 /* SidebarListCollectionViewCell.swift in Sources */, diff --git a/Mastodon/Scene/Compose/View/ComposeToolbarView.swift b/Mastodon/Scene/Compose/View/ComposeToolbarView.swift deleted file mode 100644 index a993da228..000000000 --- a/Mastodon/Scene/Compose/View/ComposeToolbarView.swift +++ /dev/null @@ -1,384 +0,0 @@ -// -// ComposeToolbarView.swift -// Mastodon -// -// Created by MainasuK Cirno on 2021-3-12. -// - -import os.log -import UIKit -import Combine -import MastodonSDK -import MastodonAsset -import MastodonCore -import MastodonUI -import MastodonLocalization - -protocol ComposeToolbarViewDelegate: AnyObject { - func composeToolbarView(_ composeToolbarView: ComposeToolbarView, mediaButtonDidPressed sender: Any, mediaSelectionType type: ComposeToolbarView.MediaSelectionType) - func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: Any) - func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: Any) - func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: Any) - func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: Any, visibilitySelectionType type: ComposeToolbarView.VisibilitySelectionType) -} - -final class ComposeToolbarView: UIView { - - var disposeBag = Set() - - static let toolbarButtonSize: CGSize = CGSize(width: 44, height: 44) - static let toolbarHeight: CGFloat = 44 - - weak var delegate: ComposeToolbarViewDelegate? - - // barButtonItem - private(set) lazy var mediaBarButtonItem: UIBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.image = UIImage(systemName: "photo") - barButtonItem.accessibilityLabel = L10n.Scene.Compose.Accessibility.appendAttachment - return barButtonItem - }() - - let pollBarButtonItem: UIBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.image = UIImage(systemName: "list.bullet") - barButtonItem.accessibilityLabel = L10n.Scene.Compose.Accessibility.appendPoll - return barButtonItem - }() - - let contentWarningBarButtonItem: UIBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.image = UIImage(systemName: "exclamationmark.shield") - barButtonItem.accessibilityLabel = L10n.Scene.Compose.Accessibility.enableContentWarning - return barButtonItem - }() - - let visibilityBarButtonItem: UIBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.image = UIImage(systemName: "person.3") - barButtonItem.accessibilityLabel = L10n.Scene.Compose.Accessibility.postVisibilityMenu - return barButtonItem - }() - - // button - - let mediaButton: UIButton = { - let button = HighlightDimmableButton() - ComposeToolbarView.configureToolbarButtonAppearance(button: button) - button.setImage(UIImage(systemName: "photo", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular)), for: .normal) - button.accessibilityLabel = L10n.Scene.Compose.Accessibility.appendAttachment - return button - }() - - let pollButton: UIButton = { - let button = HighlightDimmableButton(type: .custom) - ComposeToolbarView.configureToolbarButtonAppearance(button: button) - button.setImage(UIImage(systemName: "list.bullet", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium)), for: .normal) - button.accessibilityLabel = L10n.Scene.Compose.Accessibility.appendPoll - return button - }() - - let emojiButton: UIButton = { - let button = HighlightDimmableButton() - ComposeToolbarView.configureToolbarButtonAppearance(button: button) - let image = Asset.Human.faceSmilingAdaptive.image - .af.imageScaled(to: CGSize(width: 20, height: 20)) - .withRenderingMode(.alwaysTemplate) - button.setImage(image, for: .normal) - button.accessibilityLabel = L10n.Scene.Compose.Accessibility.customEmojiPicker - return button - }() - - let contentWarningButton: UIButton = { - let button = HighlightDimmableButton() - ComposeToolbarView.configureToolbarButtonAppearance(button: button) - button.setImage(UIImage(systemName: "exclamationmark.shield", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular)), for: .normal) - button.accessibilityLabel = L10n.Scene.Compose.Accessibility.enableContentWarning - return button - }() - - let visibilityButton: UIButton = { - let button = HighlightDimmableButton() - ComposeToolbarView.configureToolbarButtonAppearance(button: button) - button.setImage(UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium)), for: .normal) - button.accessibilityLabel = L10n.Scene.Compose.Accessibility.postVisibilityMenu - return button - }() - - let characterCountLabel: UILabel = { - let label = UILabel() - label.font = .systemFont(ofSize: 15, weight: .regular) - label.text = "500" - label.textColor = Asset.Colors.Label.secondary.color - label.accessibilityLabel = L10n.A11y.Plural.Count.inputLimitRemains(500) - return label - }() - - let activeVisibilityType = CurrentValueSubject(.public) - - override init(frame: CGRect) { - super.init(frame: frame) - _init() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - _init() - } - -} - -extension ComposeToolbarView { - - private func _init() { - setupBackgroundColor(theme: ThemeService.shared.currentTheme.value) - ThemeService.shared.currentTheme - .receive(on: DispatchQueue.main) - .sink { [weak self] theme in - guard let self = self else { return } - self.setupBackgroundColor(theme: theme) - } - .store(in: &disposeBag) - - let stackView = UIStackView() - stackView.axis = .horizontal - stackView.spacing = 0 - stackView.distribution = .fillEqually - stackView.translatesAutoresizingMaskIntoConstraints = false - addSubview(stackView) - NSLayoutConstraint.activate([ - stackView.centerYAnchor.constraint(equalTo: centerYAnchor), - layoutMarginsGuide.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 8), // tweak button margin offset - ]) - - let buttons = [ - mediaButton, - pollButton, - emojiButton, - contentWarningButton, - visibilityButton, - ] - buttons.forEach { button in - button.translatesAutoresizingMaskIntoConstraints = false - stackView.addArrangedSubview(button) - NSLayoutConstraint.activate([ - button.widthAnchor.constraint(equalToConstant: 44), - button.heightAnchor.constraint(equalToConstant: 44), - ]) - } - - characterCountLabel.translatesAutoresizingMaskIntoConstraints = false - addSubview(characterCountLabel) - NSLayoutConstraint.activate([ - characterCountLabel.topAnchor.constraint(equalTo: topAnchor), - characterCountLabel.leadingAnchor.constraint(greaterThanOrEqualTo: stackView.trailingAnchor, constant: 8), - characterCountLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor), - characterCountLabel.bottomAnchor.constraint(equalTo: bottomAnchor), - ]) - characterCountLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) - - mediaBarButtonItem.menu = createMediaContextMenu() - mediaButton.menu = createMediaContextMenu() - mediaButton.showsMenuAsPrimaryAction = true - pollBarButtonItem.target = self - pollBarButtonItem.action = #selector(ComposeToolbarView.pollButtonDidPressed(_:)) - pollButton.addTarget(self, action: #selector(ComposeToolbarView.pollButtonDidPressed(_:)), for: .touchUpInside) - emojiButton.addTarget(self, action: #selector(ComposeToolbarView.emojiButtonDidPressed(_:)), for: .touchUpInside) - contentWarningBarButtonItem.target = self - contentWarningBarButtonItem.action = #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:)) - contentWarningButton.addTarget(self, action: #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:)), for: .touchUpInside) - visibilityBarButtonItem.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle) - visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle) - visibilityButton.showsMenuAsPrimaryAction = true - - updateToolbarButtonUserInterfaceStyle() - - // update menu when selected visibility type changed - activeVisibilityType - .receive(on: RunLoop.main) - .sink { [weak self] type in - guard let self = self else { return } - self.visibilityBarButtonItem.menu = self.createVisibilityContextMenu(interfaceStyle: self.traitCollection.userInterfaceStyle) - self.visibilityButton.menu = self.createVisibilityContextMenu(interfaceStyle: self.traitCollection.userInterfaceStyle) - } - .store(in: &disposeBag) - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - updateToolbarButtonUserInterfaceStyle() - } - -} - -extension ComposeToolbarView { - enum MediaSelectionType: String { - case camera - case photoLibrary - case browse - } - - enum VisibilitySelectionType: String, CaseIterable { - case `public` - // TODO: remove unlisted option from codebase - // case unlisted - case `private` - case direct - - var title: String { - switch self { - case .public: return L10n.Scene.Compose.Visibility.public - // case .unlisted: return L10n.Scene.Compose.Visibility.unlisted - case .private: return L10n.Scene.Compose.Visibility.private - case .direct: return L10n.Scene.Compose.Visibility.direct - } - } - - func image(interfaceStyle: UIUserInterfaceStyle) -> UIImage { - switch self { - case .public: return UIImage(systemName: "globe", withConfiguration: UIImage.SymbolConfiguration(pointSize: 19, weight: .medium))! - // case .unlisted: return UIImage(systemName: "eye.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular))! - case .private: - switch interfaceStyle { - case .light: return UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))! - default: return UIImage(systemName: "person.3.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))! - } - case .direct: return UIImage(systemName: "at", withConfiguration: UIImage.SymbolConfiguration(pointSize: 19, weight: .regular))! - } - } - - var visibility: Mastodon.Entity.Status.Visibility { - switch self { - case .public: return .public - // case .unlisted: return .unlisted - case .private: return .private - case .direct: return .direct - } - } - } -} - -extension ComposeToolbarView { - - private func setupBackgroundColor(theme: Theme) { - backgroundColor = theme.composeToolbarBackgroundColor - } - - private static func configureToolbarButtonAppearance(button: UIButton) { - button.tintColor = ThemeService.tintColor - button.setBackgroundImage(.placeholder(size: ComposeToolbarView.toolbarButtonSize, color: .systemFill), for: .highlighted) - button.layer.masksToBounds = true - button.layer.cornerRadius = 5 - button.layer.cornerCurve = .continuous - } - - private func updateToolbarButtonUserInterfaceStyle() { - // reset emoji - let emojiButtonImage = Asset.Human.faceSmilingAdaptive.image - .af.imageScaled(to: CGSize(width: 20, height: 20)) - .withRenderingMode(.alwaysTemplate) - emojiButton.setImage(emojiButtonImage, for: .normal) - - switch traitCollection.userInterfaceStyle { - case .light: - mediaBarButtonItem.image = UIImage(systemName: "photo") - mediaButton.setImage(UIImage(systemName: "photo", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal) - contentWarningBarButtonItem.image = UIImage(systemName: "exclamationmark.shield") - contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal) - - case .dark: - mediaBarButtonItem.image = UIImage(systemName: "photo.fill") - mediaButton.setImage(UIImage(systemName: "photo.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal) - contentWarningBarButtonItem.image = UIImage(systemName: "exclamationmark.shield.fill") - contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal) - - default: - assertionFailure() - } - - visibilityBarButtonItem.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle) - visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle) - } - - private func createMediaContextMenu() -> UIMenu { - var children: [UIMenuElement] = [] - let photoLibraryAction = UIAction(title: L10n.Scene.Compose.MediaSelection.photoLibrary, image: UIImage(systemName: "rectangle.on.rectangle"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in - guard let self = self else { return } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .photoLibrary", ((#file as NSString).lastPathComponent), #line, #function) - self.delegate?.composeToolbarView(self, mediaButtonDidPressed: self.mediaButton, mediaSelectionType: .photoLibrary) - } - children.append(photoLibraryAction) - if UIImagePickerController.isSourceTypeAvailable(.camera) { - let cameraAction = UIAction(title: L10n.Scene.Compose.MediaSelection.camera, image: UIImage(systemName: "camera"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { [weak self] _ in - guard let self = self else { return } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .camera", ((#file as NSString).lastPathComponent), #line, #function) - self.delegate?.composeToolbarView(self, mediaButtonDidPressed: self.mediaButton, mediaSelectionType: .camera) - }) - children.append(cameraAction) - } - let browseAction = UIAction(title: L10n.Scene.Compose.MediaSelection.browse, image: UIImage(systemName: "ellipsis"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in - guard let self = self else { return } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .browse", ((#file as NSString).lastPathComponent), #line, #function) - self.delegate?.composeToolbarView(self, mediaButtonDidPressed: self.mediaButton, mediaSelectionType: .browse) - } - children.append(browseAction) - - return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children) - } - - private func createVisibilityContextMenu(interfaceStyle: UIUserInterfaceStyle) -> UIMenu { - let children: [UIMenuElement] = VisibilitySelectionType.allCases.map { type in - let state: UIMenuElement.State = activeVisibilityType.value == type ? .on : .off - return UIAction(title: type.title, image: type.image(interfaceStyle: interfaceStyle), identifier: nil, discoverabilityTitle: nil, attributes: [], state: state) { [weak self] action in - guard let self = self else { return } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: visibilitySelectionType: %s", ((#file as NSString).lastPathComponent), #line, #function, type.rawValue) - self.delegate?.composeToolbarView(self, visibilityButtonDidPressed: self.visibilityButton, visibilitySelectionType: type) - } - } - return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children) - } - -} - -extension ComposeToolbarView { - - @objc private func pollButtonDidPressed(_ sender: Any) { - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) - delegate?.composeToolbarView(self, pollButtonDidPressed: sender) - } - - @objc private func emojiButtonDidPressed(_ sender: Any) { - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) - delegate?.composeToolbarView(self, emojiButtonDidPressed: sender) - } - - @objc private func contentWarningButtonDidPressed(_ sender: Any) { - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) - delegate?.composeToolbarView(self, contentWarningButtonDidPressed: sender) - } - -} - -#if canImport(SwiftUI) && DEBUG -import SwiftUI - -struct ComposeToolbarView_Previews: PreviewProvider { - - static var previews: some View { - UIViewPreview(width: 375) { - let toolbarView = ComposeToolbarView() - toolbarView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - toolbarView.widthAnchor.constraint(equalToConstant: 375).priority(.defaultHigh), - toolbarView.heightAnchor.constraint(equalToConstant: 64).priority(.defaultHigh), - ]) - return toolbarView - } - .previewLayout(.fixed(width: 375, height: 100)) - } - -} - -#endif - diff --git a/MastodonSDK/Sources/MastodonCore/Model/Compose/ComposeStatusSection.swift b/MastodonSDK/Sources/MastodonCore/Model/Compose/ComposeStatusSection.swift index 12dc88053..b91183e90 100644 --- a/MastodonSDK/Sources/MastodonCore/Model/Compose/ComposeStatusSection.swift +++ b/MastodonSDK/Sources/MastodonCore/Model/Compose/ComposeStatusSection.swift @@ -20,43 +20,6 @@ public enum ComposeStatusSection: Equatable, Hashable { case poll } -extension ComposeStatusSection { - -// static func configure( -// cell: ComposeStatusContentTableViewCell, -// attribute: ComposeStatusItem.ComposeStatusAttribute -// ) { -// cell.prepa -// // set avatar -// attribute.avatarURL -// .receive(on: DispatchQueue.main) -// .sink { avatarURL in -// cell.statusView.configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: avatarURL)) -// } -// .store(in: &cell.disposeBag) -// // set display name and username -// Publishers.CombineLatest3( -// attribute.displayName, -// attribute.emojiMeta, -// attribute.username -// ) -// .receive(on: DispatchQueue.main) -// .sink { displayName, emojiMeta, username in -// do { -// let mastodonContent = MastodonContent(content: displayName ?? " ", emojis: emojiMeta) -// let metaContent = try MastodonMetaContent.convert(document: mastodonContent) -// cell.statusView.nameLabel.configure(content: metaContent) -// } catch { -// let metaContent = PlaintextMetaContent(string: " ") -// cell.statusView.nameLabel.configure(content: metaContent) -// } -// cell.statusView.usernameLabel.text = username.flatMap { "@" + $0 } ?? " " -// } -// .store(in: &cell.disposeBag) -// } - -} - public protocol CustomEmojiReplaceableTextInput: UITextInput & UIResponder { var inputView: UIView? { get set } } @@ -71,24 +34,3 @@ public class CustomEmojiReplaceableTextInputReference { extension UITextField: CustomEmojiReplaceableTextInput { } extension UITextView: CustomEmojiReplaceableTextInput { } - -extension ComposeStatusSection { - -// static func configureCustomEmojiPicker( -// viewModel: CustomEmojiPickerInputViewModel?, -// customEmojiReplaceableTextInput: CustomEmojiReplaceableTextInput, -// disposeBag: inout Set -// ) { -// guard let viewModel = viewModel else { return } -// viewModel.isCustomEmojiComposing -// .receive(on: DispatchQueue.main) -// .sink { [weak viewModel] isCustomEmojiComposing in -// guard let viewModel = viewModel else { return } -// customEmojiReplaceableTextInput.inputView = isCustomEmojiComposing ? viewModel.customEmojiPickerInputView : nil -// customEmojiReplaceableTextInput.reloadInputViews() -// viewModel.append(customEmojiReplaceableTextInput: customEmojiReplaceableTextInput) -// } -// .store(in: &disposeBag) -// } - -} diff --git a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel.swift b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel.swift index 38e1e1f7a..0cba10d6d 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel.swift @@ -32,8 +32,8 @@ extension EmojiService { stateMachine.enter(LoadState.Initial.self) return stateMachine }() - public let emojis = CurrentValueSubject<[Mastodon.Entity.Emoji], Never>([]) - public let emojiDict = CurrentValueSubject<[String: [Mastodon.Entity.Emoji]], Never>([:]) + public let emojis = CurrentValueSubject<[Mastodon.Entity.Emoji]?, Never>(nil) + public let emojiDict = CurrentValueSubject<[String: [Mastodon.Entity.Emoji]]?, Never>(nil) public let emojiMapping = CurrentValueSubject<[String: String], Never>([:]) public let emojiTrie = CurrentValueSubject?, Never>(nil) @@ -44,12 +44,17 @@ extension EmojiService { self.service = service emojis - .map { Dictionary(grouping: $0, by: { $0.shortcode }) } + .compactMap { value in + guard let value else { return nil } + return Dictionary(grouping: value, by: { value in value.shortcode }) + } .assign(to: \.value, on: emojiDict) .store(in: &disposeBag) emojiDict - .map { dict in + .compactMap { dict in + guard let dict else { return nil } + var mapping: [String: String] = [:] for (key, values) in dict { guard let emoji = values.first else { continue } @@ -61,8 +66,8 @@ extension EmojiService { .store(in: &disposeBag) emojis - .map { emojis -> Trie? in - guard !emojis.isEmpty else { return nil } + .compactMap { emojis -> Trie? in + guard let emojis, emojis.isEmpty == false else { return nil } var trie: Trie = Trie() for emoji in emojis { let key = emoji.shortcode.lowercased() @@ -84,7 +89,7 @@ extension EmojiService { } } - return emojiDict.value[shortcode]?.first + return emojiDict.value?[shortcode]?.first } } diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift index 41813e232..787847705 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift @@ -272,10 +272,10 @@ extension ComposeContentViewController { .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] emojis in guard let self = self else { return } - if emojis.isEmpty { - self.customEmojiPickerInputView.activityIndicatorView.startAnimating() - } else { + if emojis != nil { self.customEmojiPickerInputView.activityIndicatorView.stopAnimating() + } else { + self.customEmojiPickerInputView.activityIndicatorView.startAnimating() } }) .store(in: &disposeBag) diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel+DataSource.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel+DataSource.swift index 0cf093339..9f036643b 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel+DataSource.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel+DataSource.swift @@ -111,7 +111,9 @@ extension ComposeContentViewModel { // Don't block the main queue .receive(on: DispatchQueue.global(qos: .userInteractive)) // Sort emojis - .map({ (emojis) -> [Mastodon.Entity.Emoji] in + .compactMap({ (emojis) -> [Mastodon.Entity.Emoji]? in + guard let emojis else { return nil } + return emojis.sorted { a, b in a.shortcode.lowercased() < b.shortcode.lowercased() } diff --git a/MastodonSDK/Sources/MastodonUI/View/Utility/ViewLayoutFrame.swift b/MastodonSDK/Sources/MastodonUI/View/Utility/ViewLayoutFrame.swift index 183364abc..e97453fdb 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Utility/ViewLayoutFrame.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Utility/ViewLayoutFrame.swift @@ -30,7 +30,6 @@ public struct ViewLayoutFrame { extension ViewLayoutFrame { public mutating func update(view: UIView) { guard view.window != nil else { - logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): layoutFrame update for a view without attached window. Skip this invalid update") return } @@ -48,10 +47,5 @@ extension ViewLayoutFrame { if self.readableContentLayoutFrame != readableContentLayoutFrame { self.readableContentLayoutFrame = readableContentLayoutFrame } - - logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): layoutFrame: \(layoutFrame.debugDescription)") - logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): safeAreaLayoutFrame: \(safeAreaLayoutFrame.debugDescription)") - logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): readableContentLayoutFrame: \(readableContentLayoutFrame.debugDescription)") - } }