Make follow-requests work again (IOS-192)

Aaaaand remove obsolete viewmodel and put relationship to make menu work again (too)
This commit is contained in:
Nathan Mattes 2024-01-30 11:13:39 +01:00
parent f363316662
commit 7dfa56507e
11 changed files with 94 additions and 175 deletions

View File

@ -133,7 +133,6 @@
D809907A294BC9390050219F /* PrivacyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099079294BC9390050219F /* PrivacyTableViewCell.swift */; };
D809907C294D25510050219F /* PrivacyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D809907B294D25510050219F /* PrivacyViewModel.swift */; };
D80F627C2B5C32C500877059 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80F627A2B5C32C500877059 /* NotificationView.swift */; };
D80F627D2B5C32C500877059 /* NotificationView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80F627B2B5C32C500877059 /* NotificationView+ViewModel.swift */; };
D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439852AD415DE0071A88F /* AboutInstance.swift */; };
D81439882AD450A40071A88F /* AboutInstanceTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */; };
D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; };
@ -787,7 +786,6 @@
D8099079294BC9390050219F /* PrivacyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewCell.swift; sourceTree = "<group>"; };
D809907B294D25510050219F /* PrivacyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewModel.swift; sourceTree = "<group>"; };
D80F627A2B5C32C500877059 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
D80F627B2B5C32C500877059 /* NotificationView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationView+ViewModel.swift"; sourceTree = "<group>"; };
D81439852AD415DE0071A88F /* AboutInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstance.swift; sourceTree = "<group>"; };
D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewDataSource.swift; sourceTree = "<group>"; };
D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsOverviewTableViewController.swift; sourceTree = "<group>"; };
@ -1824,7 +1822,6 @@
isa = PBXGroup;
children = (
D80F627A2B5C32C500877059 /* NotificationView.swift */,
D80F627B2B5C32C500877059 /* NotificationView+ViewModel.swift */,
DB63F776279A9A2A00455B82 /* NotificationView+Configuration.swift */,
);
path = NotificationView;
@ -3881,7 +3878,6 @@
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
DB3EA8E9281B7A3700598866 /* DiscoveryCommunityViewModel.swift in Sources */,
D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */,
D80F627D2B5C32C500877059 /* NotificationView+ViewModel.swift in Sources */,
DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */,
DBEFCD71282A12B200C0ABEA /* ReportReasonViewController.swift in Sources */,
DB98EB5627B0FF1B0082E365 /* ReportViewControllerAppearance.swift in Sources */,

View File

