forked from shadowfacts/Tusker
129 lines
4.7 KiB
Swift
129 lines
4.7 KiB
Swift
//
|
|
// EmojiPickerCollectionViewController.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 10/12/20.
|
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
|
|
private let reuseIdentifier = "EmojiCell"
|
|
|
|
protocol EmojiPickerCollectionViewControllerDelegate: AnyObject {
|
|
func selectedEmoji(_ emoji: Emoji)
|
|
}
|
|
|
|
// It would be nice to replace this with a LazyVGrid when the deployment target is bumped to 14.0
|
|
class EmojiPickerCollectionViewController: UICollectionViewController {
|
|
|
|
weak var delegate: EmojiPickerCollectionViewControllerDelegate?
|
|
|
|
private weak var mastodonController: MastodonController!
|
|
|
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
|
|
|
var searchQuery: String = "" {
|
|
didSet {
|
|
guard let emojis = mastodonController.customEmojis else { return }
|
|
let snapshot = createFilteredSnapshot(emojis: emojis)
|
|
DispatchQueue.main.async {
|
|
self.dataSource.apply(snapshot)
|
|
}
|
|
}
|
|
}
|
|
|
|
init(mastodonController: MastodonController) {
|
|
self.mastodonController = mastodonController
|
|
|
|
let itemWidth = NSCollectionLayoutDimension.fractionalWidth(1.0 / 10)
|
|
let itemSize = NSCollectionLayoutSize(widthDimension: itemWidth, heightDimension: itemWidth)
|
|
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
|
|
|
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: itemWidth)
|
|
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
|
|
group.interItemSpacing = .fixed(4)
|
|
|
|
let section = NSCollectionLayoutSection(group: group)
|
|
section.interGroupSpacing = 4
|
|
|
|
let layout = UICollectionViewCompositionalLayout(section: section)
|
|
|
|
super.init(collectionViewLayout: layout)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
|
|
// use negative indicator insets to bring the indicators back to the edge of the containing view
|
|
// using collectionView.contentInset doesn't work the compositional layout ignores the inset when calculating fractional widths
|
|
collectionView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: -8)
|
|
collectionView.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
|
|
|
|
collectionView.backgroundColor = .clear
|
|
collectionView.register(EmojiCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
|
|
|
|
dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
|
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! EmojiCollectionViewCell
|
|
cell.updateUI(emoji: item.emoji)
|
|
return cell
|
|
}
|
|
|
|
mastodonController.getCustomEmojis { (emojis) in
|
|
DispatchQueue.main.async {
|
|
self.dataSource.apply(self.createFilteredSnapshot(emojis: emojis))
|
|
}
|
|
}
|
|
}
|
|
|
|
private func createFilteredSnapshot(emojis: [Emoji]) -> NSDiffableDataSourceSnapshot<Section, Item> {
|
|
let items: [Item]
|
|
if searchQuery.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
items = emojis.map { Item(emoji: $0) }
|
|
} else {
|
|
items = emojis
|
|
.map { ($0, FuzzyMatcher.match(pattern: searchQuery, str: $0.shortcode)) }
|
|
.filter(\.1.matched)
|
|
.sorted { $0.1.score > $1.1.score }
|
|
.map { Item(emoji: $0.0) }
|
|
}
|
|
|
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
|
snapshot.appendSections([.emojis])
|
|
snapshot.appendItems(items)
|
|
return snapshot
|
|
}
|
|
|
|
// MARK: UICollectionViewDelegate
|
|
|
|
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
|
|
delegate?.selectedEmoji(item.emoji)
|
|
}
|
|
|
|
}
|
|
|
|
extension EmojiPickerCollectionViewController {
|
|
enum Section {
|
|
case emojis
|
|
}
|
|
|
|
struct Item: Hashable, Equatable {
|
|
let emoji: Emoji
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(emoji.shortcode)
|
|
}
|
|
|
|
static func ==(lhs: Item, rhs: Item) -> Bool {
|
|
lhs.emoji.shortcode == rhs.emoji.shortcode
|
|
}
|
|
}
|
|
}
|