feat: restore emoji picker for post compose
This commit is contained in:
parent
e7ef0f79c7
commit
88307057c0
|
@ -151,7 +151,6 @@
|
|||
DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD44325F26CCC004CFCFC /* PickServerSection.swift */; };
|
||||
DB1FD45025F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD44F25F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift */; };
|
||||
DB1FD45A25F27898004CFCFC /* CategoryPickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */; };
|
||||
DB221B16260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB221B15260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift */; };
|
||||
DB22C92228E700A10082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92128E700A10082A9E9 /* MastodonSDK */; };
|
||||
DB22C92428E700A80082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92328E700A80082A9E9 /* MastodonSDK */; };
|
||||
DB22C92628E700AF0082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92528E700AF0082A9E9 /* MastodonSDK */; };
|
||||
|
@ -185,9 +184,6 @@
|
|||
DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; };
|
||||
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
|
||||
DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB443CD32694627B00159B29 /* AppearanceView.swift */; };
|
||||
DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */; };
|
||||
DB447691260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */; };
|
||||
DB447697260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */; };
|
||||
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4481B825EE289600BEFB67 /* UITableView.swift */; };
|
||||
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */; };
|
||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */; };
|
||||
|
@ -680,7 +676,6 @@
|
|||
DB1FD44F25F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonPickServerViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerItem.swift; sourceTree = "<group>"; };
|
||||
DB1FD45F25F278AF004CFCFC /* CategoryPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerSection.swift; sourceTree = "<group>"; };
|
||||
DB221B15260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputViewModel.swift; sourceTree = "<group>"; };
|
||||
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||
DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollExpiresOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
|
@ -717,9 +712,6 @@
|
|||
DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = "<group>"; };
|
||||
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = "<group>"; };
|
||||
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = "<group>"; };
|
||||
DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerItemCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerHeaderCollectionReusableView.swift; sourceTree = "<group>"; };
|
||||
DB4481B825EE289600BEFB67 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = "<group>"; };
|
||||
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = "<group>"; };
|
||||
DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = "<group>"; };
|
||||
|
@ -1876,8 +1868,6 @@
|
|||
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */,
|
||||
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */,
|
||||
DB9A486B26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift */,
|
||||
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */,
|
||||
DB221B15260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift */,
|
||||
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */,
|
||||
);
|
||||
path = View;
|
||||
|
@ -2157,8 +2147,6 @@
|
|||
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */,
|
||||
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */,
|
||||
DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */,
|
||||
DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */,
|
||||
DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */,
|
||||
);
|
||||
path = CollectionViewCell;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3335,7 +3323,6 @@
|
|||
DB98EB6227B215EB0082E365 /* ReportResultViewController.swift in Sources */,
|
||||
DB6B74F4272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift in Sources */,
|
||||
DB6B74F2272FB67600C70B6E /* FollowerListViewModel.swift in Sources */,
|
||||
DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */,
|
||||
0F20222D261457EE000C64BF /* HashtagTimelineViewModel+State.swift in Sources */,
|
||||
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */,
|
||||
5B90C462262599800002E742 /* SettingsSectionHeader.swift in Sources */,
|
||||
|
@ -3349,7 +3336,6 @@
|
|||
DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */,
|
||||
DBB525562611EDCA002F1F29 /* UserTimelineViewModel.swift in Sources */,
|
||||
DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */,
|
||||
DB221B16260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift in Sources */,
|
||||
DB98EB5C27B10A730082E365 /* ReportSupplementaryViewModel.swift in Sources */,
|
||||
DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */,
|
||||
DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */,
|
||||
|
@ -3433,7 +3419,6 @@
|
|||
2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */,
|
||||
2D4AD89C263165B500613EFC /* SuggestionAccountCollectionViewCell.swift in Sources */,
|
||||
DB98EB6927B21A7C0082E365 /* ReportResultActionTableViewCell.swift in Sources */,
|
||||
DB447691260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift in Sources */,
|
||||
DB9282B225F3222800823B15 /* PickServerEmptyStateView.swift in Sources */,
|
||||
DB697DDF278F524F004EF2F7 /* DataSourceFacade+Profile.swift in Sources */,
|
||||
DB1FD45025F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift in Sources */,
|
||||
|
@ -3457,7 +3442,6 @@
|
|||
DB63F74D27993F5B00455B82 /* SearchHistoryUserCollectionViewCell.swift in Sources */,
|
||||
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
||||
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
|
||||
DB447697260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift in Sources */,
|
||||
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
|
||||
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
|
||||
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
|
||||
|
|
|
@ -95,11 +95,7 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
// }
|
||||
// }
|
||||
//
|
||||
// // CustomEmojiPickerView
|
||||
// let customEmojiPickerInputView: CustomEmojiPickerInputView = {
|
||||
// let view = CustomEmojiPickerInputView(frame: CGRect(x: 0, y: 0, width: 0, height: 300), inputViewStyle: .keyboard)
|
||||
// return view
|
||||
// }()
|
||||
|
||||
//
|
||||
// let composeToolbarView = ComposeToolbarView()
|
||||
// var composeToolbarViewBottomLayoutConstraint: NSLayoutConstraint!
|
||||
|
@ -107,7 +103,6 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
//
|
||||
//
|
||||
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
@ -225,13 +220,6 @@ extension ComposeViewController {
|
|||
// }
|
||||
// .store(in: &disposeBag)
|
||||
|
||||
// customEmojiPickerInputView.collectionView.delegate = self
|
||||
// viewModel.customEmojiPickerInputViewModel.customEmojiPickerInputView = customEmojiPickerInputView
|
||||
// viewModel.setupCustomEmojiPickerDiffableDataSource(
|
||||
// for: customEmojiPickerInputView.collectionView,
|
||||
// dependency: self
|
||||
// )
|
||||
|
||||
// viewModel.composeStatusContentTableViewCell.delegate = self
|
||||
//
|
||||
// // update layout when keyboard show/dismiss
|
||||
|
@ -350,19 +338,6 @@ extension ComposeViewController {
|
|||
// }
|
||||
// .store(in: &disposeBag)
|
||||
//
|
||||
// // bind custom emoji picker UI
|
||||
// viewModel.customEmojiViewModel?.emojis
|
||||
// .receive(on: DispatchQueue.main)
|
||||
// .sink(receiveValue: { [weak self] emojis in
|
||||
// guard let self = self else { return }
|
||||
// if emojis.isEmpty {
|
||||
// self.customEmojiPickerInputView.activityIndicatorView.startAnimating()
|
||||
// } else {
|
||||
// self.customEmojiPickerInputView.activityIndicatorView.stopAnimating()
|
||||
// }
|
||||
// })
|
||||
// .store(in: &disposeBag)
|
||||
//
|
||||
// configureToolbarDisplay(keyboardHasShortcutBar: keyboardHasShortcutBar.value)
|
||||
// Publishers.CombineLatest(
|
||||
// keyboardHasShortcutBar,
|
||||
|
@ -694,30 +669,6 @@ extension ComposeViewController {
|
|||
|
||||
//// MARK: - UITableViewDelegate
|
||||
//extension ComposeViewController: UITableViewDelegate { }
|
||||
//
|
||||
//// MARK: - UICollectionViewDelegate
|
||||
//extension ComposeViewController: UICollectionViewDelegate {
|
||||
//
|
||||
// func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, indexPath.debugDescription)
|
||||
//
|
||||
// if collectionView === customEmojiPickerInputView.collectionView {
|
||||
// guard let diffableDataSource = viewModel.customEmojiPickerDiffableDataSource else { return }
|
||||
// let item = diffableDataSource.itemIdentifier(for: indexPath)
|
||||
// guard case let .emoji(attribute) = item else { return }
|
||||
// let emoji = attribute.emoji
|
||||
//
|
||||
// // make click sound
|
||||
// UIDevice.current.playInputClick()
|
||||
//
|
||||
// // retrieve active text input and insert emoji
|
||||
// // the trailing space is REQUIRED to make regex happy
|
||||
// _ = viewModel.customEmojiPickerInputViewModel.insertText(":\(emoji.shortcode): ")
|
||||
// } else {
|
||||
// // do nothing
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
||||
|
@ -877,49 +828,7 @@ extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
|||
// return true
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// MARK: - AutoCompleteViewControllerDelegate
|
||||
//extension ComposeViewController: AutoCompleteViewControllerDelegate {
|
||||
// func autoCompleteViewController(_ viewController: AutoCompleteViewController, didSelectItem item: AutoCompleteItem) {
|
||||
// guard let info = viewModel.autoCompleteInfo else { return }
|
||||
// let _replacedText: String? = {
|
||||
// var text: String
|
||||
// switch item {
|
||||
// case .hashtag(let hashtag):
|
||||
// text = "#" + hashtag.name
|
||||
// case .hashtagV1(let hashtagName):
|
||||
// text = "#" + hashtagName
|
||||
// case .account(let account):
|
||||
// text = "@" + account.acct
|
||||
// case .emoji(let emoji):
|
||||
// text = ":" + emoji.shortcode + ":"
|
||||
// case .bottomLoader:
|
||||
// return nil
|
||||
// }
|
||||
// return text
|
||||
// }()
|
||||
// guard let replacedText = _replacedText else { return }
|
||||
// guard let text = textEditorView.textView.text else { return }
|
||||
//
|
||||
// let range = NSRange(info.toHighlightEndRange, in: text)
|
||||
// textEditorView.textStorage.replaceCharacters(in: range, with: replacedText)
|
||||
// DispatchQueue.main.async {
|
||||
// self.textEditorView.textView.insertText(" ") // trigger textView delegate update
|
||||
// }
|
||||
// viewModel.autoCompleteInfo = nil
|
||||
//
|
||||
// switch item {
|
||||
// case .emoji, .bottomLoader:
|
||||
// break
|
||||
// default:
|
||||
// // set selected range except emoji
|
||||
// let newRange = NSRange(location: range.location + (replacedText as NSString).length, length: 0)
|
||||
// guard textEditorView.textStorage.length <= newRange.location else { return }
|
||||
// textEditorView.textView.selectedRange = newRange
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
|
||||
//extension ComposeViewController {
|
||||
// override var keyCommands: [UIKeyCommand]? {
|
||||
// composeKeyCommands
|
||||
|
|
|
@ -51,43 +51,6 @@ extension ComposeViewModel {
|
|||
// // setup data source
|
||||
// tableView.dataSource = self
|
||||
// }
|
||||
//
|
||||
// func setupCustomEmojiPickerDiffableDataSource(
|
||||
// for collectionView: UICollectionView,
|
||||
// dependency: NeedsDependency
|
||||
// ) {
|
||||
// let diffableDataSource = CustomEmojiPickerSection.collectionViewDiffableDataSource(
|
||||
// for: collectionView,
|
||||
// dependency: dependency
|
||||
// )
|
||||
// self.customEmojiPickerDiffableDataSource = diffableDataSource
|
||||
//
|
||||
// let _domain = customEmojiViewModel?.domain
|
||||
// customEmojiViewModel?.emojis
|
||||
// .receive(on: DispatchQueue.main)
|
||||
// .sink { [weak self, weak diffableDataSource] emojis in
|
||||
// guard let _ = self else { return }
|
||||
// guard let diffableDataSource = diffableDataSource else { return }
|
||||
//
|
||||
// var snapshot = NSDiffableDataSourceSnapshot<CustomEmojiPickerSection, CustomEmojiPickerItem>()
|
||||
// let domain = _domain?.uppercased() ?? " "
|
||||
// let customEmojiSection = CustomEmojiPickerSection.emoji(name: domain)
|
||||
// snapshot.appendSections([customEmojiSection])
|
||||
// let items: [CustomEmojiPickerItem] = {
|
||||
// var items = [CustomEmojiPickerItem]()
|
||||
// for emoji in emojis where emoji.visibleInPicker {
|
||||
// let attribute = CustomEmojiPickerItem.CustomEmojiAttribute(emoji: emoji)
|
||||
// let item = CustomEmojiPickerItem.emoji(attribute: attribute)
|
||||
// items.append(item)
|
||||
// }
|
||||
// return items
|
||||
// }()
|
||||
// snapshot.appendItems(items, toSection: customEmojiSection)
|
||||
//
|
||||
// diffableDataSource.apply(snapshot)
|
||||
// }
|
||||
// .store(in: &disposeBag)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,6 @@ final class ComposeViewModel: NSObject {
|
|||
// // for mention: "@<mention> "
|
||||
// var preInsertedContent: String?
|
||||
//
|
||||
// // custom emojis
|
||||
// let customEmojiViewModel: EmojiService.CustomEmojiViewModel?
|
||||
// let customEmojiPickerInputViewModel = CustomEmojiPickerInputViewModel()
|
||||
// @Published var isLoadingCustomEmoji = false
|
||||
//
|
||||
// // attachment
|
||||
// @Published var attachmentServices: [MastodonAttachmentService] = []
|
||||
//
|
||||
|
|
|
@ -8,28 +8,28 @@
|
|||
import Foundation
|
||||
import MastodonSDK
|
||||
|
||||
enum CustomEmojiPickerItem {
|
||||
public enum CustomEmojiPickerItem {
|
||||
case emoji(attribute: CustomEmojiAttribute)
|
||||
}
|
||||
|
||||
extension CustomEmojiPickerItem: Equatable, Hashable { }
|
||||
|
||||
extension CustomEmojiPickerItem {
|
||||
final class CustomEmojiAttribute: Equatable, Hashable {
|
||||
let id = UUID()
|
||||
public final class CustomEmojiAttribute: Equatable, Hashable {
|
||||
public let id = UUID()
|
||||
|
||||
let emoji: Mastodon.Entity.Emoji
|
||||
public let emoji: Mastodon.Entity.Emoji
|
||||
|
||||
init(emoji: Mastodon.Entity.Emoji) {
|
||||
public init(emoji: Mastodon.Entity.Emoji) {
|
||||
self.emoji = emoji
|
||||
}
|
||||
|
||||
static func == (lhs: CustomEmojiPickerItem.CustomEmojiAttribute, rhs: CustomEmojiPickerItem.CustomEmojiAttribute) -> Bool {
|
||||
public static func == (lhs: CustomEmojiPickerItem.CustomEmojiAttribute, rhs: CustomEmojiPickerItem.CustomEmojiAttribute) -> Bool {
|
||||
return lhs.id == rhs.id &&
|
||||
lhs.emoji.shortcode == rhs.emoji.shortcode
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,55 +5,55 @@
|
|||
// Created by MainasuK on 22/10/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MastodonCore
|
||||
|
||||
extension CustomEmojiPickerSection {
|
||||
// static func collectionViewDiffableDataSource(
|
||||
// collectionView: UICollectionView,
|
||||
// dependency: NeedsDependency
|
||||
// ) -> UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem> {
|
||||
// let dataSource = UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in
|
||||
// guard let _ = dependency else { return nil }
|
||||
// switch item {
|
||||
// case .emoji(let attribute):
|
||||
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CustomEmojiPickerItemCollectionViewCell.self), for: indexPath) as! CustomEmojiPickerItemCollectionViewCell
|
||||
// let placeholder = UIImage.placeholder(size: CustomEmojiPickerItemCollectionViewCell.itemSize, color: .systemFill)
|
||||
// .af.imageRounded(withCornerRadius: 4)
|
||||
//
|
||||
// let isAnimated = !UserDefaults.shared.preferredStaticEmoji
|
||||
// let url = URL(string: isAnimated ? attribute.emoji.url : attribute.emoji.staticURL)
|
||||
// cell.emojiImageView.sd_setImage(
|
||||
// with: url,
|
||||
// placeholderImage: placeholder,
|
||||
// options: [],
|
||||
// context: nil
|
||||
// )
|
||||
// cell.accessibilityLabel = attribute.emoji.shortcode
|
||||
// return cell
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// dataSource.supplementaryViewProvider = { [weak dataSource] collectionView, kind, indexPath -> UICollectionReusableView? in
|
||||
// guard let dataSource = dataSource else { return nil }
|
||||
// let sections = dataSource.snapshot().sectionIdentifiers
|
||||
// guard indexPath.section < sections.count else { return nil }
|
||||
// let section = sections[indexPath.section]
|
||||
//
|
||||
// switch kind {
|
||||
// case String(describing: CustomEmojiPickerHeaderCollectionReusableView.self):
|
||||
// let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: CustomEmojiPickerHeaderCollectionReusableView.self), for: indexPath) as! CustomEmojiPickerHeaderCollectionReusableView
|
||||
// switch section {
|
||||
// case .emoji(let name):
|
||||
// header.titleLabel.text = name
|
||||
// }
|
||||
// return header
|
||||
// default:
|
||||
// assertionFailure()
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return dataSource
|
||||
// }
|
||||
static func collectionViewDiffableDataSource(
|
||||
collectionView: UICollectionView,
|
||||
context: AppContext
|
||||
) -> UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem> {
|
||||
let dataSource = UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>(collectionView: collectionView) { [weak context] collectionView, indexPath, item -> UICollectionViewCell? in
|
||||
guard let _ = context else { return nil }
|
||||
switch item {
|
||||
case .emoji(let attribute):
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CustomEmojiPickerItemCollectionViewCell.self), for: indexPath) as! CustomEmojiPickerItemCollectionViewCell
|
||||
let placeholder = UIImage.placeholder(size: CustomEmojiPickerItemCollectionViewCell.itemSize, color: .systemFill)
|
||||
.af.imageRounded(withCornerRadius: 4)
|
||||
|
||||
let isAnimated = !UserDefaults.shared.preferredStaticEmoji
|
||||
let url = URL(string: isAnimated ? attribute.emoji.url : attribute.emoji.staticURL)
|
||||
cell.emojiImageView.sd_setImage(
|
||||
with: url,
|
||||
placeholderImage: placeholder,
|
||||
options: [],
|
||||
context: nil
|
||||
)
|
||||
cell.accessibilityLabel = attribute.emoji.shortcode
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
dataSource.supplementaryViewProvider = { [weak dataSource] collectionView, kind, indexPath -> UICollectionReusableView? in
|
||||
guard let dataSource = dataSource else { return nil }
|
||||
let sections = dataSource.snapshot().sectionIdentifiers
|
||||
guard indexPath.section < sections.count else { return nil }
|
||||
let section = sections[indexPath.section]
|
||||
|
||||
switch kind {
|
||||
case String(describing: CustomEmojiPickerHeaderCollectionReusableView.self):
|
||||
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: CustomEmojiPickerHeaderCollectionReusableView.self), for: indexPath) as! CustomEmojiPickerHeaderCollectionReusableView
|
||||
switch section {
|
||||
case .emoji(let name):
|
||||
header.titleLabel.text = name
|
||||
}
|
||||
return header
|
||||
default:
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return dataSource
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ extension AutoCompleteViewModel.State {
|
|||
return
|
||||
}
|
||||
|
||||
guard let customEmojiViewModel = viewModel.customEmojiViewModel.value else {
|
||||
guard let customEmojiViewModel = viewModel.customEmojiViewModel else {
|
||||
await enter(state: Fail.self)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ final class AutoCompleteViewModel {
|
|||
let authContext: AuthContext
|
||||
public let inputText = CurrentValueSubject<String, Never>("") // contains "@" or "#" prefix
|
||||
public let symbolBoundingRect = CurrentValueSubject<CGRect, Never>(.zero)
|
||||
public let customEmojiViewModel = CurrentValueSubject<EmojiService.CustomEmojiViewModel?, Never>(nil)
|
||||
public let customEmojiViewModel: EmojiService.CustomEmojiViewModel?
|
||||
|
||||
// output
|
||||
public var autoCompleteItems = CurrentValueSubject<[AutoCompleteItem], Never>([])
|
||||
|
@ -40,6 +40,8 @@ final class AutoCompleteViewModel {
|
|||
init(context: AppContext, authContext: AuthContext) {
|
||||
self.context = context
|
||||
self.authContext = authContext
|
||||
self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(for: authContext.mastodonAuthenticationBox.domain)
|
||||
// end init
|
||||
|
||||
autoCompleteItems
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
|
|
@ -72,6 +72,15 @@ public final class ComposeContentViewController: UIViewController {
|
|||
documentPickerController.delegate = self
|
||||
return documentPickerController
|
||||
}()
|
||||
|
||||
// emoji picker inputView
|
||||
let customEmojiPickerInputView: CustomEmojiPickerInputView = {
|
||||
let view = CustomEmojiPickerInputView(
|
||||
frame: CGRect(x: 0, y: 0, width: 0, height: 300),
|
||||
inputViewStyle: .keyboard
|
||||
)
|
||||
return view
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
@ -83,6 +92,8 @@ extension ComposeContentViewController {
|
|||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
viewModel.delegate = self
|
||||
|
||||
// setup view
|
||||
self.setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
|
||||
ThemeService.shared.currentTheme
|
||||
|
@ -106,6 +117,12 @@ extension ComposeContentViewController {
|
|||
tableView.delegate = self
|
||||
viewModel.setupDataSource(tableView: tableView)
|
||||
|
||||
// setup emoji picker
|
||||
customEmojiPickerInputView.collectionView.delegate = self
|
||||
viewModel.customEmojiPickerInputViewModel.customEmojiPickerInputView = customEmojiPickerInputView
|
||||
viewModel.setupCustomEmojiPickerDiffableDataSource(collectionView: customEmojiPickerInputView.collectionView)
|
||||
|
||||
// setup toolbar
|
||||
let toolbarHostingView = UIHostingController(rootView: composeContentToolbarView)
|
||||
toolbarHostingView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(toolbarHostingView.view)
|
||||
|
@ -128,6 +145,7 @@ extension ComposeContentViewController {
|
|||
view.bottomAnchor.constraint(equalTo: composeContentToolbarBackgroundView.bottomAnchor),
|
||||
])
|
||||
|
||||
// bind keyboard
|
||||
let keyboardHasShortcutBar = CurrentValueSubject<Bool, Never>(traitCollection.userInterfaceIdiom == .pad) // update default value later
|
||||
let keyboardEventPublishers = Publishers.CombineLatest3(
|
||||
KeyboardResponderService.shared.isShow,
|
||||
|
@ -256,6 +274,19 @@ extension ComposeContentViewController {
|
|||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// bind emoji picker
|
||||
viewModel.customEmojiViewModel?.emojis
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveValue: { [weak self] emojis in
|
||||
guard let self = self else { return }
|
||||
if emojis.isEmpty {
|
||||
self.customEmojiPickerInputView.activityIndicatorView.startAnimating()
|
||||
} else {
|
||||
self.customEmojiPickerInputView.activityIndicatorView.stopAnimating()
|
||||
}
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// bind toolbar
|
||||
bindToolbarViewModel()
|
||||
}
|
||||
|
@ -485,5 +516,115 @@ extension ComposeContentViewController: AutoCompleteViewControllerDelegate {
|
|||
didSelectItem item: AutoCompleteItem
|
||||
) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): did select item: \(String(describing: item))")
|
||||
|
||||
guard let info = viewModel.autoCompleteInfo else { return }
|
||||
guard let metaText = viewModel.contentMetaText else { return }
|
||||
|
||||
let _replacedText: String? = {
|
||||
var text: String
|
||||
switch item {
|
||||
case .hashtag(let hashtag):
|
||||
text = "#" + hashtag.name
|
||||
case .hashtagV1(let hashtagName):
|
||||
text = "#" + hashtagName
|
||||
case .account(let account):
|
||||
text = "@" + account.acct
|
||||
case .emoji(let emoji):
|
||||
text = ":" + emoji.shortcode + ":"
|
||||
case .bottomLoader:
|
||||
return nil
|
||||
}
|
||||
return text
|
||||
}()
|
||||
guard let replacedText = _replacedText else { return }
|
||||
guard let text = metaText.textView.text else { return }
|
||||
|
||||
let range = NSRange(info.toHighlightEndRange, in: text)
|
||||
metaText.textStorage.replaceCharacters(in: range, with: replacedText)
|
||||
viewModel.autoCompleteInfo = nil
|
||||
|
||||
// set selected range
|
||||
let newRange = NSRange(location: range.location + (replacedText as NSString).length, length: 0)
|
||||
guard metaText.textStorage.length <= newRange.location else { return }
|
||||
metaText.textView.selectedRange = newRange
|
||||
|
||||
// append a space and trigger textView delegate update
|
||||
DispatchQueue.main.async {
|
||||
metaText.textView.insertText(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
extension ComposeContentViewController: UICollectionViewDelegate {
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, indexPath.debugDescription)
|
||||
|
||||
switch collectionView {
|
||||
case customEmojiPickerInputView.collectionView:
|
||||
guard let diffableDataSource = viewModel.customEmojiPickerDiffableDataSource else { return }
|
||||
let item = diffableDataSource.itemIdentifier(for: indexPath)
|
||||
guard case let .emoji(attribute) = item else { return }
|
||||
let emoji = attribute.emoji
|
||||
|
||||
// make click sound
|
||||
UIDevice.current.playInputClick()
|
||||
|
||||
// retrieve active text input and insert emoji
|
||||
// the trailing space is REQUIRED to make regex happy
|
||||
_ = viewModel.customEmojiPickerInputViewModel.insertText(":\(emoji.shortcode): ")
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
} // end func
|
||||
|
||||
}
|
||||
|
||||
// MARK: - ComposeContentViewModelDelegate
|
||||
extension ComposeContentViewController: ComposeContentViewModelDelegate {
|
||||
public func composeContentViewModel(
|
||||
_ viewModel: ComposeContentViewModel,
|
||||
handleAutoComplete info: ComposeContentViewModel.AutoCompleteInfo
|
||||
) -> Bool {
|
||||
let snapshot = autoCompleteViewController.viewModel.diffableDataSource.snapshot()
|
||||
guard let item = snapshot.itemIdentifiers.first else { return false }
|
||||
|
||||
// FIXME: redundant code
|
||||
guard let metaText = viewModel.contentMetaText else { return false }
|
||||
guard let text = metaText.textView.text else { return false }
|
||||
let _replacedText: String? = {
|
||||
var text: String
|
||||
switch item {
|
||||
case .hashtag(let hashtag):
|
||||
text = "#" + hashtag.name
|
||||
case .hashtagV1(let hashtagName):
|
||||
text = "#" + hashtagName
|
||||
case .account(let account):
|
||||
text = "@" + account.acct
|
||||
case .emoji(let emoji):
|
||||
text = ":" + emoji.shortcode + ":"
|
||||
case .bottomLoader:
|
||||
return nil
|
||||
}
|
||||
return text
|
||||
}()
|
||||
guard let replacedText = _replacedText else { return false }
|
||||
|
||||
let range = NSRange(info.toHighlightEndRange, in: text)
|
||||
metaText.textStorage.replaceCharacters(in: range, with: replacedText)
|
||||
viewModel.autoCompleteInfo = nil
|
||||
|
||||
// set selected range
|
||||
let newRange = NSRange(location: range.location + (replacedText as NSString).length, length: 0)
|
||||
guard metaText.textStorage.length <= newRange.location else { return true }
|
||||
metaText.textView.selectedRange = newRange
|
||||
|
||||
// append a space and trigger textView delegate update
|
||||
DispatchQueue.main.async {
|
||||
metaText.textView.insertText(" ")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ extension ComposeContentViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension ComposeContentViewModel: UITableViewDataSource {
|
||||
public func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return Section.allCases.count
|
||||
|
@ -99,3 +100,42 @@ extension ComposeContentViewModel: UITableViewDataSource {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeContentViewModel {
|
||||
|
||||
func setupCustomEmojiPickerDiffableDataSource(
|
||||
collectionView: UICollectionView
|
||||
) {
|
||||
let diffableDataSource = CustomEmojiPickerSection.collectionViewDiffableDataSource(
|
||||
collectionView: collectionView,
|
||||
context: context
|
||||
)
|
||||
self.customEmojiPickerDiffableDataSource = diffableDataSource
|
||||
|
||||
let domain = authContext.mastodonAuthenticationBox.domain.uppercased()
|
||||
customEmojiViewModel?.emojis
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self, weak diffableDataSource] emojis in
|
||||
guard let _ = self else { return }
|
||||
guard let diffableDataSource = diffableDataSource else { return }
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<CustomEmojiPickerSection, CustomEmojiPickerItem>()
|
||||
let customEmojiSection = CustomEmojiPickerSection.emoji(name: domain)
|
||||
snapshot.appendSections([customEmojiSection])
|
||||
let items: [CustomEmojiPickerItem] = {
|
||||
var items = [CustomEmojiPickerItem]()
|
||||
for emoji in emojis where emoji.visibleInPicker {
|
||||
let attribute = CustomEmojiPickerItem.CustomEmojiAttribute(emoji: emoji)
|
||||
let item = CustomEmojiPickerItem.emoji(attribute: attribute)
|
||||
items.append(item)
|
||||
}
|
||||
return items
|
||||
}()
|
||||
snapshot.appendItems(items, toSection: customEmojiSection)
|
||||
|
||||
diffableDataSource.apply(snapshot)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ extension ComposeContentViewModel: MetaTextDelegate {
|
|||
|
||||
let content = MastodonContent(
|
||||
content: textInput,
|
||||
emojis: [:] // TODO: emojiViewModel?.emojis.asDictionary ?? [:]
|
||||
emojis: [:] // customEmojiViewModel?.emojis.value.asDictionary ?? [:]
|
||||
)
|
||||
let metaContent = MastodonMetaContent.convert(text: content)
|
||||
return metaContent
|
||||
|
@ -48,7 +48,7 @@ extension ComposeContentViewModel: MetaTextDelegate {
|
|||
|
||||
let content = MastodonContent(
|
||||
content: textInput,
|
||||
emojis: [:] // emojiViewModel?.emojis.asDictionary ?? [:]
|
||||
emojis: [:] // customEmojiViewModel?.emojis.value.asDictionary ?? [:]
|
||||
)
|
||||
let metaContent = MastodonMetaContent.convert(text: content)
|
||||
return metaContent
|
||||
|
|
|
@ -64,6 +64,12 @@ extension ComposeContentViewModel: UITextViewDelegate {
|
|||
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
switch textView {
|
||||
case contentMetaText?.textView:
|
||||
if text == " ", let autoCompleteInfo = self.autoCompleteInfo {
|
||||
assert(delegate != nil)
|
||||
let isHandled = delegate?.composeContentViewModel(self, handleAutoComplete: autoCompleteInfo) ?? false
|
||||
return !isHandled
|
||||
}
|
||||
|
||||
return true
|
||||
case contentWarningMetaText?.textView:
|
||||
let isReturn = text == "\n"
|
||||
|
|
|
@ -15,6 +15,10 @@ import MastodonMeta
|
|||
import MastodonCore
|
||||
import MastodonSDK
|
||||
|
||||
public protocol ComposeContentViewModelDelegate: AnyObject {
|
||||
func composeContentViewModel(_ viewModel: ComposeContentViewModel, handleAutoComplete info: ComposeContentViewModel.AutoCompleteInfo) -> Bool
|
||||
}
|
||||
|
||||
public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
|
||||
let logger = Logger(subsystem: "ComposeContentViewModel", category: "ViewModel")
|
||||
|
@ -28,6 +32,7 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
// input
|
||||
let context: AppContext
|
||||
let kind: Kind
|
||||
weak var delegate: ComposeContentViewModelDelegate?
|
||||
|
||||
@Published var viewLayoutFrame = ViewLayoutFrame()
|
||||
|
||||
|
@ -38,6 +43,9 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
@Published var autoCompleteRetryLayoutTimes = 0
|
||||
@Published var autoCompleteInfo: AutoCompleteInfo? = nil
|
||||
|
||||
// emoji
|
||||
var customEmojiPickerDiffableDataSource: UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>?
|
||||
|
||||
// output
|
||||
|
||||
// limit
|
||||
|
@ -46,8 +54,8 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
// content
|
||||
public weak var contentMetaText: MetaText? {
|
||||
didSet {
|
||||
// guard let textView = contentMetaText?.textView else { return }
|
||||
// customEmojiPickerInputViewModel.configure(textInput: textView)
|
||||
guard let textView = contentMetaText?.textView else { return }
|
||||
customEmojiPickerInputViewModel.configure(textInput: textView)
|
||||
}
|
||||
}
|
||||
@Published public var initialContent = ""
|
||||
|
@ -60,8 +68,8 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
// content warning
|
||||
weak var contentWarningMetaText: MetaText? {
|
||||
didSet {
|
||||
//guard let textView = contentWarningMetaText?.textView else { return }
|
||||
//customEmojiPickerInputViewModel.configure(textInput: textView)
|
||||
guard let textView = contentWarningMetaText?.textView else { return }
|
||||
customEmojiPickerInputViewModel.configure(textInput: textView)
|
||||
}
|
||||
}
|
||||
@Published public var isContentWarningActive = false
|
||||
|
@ -95,6 +103,9 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
|
||||
// emoji
|
||||
@Published var isEmojiActive = false
|
||||
let customEmojiViewModel: EmojiService.CustomEmojiViewModel?
|
||||
let customEmojiPickerInputViewModel = CustomEmojiPickerInputViewModel()
|
||||
@Published var isLoadingCustomEmoji = false
|
||||
|
||||
// visibility
|
||||
@Published var visibility: Mastodon.Entity.Status.Visibility
|
||||
|
@ -148,6 +159,9 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
return visibility
|
||||
}()
|
||||
self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(
|
||||
for: authContext.mastodonAuthenticationBox.domain
|
||||
)
|
||||
super.init()
|
||||
// end init
|
||||
|
||||
|
@ -192,6 +206,10 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// bind emoji inputView
|
||||
$isEmojiActive.assign(to: &customEmojiPickerInputViewModel.$isCustomEmojiComposing)
|
||||
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -215,7 +233,7 @@ extension ComposeContentViewModel {
|
|||
}
|
||||
|
||||
extension ComposeContentViewModel {
|
||||
struct AutoCompleteInfo {
|
||||
public struct AutoCompleteInfo {
|
||||
// model
|
||||
let inputText: Substring
|
||||
// range
|
||||
|
|
|
@ -9,7 +9,6 @@ import UIKit
|
|||
import Combine
|
||||
import MetaTextKit
|
||||
import MastodonCore
|
||||
import MastodonUI
|
||||
|
||||
final class CustomEmojiPickerInputViewModel {
|
||||
|
||||
|
@ -20,8 +19,7 @@ final class CustomEmojiPickerInputViewModel {
|
|||
// input
|
||||
weak var customEmojiPickerInputView: CustomEmojiPickerInputView?
|
||||
|
||||
// output
|
||||
let isCustomEmojiComposing = CurrentValueSubject<Bool, Never>(false)
|
||||
@Published var isCustomEmojiComposing = false
|
||||
|
||||
}
|
||||
|
||||
|
@ -51,27 +49,28 @@ extension CustomEmojiPickerInputViewModel {
|
|||
for reference in customEmojiReplaceableTextInputReferences {
|
||||
guard let textInput = reference.value else { continue }
|
||||
guard textInput.isFirstResponder == true else { continue }
|
||||
guard let selectedTextRange = textInput.selectedTextRange else { continue }
|
||||
// guard let selectedTextRange = textInput.selectedTextRange else { continue }
|
||||
|
||||
textInput.insertText(text)
|
||||
|
||||
// FIXME: inline emoji
|
||||
// due to insert text render as attachment
|
||||
// the cursor reset logic not works
|
||||
// hack with hard code +2 offset
|
||||
assert(text.hasSuffix(": "))
|
||||
guard text.hasPrefix(":") && text.hasSuffix(": ") else { continue }
|
||||
|
||||
if let _ = textInput as? MetaTextView {
|
||||
if let newPosition = textInput.position(from: selectedTextRange.start, offset: 2) {
|
||||
let newSelectedTextRange = textInput.textRange(from: newPosition, to: newPosition)
|
||||
textInput.selectedTextRange = newSelectedTextRange
|
||||
}
|
||||
} else {
|
||||
if let newPosition = textInput.position(from: selectedTextRange.start, offset: text.length) {
|
||||
let newSelectedTextRange = textInput.textRange(from: newPosition, to: newPosition)
|
||||
textInput.selectedTextRange = newSelectedTextRange
|
||||
}
|
||||
}
|
||||
// assert(text.hasSuffix(": "))
|
||||
// guard text.hasPrefix(":") && text.hasSuffix(": ") else { continue }
|
||||
//
|
||||
// if let _ = textInput as? MetaTextView {
|
||||
// if let newPosition = textInput.position(from: selectedTextRange.start, offset: 2) {
|
||||
// let newSelectedTextRange = textInput.textRange(from: newPosition, to: newPosition)
|
||||
// textInput.selectedTextRange = newSelectedTextRange
|
||||
// }
|
||||
// } else {
|
||||
// if let newPosition = textInput.position(from: selectedTextRange.start, offset: text.length) {
|
||||
// let newSelectedTextRange = textInput.textRange(from: newPosition, to: newPosition)
|
||||
// textInput.selectedTextRange = newSelectedTextRange
|
||||
// }
|
||||
// }
|
||||
|
||||
return reference
|
||||
}
|
||||
|
@ -81,3 +80,16 @@ extension CustomEmojiPickerInputViewModel {
|
|||
|
||||
}
|
||||
|
||||
extension CustomEmojiPickerInputViewModel {
|
||||
public func configure(textInput: CustomEmojiReplaceableTextInput) {
|
||||
$isCustomEmojiComposing
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isCustomEmojiComposing in
|
||||
guard let self = self else { return }
|
||||
textInput.inputView = isCustomEmojiComposing ? self.customEmojiPickerInputView : nil
|
||||
textInput.reloadInputViews()
|
||||
self.append(customEmojiReplaceableTextInput: textInput)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
|
@ -185,7 +185,7 @@ extension ComposeContentView {
|
|||
index: _index,
|
||||
deleteBackwardResponseTextFieldRelayDelegate: viewModel
|
||||
) { textField in
|
||||
// viewModel.customEmojiPickerInputViewModel.configure(textInput: textField)
|
||||
viewModel.customEmojiPickerInputViewModel.configure(textInput: textField)
|
||||
}
|
||||
}
|
||||
if viewModel.maxPollOptionLimit != viewModel.pollOptions.count {
|
||||
|
|
Loading…
Reference in New Issue