@ -73,8 +73,6 @@ extension NotificationSection {
viewModel: NotificationTableViewCell.ViewModel,
configuration: Configuration
) {
cell.notificationView.viewModel.authContext = configuration.authContext
StatusSection.setupStatusPollDataSource(
context: context,
authContext: configuration.authContext,
@ -90,7 +88,8 @@ extension NotificationSection {
cell.configure(
tableView: tableView,
viewModel: viewModel,
delegate: configuration.notificationTableViewCellDelegate
delegate: configuration.notificationTableViewCellDelegate,
authenticationBox: configuration.authContext.mastodonAuthenticationBox
)
cell.notificationView.statusView.viewModel.filterContext = configuration.filterContext

View File

@ -35,44 +35,47 @@ extension DataSourceFacade {
static func responseToUserFollowRequestAction(
dependency: NeedsDependency & AuthContextProvider,
notification: MastodonNotification,
notificationView: NotificationView,
query: Mastodon.API.Account.FollowRequestQuery
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
let managedObjectContext = dependency.context.managedObjectContext
let _userID: String? = try await managedObjectContext.perform {
return notification.account.id
}
guard let userID = _userID else {
assertionFailure()
throw APIService.APIError.implicit(.badRequest)
}
let userID = notification.account.id
let state: MastodonFollowRequestState = notification.followRequestState
guard state.state == .none else {
return
}
guard state.state == .none else { return }
switch query {
case .accept:
notification.transientFollowRequestState = .init(state: .isAccepting)
case .reject:
notification.transientFollowRequestState = .init(state: .isRejecting)
}
await notificationView.configure(notification: notification, authenticationBox: dependency.authContext.mastodonAuthenticationBox)
do {
_ = try await dependency.context.apiService.followRequest(
let newRelationship = try await dependency.context.apiService.followRequest(
userID: userID,
query: query,
authenticationBox: dependency.authContext.mastodonAuthenticationBox
)
).value
switch query {
case .accept:
notification.transientFollowRequestState = .init(state: .isAccept)
notification.followRequestState = .init(state: .isAccept)
case .reject:
break
}
await notificationView.configure(notification: notification, authenticationBox: dependency.authContext.mastodonAuthenticationBox)
} catch {
// reset state when failure
notification.transientFollowRequestState = .init(state: .none)
await notificationView.configure(notification: notification, authenticationBox: dependency.authContext.mastodonAuthenticationBox)
if let error = error as? Mastodon.API.Error {
switch error.httpResponseStatus {
case .notFound:
@ -88,17 +91,8 @@ extension DataSourceFacade {
)
}
}
return
}
switch query {
case .accept:
notification.transientFollowRequestState = .init(state: .isAccept)
notification.followRequestState = .init(state: .isAccept)
case .reject:
break
}
}
}

View File

@ -44,10 +44,10 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
barButtonItem: nil
),
completion: { (newRelationship: Mastodon.Entity.Relationship) in
//TODO: Update Relationship.
//TODO: Get Relationship into here, first!
print(newRelationship)
notification.relationship = newRelationship
Task { @MainActor in
notificationView.configure(notification: notification, authenticationBox: self.authContext.mastodonAuthenticationBox)
}
}
)
case .reportUser(_):
@ -117,9 +117,10 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
try await DataSourceFacade.responseToUserFollowRequestAction(
dependency: self,
notification: notification,
notificationView: notificationView,
query: .accept
)
} // end Task
}
}
func tableViewCell(
@ -141,9 +142,10 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
try await DataSourceFacade.responseToUserFollowRequestAction(
dependency: self,
notification: notification,
notificationView: notificationView,
query: .reject
)
} // end Task
}
}
}

View File

