Add trending hashtags to add saved hashtag controller
This commit is contained in:
parent
f9411d706b
commit
4ed862120c
|
@ -18,6 +18,10 @@
|
|||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */; };
|
||||
04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* ImageCache.swift */; };
|
||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
||||
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */; };
|
||||
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */; };
|
||||
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */; };
|
||||
D6093FB725BE0CF3004811E6 /* HashtagHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093FB625BE0CF3004811E6 /* HashtagHistoryView.swift */; };
|
||||
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = D60CFFDA24A290BA00D00083 /* SwiftSoup */; };
|
||||
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */; };
|
||||
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60E2F232442372B005F8713 /* StatusMO.swift */; };
|
||||
|
@ -296,6 +300,7 @@
|
|||
D6E426AD25334DA500C02E1C /* FuzzyMatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426AC25334DA500C02E1C /* FuzzyMatcherTests.swift */; };
|
||||
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */; };
|
||||
D6E426B9253382B300C02E1C /* SearchResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426B8253382B300C02E1C /* SearchResultType.swift */; };
|
||||
D6E57FA325C26FAB00341037 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D6E57FA525C26FAB00341037 /* Localizable.stringsdict */; };
|
||||
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
||||
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
||||
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */; };
|
||||
|
@ -376,6 +381,10 @@
|
|||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarViewController.swift; sourceTree = "<group>"; };
|
||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = "<group>"; };
|
||||
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagSearchResultsViewController.swift; sourceTree = "<group>"; };
|
||||
D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingHashtagTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D6093FB625BE0CF3004811E6 /* HashtagHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagHistoryView.swift; sourceTree = "<group>"; };
|
||||
D60A4FFB238B726A008AC647 /* StatusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusState.swift; sourceTree = "<group>"; };
|
||||
D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseStatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D60E2F232442372B005F8713 /* StatusMO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMO.swift; sourceTree = "<group>"; };
|
||||
|
@ -660,6 +669,7 @@
|
|||
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiImageView.swift; sourceTree = "<group>"; };
|
||||
D6E426B8253382B300C02E1C /* SearchResultType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultType.swift; sourceTree = "<group>"; };
|
||||
D6E4885C24A2890C0011C13E /* Tusker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tusker.entitlements; sourceTree = "<group>"; };
|
||||
D6E57FA425C26FAB00341037 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
||||
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
||||
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableTextField.swift; sourceTree = "<group>"; };
|
||||
|
@ -834,6 +844,9 @@
|
|||
children = (
|
||||
D611C2CD232DC61100C86A49 /* HashtagTableViewCell.swift */,
|
||||
D611C2CE232DC61100C86A49 /* HashtagTableViewCell.xib */,
|
||||
D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */,
|
||||
D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */,
|
||||
D6093FB625BE0CF3004811E6 /* HashtagHistoryView.swift */,
|
||||
);
|
||||
path = "Hashtag Cell";
|
||||
sourceTree = "<group>";
|
||||
|
@ -901,6 +914,7 @@
|
|||
children = (
|
||||
D627943D23A564D400D38C68 /* ExploreViewController.swift */,
|
||||
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */,
|
||||
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */,
|
||||
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */,
|
||||
);
|
||||
path = Explore;
|
||||
|
@ -1448,6 +1462,7 @@
|
|||
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */,
|
||||
D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */,
|
||||
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
||||
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */,
|
||||
D67B506B250B28FF00FAECFB /* Vendor */,
|
||||
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
||||
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
||||
|
@ -1732,12 +1747,14 @@
|
|||
D6A3BC7D232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib in Resources */,
|
||||
D6412B0B24B0D4C600F5412E /* ProfileHeaderView.xib in Resources */,
|
||||
D6D4DDDA212518A200E1C4BB /* LaunchScreen.storyboard in Resources */,
|
||||
D6E57FA325C26FAB00341037 /* Localizable.stringsdict in Resources */,
|
||||
D6B053AC23BD2F1400A066FA /* AssetCollectionViewCell.xib in Resources */,
|
||||
D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */,
|
||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */,
|
||||
D6969EA4240DD28D002843CE /* UnknownNotificationTableViewCell.xib in Resources */,
|
||||
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
|
||||
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */,
|
||||
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
|
||||
D626493323BD751600612E6E /* ShowCameraCollectionViewCell.xib in Resources */,
|
||||
D626493D23C1000300612E6E /* AlbumTableViewCell.xib in Resources */,
|
||||
|
@ -1865,6 +1882,7 @@
|
|||
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */,
|
||||
D6412B0324AFF6A600F5412E /* TabBarScrollableViewController.swift in Sources */,
|
||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */,
|
||||
D6093FB725BE0CF3004811E6 /* HashtagHistoryView.swift in Sources */,
|
||||
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */,
|
||||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */,
|
||||
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */,
|
||||
|
@ -1956,6 +1974,7 @@
|
|||
D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */,
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */,
|
||||
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */,
|
||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
||||
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
|
||||
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */,
|
||||
|
@ -2054,6 +2073,7 @@
|
|||
D626493F23C101C500612E6E /* AlbumAssetCollectionViewController.swift in Sources */,
|
||||
D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */,
|
||||
D6C99FCB24FADC91005C74D3 /* MainComposeTextView.swift in Sources */,
|
||||
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */,
|
||||
D6E4267725327FB400C02E1C /* ComposeAutocompleteView.swift in Sources */,
|
||||
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */,
|
||||
D677284E24ECC01D00C732D3 /* Draft.swift in Sources */,
|
||||
|
@ -2133,6 +2153,14 @@
|
|||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
D6E57FA425C26FAB00341037 /* en */,
|
||||
);
|
||||
name = Localizable.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
|
|
|
@ -9,12 +9,19 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class AddSavedHashtagViewController: SearchResultsViewController {
|
||||
class AddSavedHashtagViewController: EnhancedTableViewController {
|
||||
|
||||
weak var mastodonController: MastodonController!
|
||||
|
||||
var resultsController: SearchResultsViewController!
|
||||
var searchController: UISearchController!
|
||||
|
||||
var dataSource: UITableViewDiffableDataSource<Section, Item>!
|
||||
|
||||
init(mastodonController: MastodonController) {
|
||||
super.init(mastodonController: mastodonController, resultTypes: [.hashtags])
|
||||
self.mastodonController = mastodonController
|
||||
|
||||
super.init(style: .grouped)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
@ -24,14 +31,32 @@ class AddSavedHashtagViewController: SearchResultsViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
delegate = self
|
||||
title = NSLocalizedString("Search", comment: "search screen title")
|
||||
|
||||
searchController = UISearchController(searchResultsController: nil)
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
tableView.register(UINib(nibName: "TrendingHashtagTableViewCell", bundle: .main), forCellReuseIdentifier: "trendingTagCell")
|
||||
tableView.rowHeight = 44
|
||||
|
||||
dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
|
||||
switch item {
|
||||
case let .tag(hashtag):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "trendingTagCell", for: indexPath) as! TrendingHashtagTableViewCell
|
||||
cell.updateUI(hashtag: hashtag)
|
||||
return cell
|
||||
}
|
||||
})
|
||||
|
||||
resultsController = HashtagSearchResultsViewController(mastodonController: mastodonController)
|
||||
resultsController.delegate = self
|
||||
resultsController.exploreNavigationController = self.navigationController!
|
||||
|
||||
searchController = UISearchController(searchResultsController: resultsController)
|
||||
searchController.obscuresBackgroundDuringPresentation = true
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
searchController.searchResultsUpdater = resultsController
|
||||
searchController.searchBar.autocapitalizationType = .none
|
||||
searchController.searchBar.placeholder = NSLocalizedString("Search for hashtags to save", comment: "add saved hashtag search field placeholder")
|
||||
searchController.searchBar.delegate = self
|
||||
searchController.searchBar.delegate = resultsController
|
||||
searchController.searchBar.showsCancelButton = false
|
||||
|
||||
definesPresentationContext = true
|
||||
|
||||
|
@ -41,11 +66,38 @@ class AddSavedHashtagViewController: SearchResultsViewController {
|
|||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonPressed))
|
||||
}
|
||||
|
||||
override func performSearch(query: String?) {
|
||||
if let query = query, !query.starts(with: "#") {
|
||||
super.performSearch(query: "#\(query)")
|
||||
} else {
|
||||
super.performSearch(query: query)
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
let request = Client.getTrends(limit: 10)
|
||||
mastodonController.run(request) { (response) in
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
|
||||
guard case let .success(hashtags, _) = response,
|
||||
hashtags.count > 0 else {
|
||||
self.dataSource.apply(snapshot)
|
||||
return
|
||||
}
|
||||
|
||||
snapshot.appendSections([.trendingTags])
|
||||
snapshot.appendItems(hashtags.map { .tag($0) })
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func selectHashtag(_ hashtag: Hashtag) {
|
||||
SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!)
|
||||
presentingViewController!.dismiss(animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Table View Delegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch dataSource.itemIdentifier(for: indexPath) {
|
||||
case nil:
|
||||
return
|
||||
case let .tag(hashtag):
|
||||
selectHashtag(hashtag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,9 +109,24 @@ class AddSavedHashtagViewController: SearchResultsViewController {
|
|||
|
||||
}
|
||||
|
||||
extension AddSavedHashtagViewController: SearchResultsViewControllerDelegate {
|
||||
func selectedSearchResult(hashtag: Hashtag) {
|
||||
SavedDataManager.shared.add(hashtag: hashtag, for: mastodonController.accountInfo!)
|
||||
dismiss(animated: true)
|
||||
extension AddSavedHashtagViewController {
|
||||
enum Section {
|
||||
case trendingTags
|
||||
}
|
||||
|
||||
enum Item: Hashable {
|
||||
case tag(Hashtag)
|
||||
}
|
||||
|
||||
class DataSource: UITableViewDiffableDataSource<Section, Item> {
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return NSLocalizedString("Trending Hashtags", comment: "trending hashtags seciton title")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AddSavedHashtagViewController: SearchResultsViewControllerDelegate {
|
||||
func selectedSearchResult(hashtag: Hashtag) {
|
||||
selectHashtag(hashtag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// HashtagSearchResultsViewController.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 1/24/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class HashtagSearchResultsViewController: SearchResultsViewController {
|
||||
|
||||
init(mastodonController: MastodonController) {
|
||||
super.init(mastodonController: mastodonController, resultTypes: [.hashtags])
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func performSearch(query: String?) {
|
||||
if let query = query, !query.starts(with: "#") {
|
||||
super.performSearch(query: "#\(query)")
|
||||
} else {
|
||||
super.performSearch(query: query)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -132,6 +132,7 @@ class SearchResultsViewController: EnhancedTableViewController {
|
|||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
|
||||
let resultTypes = self.resultTypes
|
||||
let request = Client.search(query: query, types: resultTypes, resolve: true, limit: 10)
|
||||
mastodonController.run(request) { (response) in
|
||||
guard case let .success(results, _) = response else { fatalError() }
|
||||
|
@ -161,16 +162,16 @@ class SearchResultsViewController: EnhancedTableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
if !results.accounts.isEmpty {
|
||||
if !results.accounts.isEmpty && (resultTypes == nil || resultTypes!.contains(.accounts)) {
|
||||
snapshot.appendSections([.accounts])
|
||||
snapshot.appendItems(results.accounts.map { .account($0.id) }, toSection: .accounts)
|
||||
addAccounts(results.accounts)
|
||||
}
|
||||
if !results.hashtags.isEmpty {
|
||||
if !results.hashtags.isEmpty && (resultTypes == nil || resultTypes!.contains(.hashtags)) {
|
||||
snapshot.appendSections([.hashtags])
|
||||
snapshot.appendItems(results.hashtags.map { .hashtag($0) }, toSection: .hashtags)
|
||||
}
|
||||
if !results.statuses.isEmpty {
|
||||
if !results.statuses.isEmpty && (resultTypes == nil || resultTypes!.contains(.statuses)) {
|
||||
snapshot.appendSections([.statuses])
|
||||
snapshot.appendItems(results.statuses.map { .status($0.id, .unknown) }, toSection: .statuses)
|
||||
addStatuses(results.statuses)
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// HashtagHistoryView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 1/24/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class HashtagHistoryView: UIView {
|
||||
|
||||
private var history: [Hashtag.History]?
|
||||
|
||||
private let curveRadius: CGFloat = 10
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
createLayers()
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
createLayers()
|
||||
}
|
||||
|
||||
func setHistory(_ history: [Hashtag.History]?) {
|
||||
if let history = history {
|
||||
self.history = history.sorted(by: { $0.day < $1.day })
|
||||
} else {
|
||||
self.history = nil
|
||||
}
|
||||
|
||||
createLayers()
|
||||
}
|
||||
|
||||
private func createLayers() {
|
||||
guard let history = history,
|
||||
history.count >= 2 else { return }
|
||||
|
||||
let maxUses = history.max(by: { $0.uses < $1.uses })!.uses
|
||||
|
||||
// remove old layers if this view is being re-used
|
||||
layer.sublayers?.forEach { $0.removeFromSuperlayer() }
|
||||
|
||||
let path = UIBezierPath()
|
||||
|
||||
let widthStep = bounds.width / CGFloat(history.count - 1)
|
||||
|
||||
let points: [CGPoint] = history.enumerated().map { (index, entry) in
|
||||
let x = CGFloat(index) * widthStep
|
||||
let yFrac = CGFloat(entry.uses) / CGFloat(maxUses)
|
||||
let y = (1 - yFrac) * bounds.height
|
||||
return CGPoint(x: x, y: y)
|
||||
}
|
||||
|
||||
var gapStartPoints = [CGPoint]()
|
||||
var gapEndPoints = [CGPoint]()
|
||||
|
||||
for (index, point) in points.enumerated().dropFirst().dropLast() {
|
||||
let prev = points[index - 1]
|
||||
let next = points[index + 1]
|
||||
|
||||
let a = atan((point.y - prev.y) / widthStep)
|
||||
let b = atan((next.y - point.y) / widthStep)
|
||||
let innerAngle = .pi - a - b
|
||||
|
||||
let gapDistance = curveRadius / sin(innerAngle / 2)
|
||||
|
||||
let x1 = point.x - cos(a) * gapDistance
|
||||
let y1 = point.y - sin(a) * gapDistance
|
||||
gapStartPoints.append(CGPoint(x: x1, y: y1))
|
||||
|
||||
let x2 = point.x + cos(b) * gapDistance
|
||||
let y2 = point.y + sin(b) * gapDistance
|
||||
gapEndPoints.append(CGPoint(x: x2, y: y2))
|
||||
}
|
||||
|
||||
path.move(to: points.first!)
|
||||
for (index, point) in points.dropFirst().dropLast().enumerated() {
|
||||
path.addLine(to: gapStartPoints[index])
|
||||
path.addQuadCurve(to: gapEndPoints[index], controlPoint: point)
|
||||
}
|
||||
path.addLine(to: points.last!)
|
||||
|
||||
let borderLayer = CAShapeLayer()
|
||||
// copy the border path so we can continue mutating the UIBezierPath to create the fill path
|
||||
borderLayer.path = path.cgPath.copy()!
|
||||
borderLayer.strokeColor = tintColor.cgColor
|
||||
borderLayer.fillColor = nil
|
||||
borderLayer.lineWidth = 2
|
||||
borderLayer.lineCap = .round
|
||||
|
||||
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
|
||||
path.addLine(to: CGPoint(x: 0, y: bounds.height))
|
||||
path.addLine(to: points.first!)
|
||||
|
||||
let fillLayer = CAShapeLayer()
|
||||
fillLayer.path = path.cgPath
|
||||
let fillColor = self.fillColor()
|
||||
fillLayer.strokeColor = fillColor
|
||||
fillLayer.fillColor = fillColor
|
||||
fillLayer.lineWidth = 2
|
||||
|
||||
layer.addSublayer(fillLayer)
|
||||
layer.addSublayer(borderLayer)
|
||||
}
|
||||
|
||||
// The non-transparent fill color.
|
||||
// We blend with the view's background color ourselves so that final color is non-transparent,
|
||||
// otherwise when the fill layer's border and fill overlap, there's a visibly darker patch
|
||||
// because transparent colors are being blended together.
|
||||
private func fillColor() -> CGColor {
|
||||
var backgroundRed: CGFloat = 0
|
||||
var backgroundGreen: CGFloat = 0
|
||||
var backgroundBlue: CGFloat = 0
|
||||
var tintRed: CGFloat = 0
|
||||
var tintGreen: CGFloat = 0
|
||||
var tintBlue: CGFloat = 0
|
||||
traitCollection.performAsCurrent {
|
||||
backgroundColor!.getRed(&backgroundRed, green: &backgroundGreen, blue: &backgroundBlue, alpha: nil)
|
||||
tintColor.getRed(&tintRed, green: &tintGreen, blue: &tintBlue, alpha: nil)
|
||||
}
|
||||
let blendedRed = (backgroundRed + tintRed) / 2
|
||||
let blendedGreen = (backgroundGreen + tintGreen) / 2
|
||||
let blendedBlue = (backgroundBlue + tintBlue) / 2
|
||||
return CGColor(red: blendedRed, green: blendedGreen, blue: blendedBlue, alpha: 1)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -17,8 +18,8 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="#hashtag" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vVg-1C-zJr">
|
||||
<rect key="frame" x="16" y="11" width="83" height="22"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<rect key="frame" x="16" y="11" width="71.5" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// TrendingHashtagTableViewCell.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 1/24/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class TrendingHashtagTableViewCell: UITableViewCell {
|
||||
|
||||
@IBOutlet weak var hashtagLabel: UILabel!
|
||||
@IBOutlet weak var peopleTodayLabel: UILabel!
|
||||
@IBOutlet weak var historyView: HashtagHistoryView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
func updateUI(hashtag: Hashtag) {
|
||||
hashtagLabel.text = "#\(hashtag.name)"
|
||||
historyView.setHistory(hashtag.history)
|
||||
historyView.isHidden = hashtag.history == nil || hashtag.history!.count < 2
|
||||
|
||||
if let history = hashtag.history {
|
||||
let sorted = history.sorted(by: { $0.day < $1.day })
|
||||
let lastTwo = sorted[(sorted.count - 2)...]
|
||||
let accounts = lastTwo.map(\.accounts).reduce(0, +)
|
||||
let uses = lastTwo.map(\.uses).reduce(0, +)
|
||||
|
||||
let format = NSLocalizedString("trending hashtag info", comment: "trending hashtag posts and people")
|
||||
peopleTodayLabel.text = String.localizedStringWithFormat(format, accounts, uses)
|
||||
peopleTodayLabel.isHidden = false
|
||||
} else {
|
||||
peopleTodayLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="TrendingHashtagTableViewCell" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="tEP-en-vHK">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="66"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="iCc-do-llt">
|
||||
<rect key="frame" x="0.0" y="15.5" width="180" height="35"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="#hashtag" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SIS-9e-Paj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="180" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="6 people today" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kc5-BL-bmu">
|
||||
<rect key="frame" x="0.0" y="20.5" width="180" height="14.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Xrw-2v-ybZ" customClass="HashtagHistoryView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="188" y="19" width="100" height="28"/>
|
||||
<color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="W4C-uw-zWg"/>
|
||||
<constraint firstAttribute="width" constant="100" id="XHb-vd-qNk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="tEP-en-vHK" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="3Qd-rF-nGk"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="tEP-en-vHK" secondAttribute="trailing" id="Ws6-oZ-9Es"/>
|
||||
<constraint firstItem="tEP-en-vHK" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="if4-Ea-awg"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tEP-en-vHK" secondAttribute="bottom" id="nTV-Ih-vTj"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<connections>
|
||||
<outlet property="hashtagLabel" destination="SIS-9e-Paj" id="1UK-Va-3rL"/>
|
||||
<outlet property="historyView" destination="Xrw-2v-ybZ" id="OIh-K9-gSk"/>
|
||||
<outlet property="peopleTodayLabel" destination="Kc5-BL-bmu" id="5L8-aO-zt4"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="132" y="132"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="secondarySystemGroupedBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>trending hashtag info</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@accounts@, %#@posts@ recently</string>
|
||||
<key>posts</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>u</string>
|
||||
<key>one</key>
|
||||
<string>1 post</string>
|
||||
<key>other</key>
|
||||
<string>%u posts</string>
|
||||
</dict>
|
||||
<key>accounts</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>u</string>
|
||||
<key>one</key>
|
||||
<string>1 person</string>
|
||||
<key>other</key>
|
||||
<string>%u people</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
Loading…
Reference in New Issue