parent
7b0dd37b77
commit
6e10efc490
|
@ -265,7 +265,7 @@
|
|||
"cancel": "Cancel"
|
||||
},
|
||||
"recommend": {
|
||||
"buttonText": "See All",
|
||||
"button_text": "See All",
|
||||
"hash_tag": {
|
||||
"title": "Trending in your timeline",
|
||||
"description": "Hashtags that are getting quite a bit of attention among people you follow",
|
||||
|
@ -276,6 +276,15 @@
|
|||
"description": "Except for Sam, you will not like his account.",
|
||||
"follow": "Follow"
|
||||
}
|
||||
},
|
||||
"searching": {
|
||||
"segment": {
|
||||
"all": "All",
|
||||
"people": "People",
|
||||
"hashtags": "Hashtags"
|
||||
},
|
||||
"recent_search": "Recent searches",
|
||||
"clear": "clear"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* StatusView.swift */; };
|
||||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
||||
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
|
||||
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; };
|
||||
2D206B7225F5D27F00143C56 /* AudioContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */; };
|
||||
2D206B8025F5F45E00143C56 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7F25F5F45E00143C56 /* UIImage.swift */; };
|
||||
2D206B8625F5FB0900143C56 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8525F5FB0900143C56 /* Double.swift */; };
|
||||
|
@ -30,7 +32,7 @@
|
|||
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */; };
|
||||
2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAB925CB9B0500C9ED86 /* UIView.swift */; };
|
||||
2D32EADA25CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAD925CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift */; };
|
||||
2D34D9CB261489930081BFC0 /* SearchViewController+RecomendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9CA261489930081BFC0 /* SearchViewController+RecomendView.swift */; };
|
||||
2D34D9CB261489930081BFC0 /* SearchViewController+Recomend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9CA261489930081BFC0 /* SearchViewController+Recomend.swift */; };
|
||||
2D34D9D126148D9E0081BFC0 /* APIService+Recommend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9D026148D9E0081BFC0 /* APIService+Recommend.swift */; };
|
||||
2D34D9DB261494120081BFC0 /* APIService+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9DA261494120081BFC0 /* APIService+Search.swift */; };
|
||||
2D34D9E226149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9E126149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift */; };
|
||||
|
@ -104,6 +106,8 @@
|
|||
2DF75BA725D10E1000694EC8 /* APIService+Favorite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BA625D10E1000694EC8 /* APIService+Favorite.swift */; };
|
||||
2DF75BB925D1474100694EC8 /* ManagedObjectObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BB825D1474100694EC8 /* ManagedObjectObserver.swift */; };
|
||||
2DF75BC725D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BC625D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift */; };
|
||||
2DFAD5272616F9D300F9EE7C /* SearchViewController+Searching.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFAD5262616F9D300F9EE7C /* SearchViewController+Searching.swift */; };
|
||||
2DFAD5372617010500F9EE7C /* SearchingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFAD5362617010500F9EE7C /* SearchingTableViewCell.swift */; };
|
||||
2DFF41892614A4DC00F776A4 /* UIView+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFF41882614A4DC00F776A4 /* UIView+Constraint.swift */; };
|
||||
5D0393902612D259007FE196 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D03938F2612D259007FE196 /* WebViewController.swift */; };
|
||||
5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; };
|
||||
|
@ -366,6 +370,8 @@
|
|||
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+PublicTimeline.swift"; sourceTree = "<group>"; };
|
||||
2D152A8B25C295CC009AA50C /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
||||
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = "<group>"; };
|
||||
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = "<group>"; };
|
||||
2D206B7125F5D27F00143C56 /* AudioContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioContainerView.swift; sourceTree = "<group>"; };
|
||||
2D206B7F25F5F45E00143C56 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
|
||||
2D206B8525F5FB0900143C56 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
|
||||
|
@ -374,7 +380,7 @@
|
|||
2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2D32EAB925CB9B0500C9ED86 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||
2D32EAD925CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewModel+LoadMiddleState.swift"; sourceTree = "<group>"; };
|
||||
2D34D9CA261489930081BFC0 /* SearchViewController+RecomendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewController+RecomendView.swift"; sourceTree = "<group>"; };
|
||||
2D34D9CA261489930081BFC0 /* SearchViewController+Recomend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewController+Recomend.swift"; sourceTree = "<group>"; };
|
||||
2D34D9D026148D9E0081BFC0 /* APIService+Recommend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Recommend.swift"; sourceTree = "<group>"; };
|
||||
2D34D9DA261494120081BFC0 /* APIService+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Search.swift"; sourceTree = "<group>"; };
|
||||
2D34D9E126149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRecommendTagsCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
|
@ -445,6 +451,8 @@
|
|||
2DF75BA625D10E1000694EC8 /* APIService+Favorite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Favorite.swift"; sourceTree = "<group>"; };
|
||||
2DF75BB825D1474100694EC8 /* ManagedObjectObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedObjectObserver.swift; sourceTree = "<group>"; };
|
||||
2DF75BC625D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedObjectContextObjectsDidChange.swift; sourceTree = "<group>"; };
|
||||
2DFAD5262616F9D300F9EE7C /* SearchViewController+Searching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewController+Searching.swift"; sourceTree = "<group>"; };
|
||||
2DFAD5362617010500F9EE7C /* SearchingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2DFF41882614A4DC00F776A4 /* UIView+Constraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraint.swift"; sourceTree = "<group>"; };
|
||||
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -943,6 +951,7 @@
|
|||
DB1E346725F518E20079D7DF /* CategoryPickerSection.swift */,
|
||||
2DE0FAC02615F04D00CDF649 /* RecomendHashTagSection.swift */,
|
||||
2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */,
|
||||
2D198648261C0B8500F0B013 /* SearchResultSection.swift */,
|
||||
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */,
|
||||
DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */,
|
||||
);
|
||||
|
@ -991,6 +1000,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
2D7631B225C159F700929FB9 /* Item.swift */,
|
||||
2D198642261BF09500F0B013 /* SearchResultItem.swift */,
|
||||
DB4481CB25EE2AFE00BEFB67 /* PollItem.swift */,
|
||||
DB1E347725F519300079D7DF /* PickServerItem.swift */,
|
||||
DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */,
|
||||
|
@ -1025,6 +1035,14 @@
|
|||
path = Stack;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DFAD5212616F8E300F9EE7C /* TableViewCell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DFAD5362617010500F9EE7C /* SearchingTableViewCell.swift */,
|
||||
);
|
||||
path = TableViewCell;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3FE14AD363ED19AE7FF210A6 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1508,9 +1526,11 @@
|
|||
DB9D6BEE25E4F5370051B173 /* Search */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DFAD5212616F8E300F9EE7C /* TableViewCell */,
|
||||
2DE0FAC62615F5D200CDF649 /* View */,
|
||||
DB9D6BE825E4F5340051B173 /* SearchViewController.swift */,
|
||||
2D34D9CA261489930081BFC0 /* SearchViewController+RecomendView.swift */,
|
||||
2D34D9CA261489930081BFC0 /* SearchViewController+Recomend.swift */,
|
||||
2DFAD5262616F9D300F9EE7C /* SearchViewController+Searching.swift */,
|
||||
2D6DE3FF26141DF600A63F6A /* SearchViewModel.swift */,
|
||||
2D34D9E026149C550081BFC0 /* CollectionViewCell */,
|
||||
);
|
||||
|
@ -2034,6 +2054,7 @@
|
|||
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */,
|
||||
DB482A3F261331E8008AE74C /* UserTimelineViewModel+State.swift in Sources */,
|
||||
2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */,
|
||||
2DFAD5372617010500F9EE7C /* SearchingTableViewCell.swift in Sources */,
|
||||
DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */,
|
||||
DBB525502611ED6D002F1F29 /* ProfileHeaderView.swift in Sources */,
|
||||
0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */,
|
||||
|
@ -2063,6 +2084,7 @@
|
|||
DB71FD4625F8C6D200512AE1 /* StatusProvider+UITableViewDataSourcePrefetching.swift in Sources */,
|
||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */,
|
||||
2DE0FAC82615F5F000CDF649 /* SearchRecommendAccountsCollectionViewCell.swift in Sources */,
|
||||
2DFAD5272616F9D300F9EE7C /* SearchViewController+Searching.swift in Sources */,
|
||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */,
|
||||
DBB5250E2611EBAF002F1F29 /* ProfileSegmentedViewController.swift in Sources */,
|
||||
|
@ -2106,6 +2128,7 @@
|
|||
DB59F11825EFA35B001F1DAB /* StripProgressView.swift in Sources */,
|
||||
DB59F10425EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift in Sources */,
|
||||
DB9A48962603685D008B817C /* MastodonAttachmentService+UploadState.swift in Sources */,
|
||||
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */,
|
||||
DB66728C25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift in Sources */,
|
||||
DBE0822425CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift in Sources */,
|
||||
2DF75B9B25D0E27500694EC8 /* StatusProviderFacade.swift in Sources */,
|
||||
|
@ -2180,7 +2203,7 @@
|
|||
DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */,
|
||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
||||
DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */,
|
||||
2D34D9CB261489930081BFC0 /* SearchViewController+RecomendView.swift in Sources */,
|
||||
2D34D9CB261489930081BFC0 /* SearchViewController+Recomend.swift in Sources */,
|
||||
DB482A45261335BA008AE74C /* UserTimelineViewController+StatusProvider.swift in Sources */,
|
||||
2D206B8625F5FB0900143C56 /* Double.swift in Sources */,
|
||||
DB9A485C2603010E008B817C /* PHPickerResultLoader.swift in Sources */,
|
||||
|
@ -2235,6 +2258,7 @@
|
|||
DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */,
|
||||
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
|
||||
DB9A489026035963008B817C /* APIService+Media.swift in Sources */,
|
||||
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */,
|
||||
DBB525302611EBF3002F1F29 /* ProfilePagingViewModel.swift in Sources */,
|
||||
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */,
|
||||
DB49A62525FF334C00B98345 /* EmojiService+CustomEmojiViewModel+LoadState.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// SearchResultItem.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/4/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MastodonSDK
|
||||
|
||||
enum SearchResultItem {
|
||||
case hashTag(tag: Mastodon.Entity.Tag)
|
||||
|
||||
case account(account: Mastodon.Entity.Account)
|
||||
}
|
||||
|
||||
extension SearchResultItem: Equatable {
|
||||
static func == (lhs: SearchResultItem, rhs: SearchResultItem) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.hashTag(let tagLeft), .hashTag(let tagRight)):
|
||||
return tagLeft == tagRight
|
||||
case (.account(let accountLeft), account(let accountRight)):
|
||||
return accountLeft == accountRight
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SearchResultItem: Hashable {
|
||||
func hash(into hasher: inout Hasher) {
|
||||
switch self {
|
||||
case .account(let account):
|
||||
hasher.combine(account)
|
||||
case .hashTag(let tag):
|
||||
hasher.combine(tag)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// SearchResultSection.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/4/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MastodonSDK
|
||||
import UIKit
|
||||
|
||||
enum SearchResultSection: Equatable, Hashable {
|
||||
case account
|
||||
case hashTag
|
||||
}
|
||||
|
||||
extension SearchResultSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView
|
||||
) -> UITableViewDiffableDataSource<SearchResultSection, SearchResultItem> {
|
||||
UITableViewDiffableDataSource(tableView: tableView) { (tableView, indexPath, result) -> UITableViewCell? in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SearchingTableViewCell.self), for: indexPath) as! SearchingTableViewCell
|
||||
switch result {
|
||||
case .account(let account):
|
||||
cell.config(with: account)
|
||||
case .hashTag(let tag):
|
||||
cell.config(with: tag)
|
||||
}
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
|
@ -408,7 +408,7 @@ internal enum L10n {
|
|||
internal enum Search {
|
||||
internal enum Recommend {
|
||||
/// See All
|
||||
internal static let buttontext = L10n.tr("Localizable", "Scene.Search.Recommend.Buttontext")
|
||||
internal static let buttonText = L10n.tr("Localizable", "Scene.Search.Recommend.ButtonText")
|
||||
internal enum Accounts {
|
||||
/// Except for Sam, you will not like his account.
|
||||
internal static let description = L10n.tr("Localizable", "Scene.Search.Recommend.Accounts.Description")
|
||||
|
@ -434,6 +434,20 @@ internal enum L10n {
|
|||
/// Search hashtags and users
|
||||
internal static let placeholder = L10n.tr("Localizable", "Scene.Search.Searchbar.Placeholder")
|
||||
}
|
||||
internal enum Searching {
|
||||
/// clear
|
||||
internal static let clear = L10n.tr("Localizable", "Scene.Search.Searching.Clear")
|
||||
/// Recent searches
|
||||
internal static let recentSearch = L10n.tr("Localizable", "Scene.Search.Searching.RecentSearch")
|
||||
internal enum Segment {
|
||||
/// All
|
||||
internal static let all = L10n.tr("Localizable", "Scene.Search.Searching.Segment.All")
|
||||
/// Hashtags
|
||||
internal static let hashtags = L10n.tr("Localizable", "Scene.Search.Searching.Segment.Hashtags")
|
||||
/// People
|
||||
internal static let people = L10n.tr("Localizable", "Scene.Search.Searching.Segment.People")
|
||||
}
|
||||
}
|
||||
}
|
||||
internal enum ServerPicker {
|
||||
/// Pick a Server,\nany server.
|
||||
|
|
|
@ -132,12 +132,17 @@ tap the link to confirm your account.";
|
|||
"Scene.Search.Recommend.Accounts.Description" = "Except for Sam, you will not like his account.";
|
||||
"Scene.Search.Recommend.Accounts.Follow" = "Follow";
|
||||
"Scene.Search.Recommend.Accounts.Title" = "Accounts you might like";
|
||||
"Scene.Search.Recommend.Buttontext" = "See All";
|
||||
"Scene.Search.Recommend.ButtonText" = "See All";
|
||||
"Scene.Search.Recommend.HashTag.Description" = "Hashtags that are getting quite a bit of attention among people you follow";
|
||||
"Scene.Search.Recommend.HashTag.PeopleTalking" = "%@ people are talking";
|
||||
"Scene.Search.Recommend.HashTag.Title" = "Trending in your timeline";
|
||||
"Scene.Search.Searchbar.Cancel" = "Cancel";
|
||||
"Scene.Search.Searchbar.Placeholder" = "Search hashtags and users";
|
||||
"Scene.Search.Searching.Clear" = "clear";
|
||||
"Scene.Search.Searching.RecentSearch" = "Recent searches";
|
||||
"Scene.Search.Searching.Segment.All" = "All";
|
||||
"Scene.Search.Searching.Segment.Hashtags" = "Hashtags";
|
||||
"Scene.Search.Searching.Segment.People" = "People";
|
||||
"Scene.ServerPicker.Button.Category.All" = "All";
|
||||
"Scene.ServerPicker.Button.SeeLess" = "See Less";
|
||||
"Scene.ServerPicker.Button.SeeMore" = "See More";
|
||||
|
|
|
@ -82,14 +82,7 @@ extension SearchRecommendTagsCollectionViewCell {
|
|||
peopleLabel.text = ""
|
||||
return
|
||||
}
|
||||
var recentHistory = [Mastodon.Entity.History]()
|
||||
for history in historys {
|
||||
if Int(history.uses) == 0 {
|
||||
break
|
||||
} else {
|
||||
recentHistory.append(history)
|
||||
}
|
||||
}
|
||||
let recentHistory = historys[0...2]
|
||||
let peopleAreTalking = recentHistory.compactMap({ Int($0.accounts) }).reduce(0, +)
|
||||
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
||||
peopleLabel.text = string
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// SearchViewController+RecomendView.swift
|
||||
// SearchViewController+Recomend.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/3/31.
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// SearchViewController+Searching.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/4/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension SearchViewController {
|
||||
func setupSearchingTableView() {
|
||||
searchingTableView.delegate = self
|
||||
searchingTableView.register(SearchingTableViewCell.self, forCellReuseIdentifier: String(describing: SearchingTableViewCell.self))
|
||||
view.addSubview(searchingTableView)
|
||||
searchingTableView.constrain([
|
||||
searchingTableView.frameLayoutGuide.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
searchingTableView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
|
||||
searchingTableView.frameLayoutGuide.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
|
||||
searchingTableView.frameLayoutGuide.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
|
||||
searchingTableView.contentLayoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor),
|
||||
])
|
||||
|
||||
viewModel.isSearching
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink {[weak self] isSearching in
|
||||
self?.searchingTableView.isHidden = !isSearching
|
||||
if !isSearching {
|
||||
self?.searchResultDiffableDataSource = nil
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
viewModel.searchResult
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] searchResult in
|
||||
guard let self = self else { return }
|
||||
let dataSource = SearchResultSection.tableViewDiffableDataSource(for: self.searchingTableView)
|
||||
var snapshot = NSDiffableDataSourceSnapshot<SearchResultSection, SearchResultItem>()
|
||||
if let accounts = searchResult?.accounts {
|
||||
snapshot.appendSections([.account])
|
||||
let items = accounts.compactMap { SearchResultItem.account(account: $0) }
|
||||
snapshot.appendItems(items, toSection: .account)
|
||||
}
|
||||
if let tags = searchResult?.hashtags {
|
||||
snapshot.appendSections([.hashTag])
|
||||
let items = tags.compactMap { SearchResultItem.hashTag(tag: $0) }
|
||||
snapshot.appendItems(items, toSection: .hashTag)
|
||||
}
|
||||
dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
||||
self.searchResultDiffableDataSource = dataSource
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension SearchViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
66
|
||||
}
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
66
|
||||
}
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
|
||||
}
|
|
@ -24,9 +24,12 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||
let micImage = UIImage(systemName: "mic.fill")
|
||||
searchBar.setImage(micImage, for: .bookmark, state: .normal)
|
||||
searchBar.showsBookmarkButton = true
|
||||
searchBar.showsScopeBar = false
|
||||
searchBar.scopeButtonTitles = [L10n.Scene.Search.Searching.Segment.all, L10n.Scene.Search.Searching.Segment.people,L10n.Scene.Search.Searching.Segment.hashtags]
|
||||
return searchBar
|
||||
}()
|
||||
|
||||
// recommend
|
||||
let scrollView: UIScrollView = {
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.showsVerticalScrollIndicator = false
|
||||
|
@ -71,6 +74,16 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
return view
|
||||
}()
|
||||
|
||||
// searching
|
||||
let searchingTableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.separatorStyle = .singleLine
|
||||
tableView.backgroundColor = .white
|
||||
return tableView
|
||||
}()
|
||||
var searchResultDiffableDataSource: UITableViewDiffableDataSource<SearchResultSection, SearchResultItem>?
|
||||
}
|
||||
|
||||
extension SearchViewController {
|
||||
|
@ -83,6 +96,7 @@ extension SearchViewController {
|
|||
setupScrollView()
|
||||
setupHashTagCollectionView()
|
||||
setupAccountsCollectionView()
|
||||
setupSearchingTableView()
|
||||
}
|
||||
|
||||
func setupScrollView() {
|
||||
|
@ -109,22 +123,40 @@ extension SearchViewController {
|
|||
extension SearchViewController: UISearchBarDelegate {
|
||||
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
||||
searchBar.setShowsCancelButton(true, animated: true)
|
||||
searchBar.showsScopeBar = true
|
||||
viewModel.isSearching.value = true
|
||||
}
|
||||
|
||||
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
|
||||
searchBar.setShowsCancelButton(false, animated: true)
|
||||
searchBar.showsScopeBar = false
|
||||
viewModel.isSearching.value = true
|
||||
}
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.setShowsCancelButton(false, animated: true)
|
||||
searchBar.showsScopeBar = false
|
||||
searchBar.text = ""
|
||||
searchBar.resignFirstResponder()
|
||||
viewModel.isSearching.value = false
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
viewModel.searchText.send(searchText)
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
|
||||
switch selectedScope {
|
||||
case 0:
|
||||
viewModel.searchScope.value = ""
|
||||
case 1:
|
||||
viewModel.searchScope.value = "accounts"
|
||||
case 2:
|
||||
viewModel.searchScope.value = "hashtags"
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,51 @@ final class SearchViewModel {
|
|||
|
||||
// output
|
||||
let searchText = CurrentValueSubject<String, Never>("")
|
||||
let searchScope = CurrentValueSubject<String, Never>("")
|
||||
|
||||
let isSearching = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
let searchResult = CurrentValueSubject<Mastodon.Entity.SearchResult?, Never>(nil)
|
||||
|
||||
var recommendHashTags = [Mastodon.Entity.Tag]()
|
||||
var recommendAccounts = [Mastodon.Entity.Account]()
|
||||
|
||||
init(context: AppContext) {
|
||||
self.context = context
|
||||
guard let activeMastodonAuthenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
return
|
||||
}
|
||||
Publishers.CombineLatest(
|
||||
searchText
|
||||
.filter { !$0.isEmpty }
|
||||
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main).removeDuplicates(),
|
||||
searchScope)
|
||||
.flatMap { (text, scope) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> in
|
||||
let query = Mastodon.API.Search.Query(accountID: nil,
|
||||
maxID: nil,
|
||||
minID: nil,
|
||||
type: scope,
|
||||
excludeUnreviewed: nil,
|
||||
q: text,
|
||||
resolve: nil,
|
||||
limit: nil,
|
||||
offset: nil,
|
||||
following: nil)
|
||||
return context.apiService.search(domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
||||
}
|
||||
.sink { _ in
|
||||
} receiveValue: { [weak self] result in
|
||||
self?.searchResult.value = result.value
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
isSearching
|
||||
.sink { [weak self] isSearching in
|
||||
if !isSearching {
|
||||
self?.searchResult.value == nil
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
func requestRecommendHashTags() -> Future<Void, Error> {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// SearchingTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/4/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MastodonSDK
|
||||
|
||||
final class SearchingTableViewCell: UITableViewCell {
|
||||
let _imageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.tintColor = .black
|
||||
return imageView
|
||||
}()
|
||||
|
||||
let _titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Asset.Colors.buttonDefault.color
|
||||
label.font = .systemFont(ofSize: 17, weight: .semibold)
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
return label
|
||||
}()
|
||||
|
||||
let _subTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.font = .preferredFont(forTextStyle: .body)
|
||||
return label
|
||||
}()
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
_imageView.af.cancelImageRequest()
|
||||
_imageView.image = nil
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
configure()
|
||||
}
|
||||
}
|
||||
|
||||
extension SearchingTableViewCell {
|
||||
private func configure() {
|
||||
self.selectionStyle = .none
|
||||
contentView.addSubview(_imageView)
|
||||
_imageView.pin(toSize: CGSize(width: 42, height: 42))
|
||||
_imageView.constrain([
|
||||
_imageView.constraint(.leading, toView: contentView, constant: 21),
|
||||
_imageView.constraint(.centerY, toView: contentView)
|
||||
])
|
||||
|
||||
contentView.addSubview(_titleLabel)
|
||||
_titleLabel.pin(top: 12, left: 75, bottom: nil, right: 0)
|
||||
|
||||
contentView.addSubview(_subTitleLabel)
|
||||
_subTitleLabel.pin(top: 34, left: 75, bottom: nil, right: 0)
|
||||
}
|
||||
|
||||
func config(with account:Mastodon.Entity.Account) {
|
||||
self._imageView.af.setImage(
|
||||
withURL: URL(string: account.avatar)!,
|
||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||
imageTransition: .crossDissolve(0.2)
|
||||
)
|
||||
self._titleLabel.text = account.displayName.isEmpty ? account.username : account.displayName
|
||||
self._subTitleLabel.text = account.acct
|
||||
}
|
||||
|
||||
func config(with tag:Mastodon.Entity.Tag) {
|
||||
let image = UIImage(systemName: "number.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 34, weight: .regular))!.withRenderingMode(.alwaysTemplate)
|
||||
self._imageView.image = image
|
||||
self._titleLabel.text = "# " + tag.name
|
||||
guard let historys = tag.history else {
|
||||
self._subTitleLabel.text = ""
|
||||
return
|
||||
}
|
||||
let recentHistory = historys[0...2]
|
||||
let peopleAreTalking = recentHistory.compactMap({ Int($0.accounts) }).reduce(0, +)
|
||||
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
||||
self._subTitleLabel.text = string
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(SwiftUI) && DEBUG
|
||||
import SwiftUI
|
||||
|
||||
struct SearchingTableViewCell_Previews: PreviewProvider {
|
||||
static var controls: some View {
|
||||
Group {
|
||||
UIViewPreview {
|
||||
let cell = SearchingTableViewCell()
|
||||
cell.backgroundColor = .white
|
||||
cell._imageView.image = UIImage(systemName: "number.circle.fill")
|
||||
cell._titleLabel.text = "Electronic Frontier Foundation"
|
||||
cell._subTitleLabel.text = "@eff@mastodon.social"
|
||||
return cell
|
||||
}
|
||||
.previewLayout(.fixed(width: 228, height: 130))
|
||||
}
|
||||
}
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
controls.colorScheme(.light)
|
||||
controls.colorScheme(.dark)
|
||||
}
|
||||
.background(Color.gray)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -28,7 +28,7 @@ class SearchRecommendCollectionHeader: UIView {
|
|||
let seeAllButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitleColor(Asset.Colors.buttonDefault.color, for: .normal)
|
||||
button.setTitle(L10n.Scene.Search.Recommend.buttontext, for: .normal)
|
||||
button.setTitle(L10n.Scene.Search.Recommend.buttonText, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
import Foundation
|
||||
extension Mastodon.Entity {
|
||||
public struct SearchResult: Codable {
|
||||
let accounts: [Mastodon.Entity.Account]
|
||||
let statuses: [Mastodon.Entity.Status]
|
||||
let hashtags: [Mastodon.Entity.Tag]
|
||||
public let accounts: [Mastodon.Entity.Account]
|
||||
public let statuses: [Mastodon.Entity.Status]
|
||||
public let hashtags: [Mastodon.Entity.Tag]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue