Skip to content

Commit

Permalink
Show up to three hashtags and up to three users (IOS-141)
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitschlag committed Sep 16, 2023
1 parent 2e384f3 commit ed11d01
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,8 @@ extension HomeTimelineViewModel {

let hasChanges = newSnapshot.itemIdentifiers != oldSnapshot.itemIdentifiers
if !hasChanges && !self.hasPendingStatusEditReload {
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): snapshot not changes")
self.didLoadLatest.send()
return
} else {
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): snapshot has changes")
}

guard let difference = self.calculateReloadSnapshotDifference(
Expand All @@ -101,7 +98,6 @@ extension HomeTimelineViewModel {
) else {
self.updateSnapshotUsingReloadData(snapshot: newSnapshot)
self.didLoadLatest.send()
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): applied new snapshot")
return
}

Expand All @@ -111,7 +107,6 @@ extension HomeTimelineViewModel {
contentOffset.y = tableView.contentOffset.y - difference.sourceDistanceToTableViewTopEdge
tableView.setContentOffset(contentOffset, animated: false)
self.didLoadLatest.send()
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): applied new snapshot")
self.hasPendingStatusEditReload = false
} // end Task
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,37 @@ enum SearchResultOverviewItem: Hashable {
}

enum SuggestionSectionEntry: Hashable {
//TODO: Use User instead
case hashtag(tag: Mastodon.Entity.Tag)
case profile(ManagedObjectRecord<MastodonUser>)
case profile(user: Mastodon.Entity.Account)

var title: String? {
if case let .hashtag(tag) = self {
return tag.name
} else {
return nil
switch self {

case .hashtag(tag: let tag):
return tag.name
case .profile(user: let user):
return "\(user.displayName)\(user.acct)"
}
// if case let .hashtag(tag) = self {
// return tag.name
// } else {
// return nil
// }
}

var icon: UIImage? {
if case let .hashtag(tag) = self {
return UIImage(systemName: "number")
} else {
return nil
switch self {

case .hashtag(tag: _):
return UIImage(systemName: "number")

case .profile(user: _):
return UIImage(systemName: "person.circle")

}
// if case let .hashtag(tag) = self {
// } else {
// }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class SearchResultsOverviewTableViewController: UIViewController {
var dataSource: UITableViewDiffableDataSource<SearchResultOverviewSection, SearchResultOverviewItem>?

weak var delegate: SearchResultsOverviewTableViewControllerDeleagte?
var activeTask: Task<Void, Never>?

init(appContext: AppContext, authContext: AuthContext) {

Expand All @@ -48,22 +49,29 @@ class SearchResultsOverviewTableViewController: UIViewController {
return cell

case .suggestion(let suggestion):
switch suggestion {

case .hashtag(let hashtag):
guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchResultDefaultSectionTableViewCell.reuseIdentifier, for: indexPath) as? SearchResultDefaultSectionTableViewCell else { fatalError() }

cell.configure(item: .hashtag(tag: hashtag))
return cell

case .profile(let profile):
guard let cell = tableView.dequeueReusableCell(withIdentifier: UserTableViewCell.reuseIdentifier, for: indexPath) as? UserTableViewCell else { fatalError() }

// cell.configure(me: <#T##MastodonUser?#>, tableView: <#T##UITableView#>, viewModel: <#T##UserTableViewCell.ViewModel#>, delegate: <#T##UserTableViewCellDelegate?#>)
guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchResultDefaultSectionTableViewCell.reuseIdentifier, for: indexPath) as? SearchResultDefaultSectionTableViewCell else { fatalError() }

return cell
}
cell.configure(item: suggestion)
return cell

// switch suggestion {
//
// case .hashtag(let hashtag):
// guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchResultDefaultSectionTableViewCell.reuseIdentifier, for: indexPath) as? SearchResultDefaultSectionTableViewCell else { fatalError() }
//
// cell.configure(item: .hashtag(tag: hashtag))
// return cell
//
// case .profile(let profile):
// //TODO: Use `UserFetchedResultsController` or `Persistence.MastodonUser.fetch` ???
// guard let cell = tableView.dequeueReusableCell(withIdentifier: UserTableViewCell.reuseIdentifier, for: indexPath) as? UserTableViewCell else { fatalError() }
//
//// cell.configure(me: <#T##MastodonUser?#>, tableView: <#T##UITableView#>, viewModel: <#T##UserTableViewCell.ViewModel#>, delegate: <#T##UserTableViewCellDelegate?#>)
//
// return cell
// }
}
}

Expand All @@ -79,9 +87,20 @@ class SearchResultsOverviewTableViewController: UIViewController {

required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

func showStandardSearch(for searchText: String) {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

var snapshot = NSDiffableDataSourceSnapshot<SearchResultOverviewSection, SearchResultOverviewItem>()
snapshot.appendSections([.default, .suggestions])
dataSource?.apply(snapshot, animatingDifferences: false)
}

func showStandardSearch(for searchText: String) {

guard let dataSource else { return }

var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .default))
snapshot.appendItems([.default(.posts(searchText)),
.default(.people(searchText)),
.default(.profile(searchText, authContext.mastodonAuthenticationBox.domain))], toSection: .default)
Expand All @@ -90,18 +109,29 @@ class SearchResultsOverviewTableViewController: UIViewController {
//TODO: Check if Mastodon-URL
snapshot.appendItems([.default(.openLink(searchText))], toSection: .default)
}
dataSource?.apply(snapshot, animatingDifferences: false)

dataSource.apply(snapshot, animatingDifferences: false)
}

func searchForSuggestions(for searchText: String) {

activeTask?.cancel()

guard let dataSource else { return }

var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .suggestions))
dataSource.apply(snapshot, animatingDifferences: false)

