Skip to content

Commit

Permalink
Merge pull request #1044 from mastodon/ios-157-popular-on-mastodon
Browse files Browse the repository at this point in the history
Better UI/UX for suggestions for new users (IOS-157)
  • Loading branch information
zeitschlag authored May 25, 2023
2 parents 02d305e + 3597e69 commit ddf0afc
Show file tree
Hide file tree
Showing 42 changed files with 601 additions and 946 deletions.
4 changes: 2 additions & 2 deletions Localization/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,8 +464,8 @@
}
},
"suggestion_account": {
"title": "Find People to Follow",
"follow_explain": "When you follow someone, you’ll see their posts in your home feed."
"title": "Popular on Mastodon",
"follow_all": "Follow all"
},
"compose": {
"title": {
Expand Down
62 changes: 13 additions & 49 deletions Mastodon.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

15 changes: 0 additions & 15 deletions Mastodon/Diffable/Account/SelectedAccountItem.swift

This file was deleted.

37 changes: 0 additions & 37 deletions Mastodon/Diffable/Account/SelectedAccountSection.swift

This file was deleted.

161 changes: 0 additions & 161 deletions Mastodon/Diffable/RecommandAccount/RecommendAccountSection.swift

This file was deleted.

7 changes: 6 additions & 1 deletion Mastodon/Diffable/Search/SearchHistorySection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ extension SearchHistorySection {
guard let user = item.object(in: context.managedObjectContext) else { return }
cell.configure(
me: authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user,
viewModel: .init(value: user, followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(), blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher()),
viewModel: SearchHistoryUserCollectionViewCell.ViewModel(
value: user,
followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(),
blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher(),
followRequestedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followRequestedUserIDs.eraseToAnyPublisher()
),
delegate: configuration.searchHistorySectionHeaderCollectionReusableViewDelegate
)
}
Expand Down
5 changes: 4 additions & 1 deletion Mastodon/Diffable/Search/SearchResultSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ extension SearchResultSection {
authContext: authContext,
tableView: tableView,
cell: cell,
viewModel: .init(value: .user(user), followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(), blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher()),
viewModel: UserTableViewCell.ViewModel(value: .user(user),
followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(),
blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher(),
followRequestedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followRequestedUserIDs.eraseToAnyPublisher()),
configuration: configuration
)
}
Expand Down
6 changes: 5 additions & 1 deletion Mastodon/Diffable/User/UserSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ extension UserSection {
authContext: authContext,
tableView: tableView,
cell: cell,
viewModel: .init(value: .user(user), followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(), blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher()),
viewModel: UserTableViewCell.ViewModel(value: .user(user),
followedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followingUserIds.eraseToAnyPublisher(),
blockedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$blockedUserIds.eraseToAnyPublisher(),
followRequestedUsers: authContext.mastodonAuthenticationBox.inMemoryCache.$followRequestedUserIDs.eraseToAnyPublisher()
),
configuration: configuration
)
}
Expand Down
2 changes: 1 addition & 1 deletion Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension DataSourceFacade {
static func responseToUserFollowRequestAction(
dependency: NeedsDependency & AuthContextProvider,
notification: ManagedObjectRecord<Notification>,
query: Mastodon.API.Account.FollowReqeustQuery
query: Mastodon.API.Account.FollowRequestQuery
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
Expand Down
21 changes: 21 additions & 0 deletions Mastodon/Protocol/Provider/DataSourceFacade+UserView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ extension DataSourceFacade {
if let userObject = user.object(in: dependency.context.managedObjectContext) {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followingUserIds.append(userObject.id)
}

case .request:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)

if let userObject = user.object(in: dependency.context.managedObjectContext) {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs.append(userObject.id)
}

case .unfollow:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
Expand All @@ -39,6 +50,16 @@ extension DataSourceFacade {
if let userObject = user.object(in: dependency.context.managedObjectContext) {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.blockedUserIds.append(userObject.id)
}

case .pending:
try await DataSourceFacade.responseToUserFollowAction(
dependency: dependency,
user: user
)

if let userObject = user.object(in: dependency.context.managedObjectContext) {
dependency.authContext.mastodonAuthenticationBox.inMemoryCache.followRequestedUserIDs.removeAll(where: { $0 == userObject.id })
}
case .none, .loading:
break //no-op
}
Expand Down
21 changes: 16 additions & 5 deletions Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,19 @@ extension HomeTimelineViewController {
.sink { [weak self] isEmpty in
if isEmpty {
self?.showEmptyView()

let userDoesntFollowPeople: Bool
if let managedObjectContext = self?.context.managedObjectContext,
let me = self?.authContext.mastodonAuthenticationBox.authenticationRecord.object(in: managedObjectContext)?.user {
userDoesntFollowPeople = me.followersCount == 0
} else {
userDoesntFollowPeople = true
}

if (self?.viewModel.presentedSuggestions == false) && userDoesntFollowPeople {
self?.findPeopleButtonPressed(self)
self?.viewModel.presentedSuggestions = true
}
} else {
self?.emptyView.removeFromSuperview()
}
Expand Down Expand Up @@ -285,8 +298,6 @@ extension HomeTimelineViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

viewModel.viewDidAppear.send()

if let timestamp = viewModel.lastAutomaticFetchTimestamp {
let now = Date()
if now.timeIntervalSince(timestamp) > 60 {
Expand Down Expand Up @@ -360,6 +371,7 @@ extension HomeTimelineViewController {
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topPaddingView.heightAnchor.constraint(equalTo: bottomPaddingView.heightAnchor, multiplier: 0.8),
manuallySearchButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 20),
])

let buttonContainerStackView = UIStackView()
Expand All @@ -374,9 +386,10 @@ extension HomeTimelineViewController {
}
}

//MARK: - Actions
extension HomeTimelineViewController {

@objc private func findPeopleButtonPressed(_ sender: PrimaryActionButton) {
@objc private func findPeopleButtonPressed(_ sender: Any?) {
let suggestionAccountViewModel = SuggestionAccountViewModel(context: context, authContext: viewModel.authContext)
suggestionAccountViewModel.delegate = viewModel
_ = coordinator.present(
Expand All @@ -387,13 +400,11 @@ extension HomeTimelineViewController {
}

@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
let searchDetailViewModel = SearchDetailViewModel(authContext: viewModel.authContext)
_ = coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .modal(animated: true, completion: nil))
}

@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let setting = context.settingService.currentSetting.value else { return }
let settingsViewModel = SettingsViewModel(context: context, authContext: viewModel.authContext, setting: setting)
_ = coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
Expand Down
3 changes: 2 additions & 1 deletion Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ final class HomeTimelineViewModel: NSObject {
let fetchedResultsController: FeedFetchedResultsController
let homeTimelineNavigationBarTitleViewModel: HomeTimelineNavigationBarTitleViewModel
let listBatchFetchViewModel = ListBatchFetchViewModel()
let viewDidAppear = PassthroughSubject<Void, Never>()

var presentedSuggestions = false

@Published var lastAutomaticFetchTimestamp: Date? = nil
@Published var scrollPositionRecord: ScrollPositionRecord? = nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ extension MastodonLoginViewController: MastodonLoginViewModelDelegate {

dataSource?.apply(snapshot, animatingDifferences: false)

OperationQueue.main.addOperation {
DispatchQueue.main.async {
let numberOfResults = viewModel.filteredServers.count
self.contentView.updateCorners(numberOfResults: numberOfResults)
}
Expand Down
Loading

0 comments on commit ddf0afc

Please sign in to comment.