@ -9,6 +9,7 @@ import UIKit
import Combine
import CoreDataStack
import MastodonSDK
import MastodonCore
extension NotificationTableViewCell {
final class ViewModel {
@ -29,7 +30,8 @@ extension NotificationTableViewCell {
func configure(
tableView: UITableView,
viewModel: ViewModel,
delegate: NotificationTableViewCellDelegate?
delegate: NotificationTableViewCellDelegate?,
authenticationBox: MastodonAuthenticationBox
) {
if notificationView.frame == .zero {
// set status view width
@ -41,7 +43,7 @@ extension NotificationTableViewCell {
switch viewModel.value {
case .feed(let feed):
notificationView.configure(feed: feed)
notificationView.configure(feed: feed, authenticationBox: authenticationBox)
}
self.delegate = delegate

View File

@ -24,10 +24,10 @@ extension NotificationTimelineViewController: DataSourceProvider {
case .feed(let feed):
let item: DataSourceItem? = {
guard feed.kind == .notificationAll || feed.kind == .notificationMentions else { return nil }
//TODO: Get relationship
if let notification = feed.notification,
let mastodonNotification = MastodonNotification.fromEntity(notification, relationship: nil, domain: authContext.mastodonAuthenticationBox.domain) {
if let notification = feed.notification {
let mastodonNotification = MastodonNotification.fromEntity(notification, relationship: nil)
return .notification(record: mastodonNotification)
} else {
return nil
@ -38,7 +38,7 @@ extension NotificationTimelineViewController: DataSourceProvider {
return nil
}
}
func update(status: MastodonStatus) {
viewModel.dataController.update(status: status)
}

View File

@ -38,8 +38,6 @@ final class NotificationTimelineViewController: UIViewController, NeedsDependenc
}()
let cellFrameCache = NSCache<NSNumber, NSValue>()
}
extension NotificationTimelineViewController {

View File

@ -18,23 +18,24 @@ import MastodonLocalization
import MastodonSDK
extension NotificationView {
public func configure(feed: MastodonFeed) {
public func configure(feed: MastodonFeed, authenticationBox: MastodonAuthenticationBox) {
guard let notification = feed.notification else {
assertionFailure()
return
}
MastodonNotification.fromEntity(
let entity = MastodonNotification.fromEntity(
notification,
relationship: feed.relationship,
domain: viewModel.authContext?.mastodonAuthenticationBox.domain ?? ""
).map(configure(notification:))
relationship: feed.relationship
)
configure(notification: entity, authenticationBox: authenticationBox)
}
}
extension NotificationView {
public func configure(notification: MastodonNotification) {
configureAuthor(notification: notification)
public func configure(notification: MastodonNotification, authenticationBox: MastodonAuthenticationBox) {
configureAuthor(notification: notification, authenticationBox: authenticationBox)
switch notification.entity.type {
case .follow:
@ -57,10 +58,8 @@ extension NotificationView {
}
}
}
extension NotificationView {
private func configureAuthor(notification: MastodonNotification) {
private func configureAuthor(notification: MastodonNotification, authenticationBox: MastodonAuthenticationBox) {
let author = notification.account
// author avatar
@ -89,8 +88,6 @@ extension NotificationView {
// notification type indicator
let notificationIndicatorText: MetaContent?
if let type = MastodonNotificationType(rawValue: notification.entity.type.rawValue) {
self.viewModel.type = type
// TODO: fix the i18n. The subject should assert place at the string beginning
func createMetaContent(text: String, emojis: MastodonContent.Emojis) -> MetaContent {
let content = MastodonContent(content: text, emojis: emojis)
@ -193,7 +190,7 @@ extension NotificationView {
notificationTypeIndicatorLabel.reset()
}
if let me = viewModel.authContext?.mastodonAuthenticationBox.authentication.account() {
if let me = authenticationBox.authentication.account() {
let isMyself = (author == me)
let isMuting: Bool
let isBlocking: Bool
@ -213,7 +210,6 @@ extension NotificationView {
menuButton.showsMenuAsPrimaryAction = true
menuButton.isHidden = menuContext.isMyself
}
timestampUpdatePublisher
@ -239,5 +235,47 @@ extension NotificationView {
}
.store(in: &disposeBag)
switch notification.followRequestState.state {
case .isAccept:
self.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
self.acceptFollowRequestButton.isUserInteractionEnabled = false
self.acceptFollowRequestButton.setImage(nil, for: .normal)
self.acceptFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.accepted, for: .normal)
case .isReject:
self.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
self.rejectFollowRequestButton.isUserInteractionEnabled = false
self.rejectFollowRequestButton.setImage(nil, for: .normal)
self.rejectFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.rejected, for: .normal)
default:
break
}
let state = notification.transientFollowRequestState.state
if state == .isAccepting {
self.acceptFollowRequestActivityIndicatorView.startAnimating()
self.acceptFollowRequestButton.tintColor = .clear
self.acceptFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
self.acceptFollowRequestActivityIndicatorView.stopAnimating()
self.acceptFollowRequestButton.tintColor = .white
self.acceptFollowRequestButton.setTitleColor(.white, for: .normal)
}
if state == .isRejecting {
self.rejectFollowRequestActivityIndicatorView.startAnimating()
self.rejectFollowRequestButton.tintColor = .clear
self.rejectFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
self.rejectFollowRequestActivityIndicatorView.stopAnimating()
self.rejectFollowRequestButton.tintColor = .black
self.rejectFollowRequestButton.setTitleColor(.black, for: .normal)
}
if state == .isAccept {
self.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
if state == .isReject {
self.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
}
}

View File

@ -1,103 +0,0 @@
//
// NotificationView+ViewModel.swift
//
//
// Created by MainasuK on 2022-1-21.
//
import UIKit
import Combine
import Meta
import MastodonSDK
import MastodonAsset
import MastodonLocalization
import MastodonExtension
import MastodonCore
import CoreData
import CoreDataStack
import MastodonUI
extension NotificationView {
public final class ViewModel: ObservableObject {
public var disposeBag = Set<AnyCancellable>()
@Published public var authContext: AuthContext?
@Published public var type: MastodonNotificationType?
@Published public var notificationIndicatorText: MetaContent?
@Published public var authorName: MetaContent?
@Published public var authorUsername: String?
@Published public var timestamp: Date?
@Published public var followRequestState = MastodonFollowRequestState(state: .none)
@Published public var transientFollowRequestState = MastodonFollowRequestState(state: .none)
}
}
extension NotificationView.ViewModel {
func bind(notificationView: NotificationView) {
$authContext
.assign(to: \.authContext, on: notificationView.statusView.viewModel)
.store(in: &disposeBag)
$authContext
.assign(to: \.authContext, on: notificationView.quoteStatusView.viewModel)
.store(in: &disposeBag)
}
private func bindFollowRequest(notificationView: NotificationView) {
Publishers.CombineLatest(
$followRequestState,
$transientFollowRequestState
)
.sink { followRequestState, transientFollowRequestState in
switch followRequestState.state {
case .isAccept:
notificationView.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
notificationView.acceptFollowRequestButton.isUserInteractionEnabled = false
notificationView.acceptFollowRequestButton.setImage(nil, for: .normal)
notificationView.acceptFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.accepted, for: .normal)
case .isReject:
notificationView.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
notificationView.rejectFollowRequestButton.isUserInteractionEnabled = false
notificationView.rejectFollowRequestButton.setImage(nil, for: .normal)
notificationView.rejectFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.rejected, for: .normal)
default:
break
}
let state = transientFollowRequestState.state
if state == .isAccepting {
notificationView.acceptFollowRequestActivityIndicatorView.startAnimating()
notificationView.acceptFollowRequestButton.tintColor = .clear
notificationView.acceptFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.acceptFollowRequestActivityIndicatorView.stopAnimating()
notificationView.acceptFollowRequestButton.tintColor = .white
notificationView.acceptFollowRequestButton.setTitleColor(.white, for: .normal)
}
if state == .isRejecting {
notificationView.rejectFollowRequestActivityIndicatorView.startAnimating()
notificationView.rejectFollowRequestButton.tintColor = .clear
notificationView.rejectFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.rejectFollowRequestActivityIndicatorView.stopAnimating()
notificationView.rejectFollowRequestButton.tintColor = .black
notificationView.rejectFollowRequestButton.setTitleColor(.black, for: .normal)
}
UIView.animate(withDuration: 0.3) {
if state == .isAccept {
notificationView.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
if state == .isReject {
notificationView.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
}
}
.store(in: &disposeBag)
}
}

View File

@ -48,12 +48,6 @@ public final class NotificationView: UIView {
var notificationActions = [UIAccessibilityCustomAction]()
var authorActions = [UIAccessibilityCustomAction]()
public private(set) lazy var viewModel: ViewModel = {
let viewModel = ViewModel()
viewModel.bind(notificationView: self)
return viewModel
}()
let containerStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
@ -185,7 +179,6 @@ public final class NotificationView: UIView {
public func prepareForReuse() {
disposeBag.removeAll()
viewModel.authContext = nil
avatarButton.avatarImageView.image = nil
avatarButton.avatarImageView.cancelTask()

View File

@ -11,7 +11,7 @@ public final class MastodonNotification {
}
public let account: Mastodon.Entity.Account
public let relationship: Mastodon.Entity.Relationship?
public var relationship: Mastodon.Entity.Relationship?
public let status: MastodonStatus?
public let feeds: [MastodonFeed]
@ -28,7 +28,7 @@ public final class MastodonNotification {
}
public extension MastodonNotification {
static func fromEntity(_ entity: Mastodon.Entity.Notification, relationship: Mastodon.Entity.Relationship?, domain: String) -> MastodonNotification? {
static func fromEntity(_ entity: Mastodon.Entity.Notification, relationship: Mastodon.Entity.Relationship?) -> MastodonNotification {
return MastodonNotification(entity: entity, account: entity.account, relationship: relationship, status: entity.status.map(MastodonStatus.fromEntity), feeds: [])
}
}