guard searchText.isNotEmpty else { return }

let query = Mastodon.API.V2.Search.Query(
q: searchText,
type: .default,
resolve: true
)

Task {
let searchTask = Task {
do {
let searchResult = try await appContext.apiService.search(
query: query,
Expand All @@ -111,22 +141,29 @@ class SearchResultsOverviewTableViewController: UIViewController {
let firstThreeHashtags = searchResult.hashtags.prefix(3)
let firstThreeUsers = searchResult.accounts.prefix(3)

guard var snapshot = dataSource?.snapshot() else { return }
var snapshot = dataSource.snapshot()

snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .suggestions))
snapshot.appendItems(firstThreeHashtags.map { .suggestion(.hashtag(tag: $0)) }, toSection: .suggestions )
// snapshot.appendItems(firstThreeUsers.map { .suggestion(.profile($0.displayName)) }, toSection: .suggestions )
if firstThreeHashtags.isNotEmpty {
snapshot.appendItems(firstThreeHashtags.map { .suggestion(.hashtag(tag: $0)) }, toSection: .suggestions )
}

await MainActor.run {
dataSource?.apply(snapshot, animatingDifferences: false)
if firstThreeUsers.isNotEmpty {
snapshot.appendItems(firstThreeUsers.map { .suggestion(.profile(user: $0)) }, toSection: .suggestions )
}

guard Task.isCancelled == false else { return }

await MainActor.run {
dataSource.apply(snapshot, animatingDifferences: false)
}

} catch {
// do nothing
print(error.localizedDescription)
}
}

activeTask = searchTask
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,29 +131,6 @@ final class SearchDetailViewController: UIViewController, NeedsDependency {
searchResultsOverviewViewController.view.pinToParent()
}

// bind search trigger
// "local" search
viewModel.searchText
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] searchText in
guard let self else { return }

self.searchResultsOverviewViewController.showStandardSearch(for: searchText)
}
.store(in: &disposeBag)

// delayed search on server
viewModel.searchText
.removeDuplicates()
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
.sink { [weak self] searchText in
guard let self else { return }

self.searchResultsOverviewViewController.searchForSuggestions(for: searchText)
}
.store(in: &disposeBag)

// bind search history display
viewModel.searchText
.removeDuplicates()
Expand Down Expand Up @@ -263,7 +240,11 @@ extension SearchDetailViewController {
extension SearchDetailViewController: UISearchBarDelegate {

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
viewModel.searchText.value = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
viewModel.searchText.value = trimmedSearchText

searchResultsOverviewViewController.showStandardSearch(for: trimmedSearchText)
searchResultsOverviewViewController.searchForSuggestions(for: trimmedSearchText)
}

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
Expand Down
1 change: 0 additions & 1 deletion MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ extension Mastodon.API {
return try Mastodon.API.decoder.decode(type, from: data)
} catch let decodeError {
#if DEBUG
os_log(.info, "%{public}s[%{public}ld], %{public}s: decode fail. content %s", ((#file as NSString).lastPathComponent), #line, #function, String(data: data, encoding: .utf8) ?? "<nil>")
debugPrint(decodeError)
#endif

Expand Down

0 comments on commit ed11d01

Please sign in to comment.