forked from zelo72/mastodon-ios
fix: [WIP] add suggestion account scene back
This commit is contained in:
parent
c1e1d527fe
commit
54e84ed814
|
@ -483,6 +483,8 @@
|
||||||
DBB45B5927B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */; };
|
DBB45B5927B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */; };
|
||||||
DBB45B5B27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */; };
|
DBB45B5B27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */; };
|
||||||
DBB45B5E27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */; };
|
DBB45B5E27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */; };
|
||||||
|
DBB45B6027B50A4F002DC5A7 /* RecommendAccountItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5F27B50A4F002DC5A7 /* RecommendAccountItem.swift */; };
|
||||||
|
DBB45B6227B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */; };
|
||||||
DBB525082611EAC0002F1F29 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = DBB525072611EAC0002F1F29 /* Tabman */; };
|
DBB525082611EAC0002F1F29 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = DBB525072611EAC0002F1F29 /* Tabman */; };
|
||||||
DBB5250E2611EBAF002F1F29 /* ProfileSegmentedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB5250D2611EBAF002F1F29 /* ProfileSegmentedViewController.swift */; };
|
DBB5250E2611EBAF002F1F29 /* ProfileSegmentedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB5250D2611EBAF002F1F29 /* ProfileSegmentedViewController.swift */; };
|
||||||
DBB525212611EBD6002F1F29 /* ProfilePagingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB525202611EBD6002F1F29 /* ProfilePagingViewController.swift */; };
|
DBB525212611EBD6002F1F29 /* ProfilePagingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB525202611EBD6002F1F29 /* ProfilePagingViewController.swift */; };
|
||||||
|
@ -1227,6 +1229,8 @@
|
||||||
DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewVideoViewModel.swift; sourceTree = "<group>"; };
|
DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewVideoViewModel.swift; sourceTree = "<group>"; };
|
||||||
DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewTransitionViewController.swift; sourceTree = "<group>"; };
|
DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewTransitionViewController.swift; sourceTree = "<group>"; };
|
||||||
DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveMarginStatusTableViewCell.swift; sourceTree = "<group>"; };
|
DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveMarginStatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
DBB45B5F27B50A4F002DC5A7 /* RecommendAccountItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountItem.swift; sourceTree = "<group>"; };
|
||||||
|
DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SuggestionAccountViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||||
DBB5250D2611EBAF002F1F29 /* ProfileSegmentedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSegmentedViewController.swift; sourceTree = "<group>"; };
|
DBB5250D2611EBAF002F1F29 /* ProfileSegmentedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSegmentedViewController.swift; sourceTree = "<group>"; };
|
||||||
DBB525202611EBD6002F1F29 /* ProfilePagingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePagingViewController.swift; sourceTree = "<group>"; };
|
DBB525202611EBD6002F1F29 /* ProfilePagingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePagingViewController.swift; sourceTree = "<group>"; };
|
||||||
DBB5252F2611EBF3002F1F29 /* ProfilePagingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePagingViewModel.swift; sourceTree = "<group>"; };
|
DBB5252F2611EBF3002F1F29 /* ProfilePagingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePagingViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1756,6 +1760,7 @@
|
||||||
children = (
|
children = (
|
||||||
2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */,
|
2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */,
|
||||||
2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */,
|
2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */,
|
||||||
|
DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */,
|
||||||
2D4AD89A2631659400613EFC /* CollectionViewCell */,
|
2D4AD89A2631659400613EFC /* CollectionViewCell */,
|
||||||
2DAC9E43262FC9DE0062E1A6 /* TableViewCell */,
|
2DAC9E43262FC9DE0062E1A6 /* TableViewCell */,
|
||||||
);
|
);
|
||||||
|
@ -2012,6 +2017,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */,
|
2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */,
|
||||||
|
DBB45B5F27B50A4F002DC5A7 /* RecommendAccountItem.swift */,
|
||||||
);
|
);
|
||||||
path = RecommandAccount;
|
path = RecommandAccount;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -3845,6 +3851,7 @@
|
||||||
DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */,
|
DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */,
|
||||||
DBBC24DE26A54BCB00398BB9 /* MastodonMetricFormatter.swift in Sources */,
|
DBBC24DE26A54BCB00398BB9 /* MastodonMetricFormatter.swift in Sources */,
|
||||||
DB06180A2785B2AB0030EE79 /* MastodonRegisterAvatarTableViewCell.swift in Sources */,
|
DB06180A2785B2AB0030EE79 /* MastodonRegisterAvatarTableViewCell.swift in Sources */,
|
||||||
|
DBB45B6227B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift in Sources */,
|
||||||
DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */,
|
DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */,
|
||||||
DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */,
|
DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */,
|
||||||
DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */,
|
DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */,
|
||||||
|
@ -4068,6 +4075,7 @@
|
||||||
DB697DD1278F4871004EF2F7 /* AutoGenerateTableViewDelegate.swift in Sources */,
|
DB697DD1278F4871004EF2F7 /* AutoGenerateTableViewDelegate.swift in Sources */,
|
||||||
DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */,
|
DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */,
|
||||||
DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */,
|
DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */,
|
||||||
|
DBB45B6027B50A4F002DC5A7 /* RecommendAccountItem.swift in Sources */,
|
||||||
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */,
|
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */,
|
||||||
DB6180E626391B550018D199 /* MediaPreviewTransitionController.swift in Sources */,
|
DB6180E626391B550018D199 /* MediaPreviewTransitionController.swift in Sources */,
|
||||||
DB0FCB922796DE19006C02E2 /* TrendSectionHeaderCollectionReusableView.swift in Sources */,
|
DB0FCB922796DE19006C02E2 /* TrendSectionHeaderCollectionReusableView.swift in Sources */,
|
||||||
|
|
|
@ -7,32 +7,9 @@
|
||||||
|
|
||||||
import CoreData
|
import CoreData
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
enum SelectedAccountItem {
|
enum SelectedAccountItem: Hashable {
|
||||||
case accountObjectID(accountObjectID: NSManagedObjectID)
|
case account(ManagedObjectRecord<MastodonUser>)
|
||||||
case placeHolder(uuid: UUID)
|
case placeHolder(uuid: UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SelectedAccountItem: Equatable {
|
|
||||||
static func == (lhs: SelectedAccountItem, rhs: SelectedAccountItem) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case (.accountObjectID(let idLeft), .accountObjectID(let idRight)):
|
|
||||||
return idLeft == idRight
|
|
||||||
case (.placeHolder(let uuidLeft), .placeHolder(let uuidRight)):
|
|
||||||
return uuidLeft == uuidRight
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SelectedAccountItem: Hashable {
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
switch self {
|
|
||||||
case .accountObjectID(let id):
|
|
||||||
hasher.combine(id)
|
|
||||||
case .placeHolder(let id):
|
|
||||||
hasher.combine(id.uuidString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,15 +17,17 @@ enum SelectedAccountSection: Equatable, Hashable {
|
||||||
|
|
||||||
extension SelectedAccountSection {
|
extension SelectedAccountSection {
|
||||||
static func collectionViewDiffableDataSource(
|
static func collectionViewDiffableDataSource(
|
||||||
for collectionView: UICollectionView,
|
collectionView: UICollectionView,
|
||||||
managedObjectContext: NSManagedObjectContext
|
context: AppContext
|
||||||
) -> UICollectionViewDiffableDataSource<SelectedAccountSection, SelectedAccountItem> {
|
) -> UICollectionViewDiffableDataSource<SelectedAccountSection, SelectedAccountItem> {
|
||||||
UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item -> UICollectionViewCell? in
|
UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item -> UICollectionViewCell? in
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: SuggestionAccountCollectionViewCell.self), for: indexPath) as! SuggestionAccountCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: SuggestionAccountCollectionViewCell.self), for: indexPath) as! SuggestionAccountCollectionViewCell
|
||||||
switch item {
|
switch item {
|
||||||
case .accountObjectID(let objectID):
|
case .account(let record):
|
||||||
let user = managedObjectContext.object(with: objectID) as! MastodonUser
|
context.managedObjectContext.performAndWait {
|
||||||
cell.config(with: user)
|
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||||
|
cell.config(with: user)
|
||||||
|
}
|
||||||
case .placeHolder:
|
case .placeHolder:
|
||||||
cell.configAsPlaceHolder()
|
cell.configAsPlaceHolder()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,15 @@ final class UserFetchedResultsController: NSObject {
|
||||||
let fetchedResultsController: NSFetchedResultsController<MastodonUser>
|
let fetchedResultsController: NSFetchedResultsController<MastodonUser>
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let domain = CurrentValueSubject<String?, Never>(nil)
|
@Published var domain: String? = nil
|
||||||
let userIDs = CurrentValueSubject<[Mastodon.Entity.Account.ID], Never>([])
|
@Published var userIDs: [Mastodon.Entity.Account.ID] = []
|
||||||
|
|
||||||
// output
|
// output
|
||||||
let _objectIDs = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
let _objectIDs = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
||||||
@Published var records: [ManagedObjectRecord<MastodonUser>] = []
|
@Published var records: [ManagedObjectRecord<MastodonUser>] = []
|
||||||
|
|
||||||
init(managedObjectContext: NSManagedObjectContext, domain: String?, additionalTweetPredicate: NSPredicate?) {
|
init(managedObjectContext: NSManagedObjectContext, domain: String?, additionalTweetPredicate: NSPredicate?) {
|
||||||
self.domain.value = domain ?? ""
|
self.domain = domain ?? ""
|
||||||
self.fetchedResultsController = {
|
self.fetchedResultsController = {
|
||||||
let fetchRequest = MastodonUser.sortedFetchRequest
|
let fetchRequest = MastodonUser.sortedFetchRequest
|
||||||
fetchRequest.predicate = MastodonUser.predicate(domain: domain ?? "", ids: [])
|
fetchRequest.predicate = MastodonUser.predicate(domain: domain ?? "", ids: [])
|
||||||
|
@ -54,8 +54,8 @@ final class UserFetchedResultsController: NSObject {
|
||||||
fetchedResultsController.delegate = self
|
fetchedResultsController.delegate = self
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
self.domain.removeDuplicates(),
|
self.$domain.removeDuplicates(),
|
||||||
self.userIDs.removeDuplicates()
|
self.$userIDs.removeDuplicates()
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] domain, ids in
|
.sink { [weak self] domain, ids in
|
||||||
|
@ -79,11 +79,11 @@ final class UserFetchedResultsController: NSObject {
|
||||||
extension UserFetchedResultsController {
|
extension UserFetchedResultsController {
|
||||||
|
|
||||||
public func append(userIDs: [Mastodon.Entity.Account.ID]) {
|
public func append(userIDs: [Mastodon.Entity.Account.ID]) {
|
||||||
var result = self.userIDs.value
|
var result = self.userIDs
|
||||||
for userID in userIDs where !result.contains(userID) {
|
for userID in userIDs where !result.contains(userID) {
|
||||||
result.append(userID)
|
result.append(userID)
|
||||||
}
|
}
|
||||||
self.userIDs.value = result
|
self.userIDs = result
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ extension UserFetchedResultsController: NSFetchedResultsControllerDelegate {
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||||
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
|
||||||
let indexes = userIDs.value
|
let indexes = userIDs
|
||||||
let objects = fetchedResultsController.fetchedObjects ?? []
|
let objects = fetchedResultsController.fetchedObjects ?? []
|
||||||
|
|
||||||
let items: [NSManagedObjectID] = objects
|
let items: [NSManagedObjectID] = objects
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
//
|
||||||
|
// RecommendAccountItem.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK on 2022-2-10.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
|
enum RecommendAccountItem: Hashable {
|
||||||
|
case account(ManagedObjectRecord<MastodonUser>)
|
||||||
|
}
|
|
@ -129,22 +129,29 @@ enum RecommendAccountSection: Equatable, Hashable {
|
||||||
//
|
//
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//extension RecommendAccountSection {
|
extension RecommendAccountSection {
|
||||||
//
|
|
||||||
// static func tableViewDiffableDataSource(
|
struct Configuration {
|
||||||
// for tableView: UITableView,
|
weak var suggestionAccountTableViewCellDelegate: SuggestionAccountTableViewCellDelegate?
|
||||||
// managedObjectContext: NSManagedObjectContext,
|
}
|
||||||
// viewModel: SuggestionAccountViewModel,
|
|
||||||
// delegate: SuggestionAccountTableViewCellDelegate
|
static func tableViewDiffableDataSource(
|
||||||
// ) -> UITableViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID> {
|
tableView: UITableView,
|
||||||
// UITableViewDiffableDataSource(tableView: tableView) { [weak viewModel, weak delegate] (tableView, indexPath, objectID) -> UITableViewCell? in
|
context: AppContext,
|
||||||
// guard let viewModel = viewModel else { return nil }
|
configuration: Configuration
|
||||||
// let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SuggestionAccountTableViewCell.self)) as! SuggestionAccountTableViewCell
|
) -> UITableViewDiffableDataSource<RecommendAccountSection, RecommendAccountItem> {
|
||||||
// let user = managedObjectContext.object(with: objectID) as! MastodonUser
|
UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in
|
||||||
// let isSelected = viewModel.selectedAccounts.value.contains(objectID)
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SuggestionAccountTableViewCell.self)) as! SuggestionAccountTableViewCell
|
||||||
// cell.delegate = delegate
|
switch item {
|
||||||
// cell.config(with: user, isSelected: isSelected)
|
case .account(let record):
|
||||||
// return cell
|
context.managedObjectContext.performAndWait {
|
||||||
// }
|
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||||
// }
|
cell.config(with: user)
|
||||||
//}
|
}
|
||||||
|
}
|
||||||
|
cell.delegate = configuration.suggestionAccountTableViewCellDelegate
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -74,6 +74,15 @@ extension HomeTimelineViewController {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.showThreadAction(action)
|
self.showThreadAction(action)
|
||||||
},
|
},
|
||||||
|
UIAction(title: "Account Recommend", image: UIImage(systemName: "human"), attributes: []) { [weak self] action in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let suggestionAccountViewModel = SuggestionAccountViewModel(context: self.context)
|
||||||
|
self.coordinator.present(
|
||||||
|
scene: .suggestionAccount(viewModel: suggestionAccountViewModel),
|
||||||
|
from: self,
|
||||||
|
transition: .modal(animated: true, completion: nil)
|
||||||
|
)
|
||||||
|
},
|
||||||
UIAction(title: "Store Rating", image: UIImage(systemName: "star.fill"), attributes: []) { [weak self] action in
|
UIAction(title: "Store Rating", image: UIImage(systemName: "star.fill"), attributes: []) { [weak self] action in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let windowScene = self.view.window?.windowScene else { return }
|
guard let windowScene = self.view.window?.windowScene else { return }
|
||||||
|
|
|
@ -383,10 +383,13 @@ extension HomeTimelineViewController {
|
||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
|
|
||||||
@objc private func findPeopleButtonPressed(_ sender: PrimaryActionButton) {
|
@objc private func findPeopleButtonPressed(_ sender: PrimaryActionButton) {
|
||||||
// TODO:
|
let suggestionAccountViewModel = SuggestionAccountViewModel(context: context)
|
||||||
// let viewModel = SuggestionAccountViewModel(context: context)
|
suggestionAccountViewModel.delegate = viewModel
|
||||||
// viewModel.delegate = self.viewModel
|
coordinator.present(
|
||||||
// coordinator.present(scene: .suggestionAccount(viewModel: viewModel), from: self, transition: .modal(animated: true, completion: nil))
|
scene: .suggestionAccount(viewModel: suggestionAccountViewModel),
|
||||||
|
from: self,
|
||||||
|
transition: .modal(animated: true, completion: nil)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
|
@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
|
||||||
|
|
|
@ -119,8 +119,6 @@ final class HomeTimelineViewModel: NSObject {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension HomeTimelineViewModel: SuggestionAccountViewModelDelegate { }
|
|
||||||
|
|
||||||
extension HomeTimelineViewModel {
|
extension HomeTimelineViewModel {
|
||||||
struct ScrollPositionRecord {
|
struct ScrollPositionRecord {
|
||||||
let item: StatusItem
|
let item: StatusItem
|
||||||
|
@ -197,3 +195,9 @@ extension HomeTimelineViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - SuggestionAccountViewModelDelegate
|
||||||
|
extension HomeTimelineViewModel: SuggestionAccountViewModelDelegate {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ extension FollowerListViewModel.State {
|
||||||
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
viewModel.userFetchedResultsController.userIDs.value = []
|
viewModel.userFetchedResultsController.userIDs = []
|
||||||
|
|
||||||
stateMachine.enter(Loading.self)
|
stateMachine.enter(Loading.self)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ extension FollowerListViewModel.State {
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count) followers")
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count) followers")
|
||||||
|
|
||||||
var hasNewAppend = false
|
var hasNewAppend = false
|
||||||
var userIDs = viewModel.userFetchedResultsController.userIDs.value
|
var userIDs = viewModel.userFetchedResultsController.userIDs
|
||||||
for user in response.value {
|
for user in response.value {
|
||||||
guard !userIDs.contains(user.id) else { continue }
|
guard !userIDs.contains(user.id) else { continue }
|
||||||
userIDs.append(user.id)
|
userIDs.append(user.id)
|
||||||
|
@ -174,7 +174,7 @@ extension FollowerListViewModel.State {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.maxID = maxID
|
self.maxID = maxID
|
||||||
viewModel.userFetchedResultsController.userIDs.value = userIDs
|
viewModel.userFetchedResultsController.userIDs = userIDs
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch follower fail: \(error.localizedDescription)")
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch follower fail: \(error.localizedDescription)")
|
||||||
|
|
|
@ -72,7 +72,7 @@ extension FollowingListViewModel.State {
|
||||||
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
viewModel.userFetchedResultsController.userIDs.value = []
|
viewModel.userFetchedResultsController.userIDs = []
|
||||||
|
|
||||||
stateMachine.enter(Loading.self)
|
stateMachine.enter(Loading.self)
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ extension FollowingListViewModel.State {
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count)")
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch \(response.value.count)")
|
||||||
|
|
||||||
var hasNewAppend = false
|
var hasNewAppend = false
|
||||||
var userIDs = viewModel.userFetchedResultsController.userIDs.value
|
var userIDs = viewModel.userFetchedResultsController.userIDs
|
||||||
for user in response.value {
|
for user in response.value {
|
||||||
guard !userIDs.contains(user.id) else { continue }
|
guard !userIDs.contains(user.id) else { continue }
|
||||||
userIDs.append(user.id)
|
userIDs.append(user.id)
|
||||||
|
@ -174,7 +174,7 @@ extension FollowingListViewModel.State {
|
||||||
await enter(state: NoMore.self)
|
await enter(state: NoMore.self)
|
||||||
}
|
}
|
||||||
self.maxID = maxID
|
self.maxID = maxID
|
||||||
viewModel.userFetchedResultsController.userIDs.value = userIDs
|
viewModel.userFetchedResultsController.userIDs = userIDs
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch following fail: \(error.localizedDescription)")
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch following fail: \(error.localizedDescription)")
|
||||||
|
|
|
@ -20,12 +20,12 @@ final class MeProfileViewModel: ProfileViewModel {
|
||||||
optionalMastodonUser: context.authenticationService.activeMastodonAuthentication.value?.user
|
optionalMastodonUser: context.authenticationService.activeMastodonAuthentication.value?.user
|
||||||
)
|
)
|
||||||
|
|
||||||
self.currentMastodonUser
|
$me
|
||||||
.sink { [weak self] currentMastodonUser in
|
.sink { [weak self] me in
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: current active mastodon user: %s", ((#file as NSString).lastPathComponent), #line, #function, currentMastodonUser?.username ?? "<nil>")
|
os_log("%{public}s[%{public}ld], %{public}s: current active mastodon user: %s", ((#file as NSString).lastPathComponent), #line, #function, me?.username ?? "<nil>")
|
||||||
|
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.mastodonUser.value = currentMastodonUser
|
self.user = me
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -577,7 +577,7 @@ extension ProfileViewController {
|
||||||
|
|
||||||
private func bindProfileRelationship() {
|
private func bindProfileRelationship() {
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
viewModel.mastodonUser,
|
viewModel.$user,
|
||||||
viewModel.relationshipActionOptionSet
|
viewModel.relationshipActionOptionSet
|
||||||
)
|
)
|
||||||
.asyncMap { [weak self] user, relationshipSet -> UIMenu? in
|
.asyncMap { [weak self] user, relationshipSet -> UIMenu? in
|
||||||
|
@ -725,7 +725,7 @@ extension ProfileViewController {
|
||||||
|
|
||||||
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
||||||
Task {
|
Task {
|
||||||
let _activityViewController = try await DataSourceFacade.createActivityViewController(
|
let _activityViewController = try await DataSourceFacade.createActivityViewController(
|
||||||
|
@ -754,7 +754,7 @@ extension ProfileViewController {
|
||||||
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
guard let mastodonUser = viewModel.mastodonUser.value else { return }
|
guard let mastodonUser = viewModel.user else { return }
|
||||||
let composeViewModel = ComposeViewModel(
|
let composeViewModel = ComposeViewModel(
|
||||||
context: context,
|
context: context,
|
||||||
composeKind: .mention(user: .init(objectID: mastodonUser.objectID)),
|
composeKind: .mention(user: .init(objectID: mastodonUser.objectID)),
|
||||||
|
@ -849,7 +849,7 @@ extension ProfileViewController: ProfilePagingViewControllerDelegate {
|
||||||
// MARK: - ProfileHeaderViewDelegate
|
// MARK: - ProfileHeaderViewDelegate
|
||||||
extension ProfileViewController: ProfileHeaderViewDelegate {
|
extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, avatarButtonDidPressed button: AvatarButton) {
|
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, avatarButtonDidPressed button: AvatarButton) {
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
|
@ -865,7 +865,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, bannerImageViewDidPressed imageView: UIImageView) {
|
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, bannerImageViewDidPressed imageView: UIImageView) {
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
|
@ -956,7 +956,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case .follow, .request, .pending, .following:
|
case .follow, .request, .pending, .following:
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let reocrd = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
let reocrd = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
||||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
Task {
|
Task {
|
||||||
|
@ -968,7 +968,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
}
|
}
|
||||||
case .muting:
|
case .muting:
|
||||||
guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let name = user.displayNameWithFallback
|
let name = user.displayNameWithFallback
|
||||||
|
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
|
@ -993,7 +993,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||||
present(alertController, animated: true, completion: nil)
|
present(alertController, animated: true, completion: nil)
|
||||||
case .blocking:
|
case .blocking:
|
||||||
guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let name = user.displayNameWithFallback
|
let name = user.displayNameWithFallback
|
||||||
|
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
|
@ -1077,7 +1077,7 @@ extension ProfileViewController: ProfileAboutViewControllerDelegate {
|
||||||
extension ProfileViewController: MastodonMenuDelegate {
|
extension ProfileViewController: MastodonMenuDelegate {
|
||||||
func menuAction(_ action: MastodonMenu.Action) {
|
func menuAction(_ action: MastodonMenu.Action) {
|
||||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
guard let user = viewModel.mastodonUser.value else { return }
|
guard let user = viewModel.user else { return }
|
||||||
|
|
||||||
let userRecord: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
let userRecord: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ class ProfileViewModel: NSObject {
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
let mastodonUser: CurrentValueSubject<MastodonUser?, Never>
|
@Published var me: MastodonUser?
|
||||||
let currentMastodonUser = CurrentValueSubject<MastodonUser?, Never>(nil)
|
@Published var user: MastodonUser?
|
||||||
let viewDidAppear = PassthroughSubject<Void, Never>()
|
let viewDidAppear = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
// output
|
// output
|
||||||
|
@ -73,7 +73,7 @@ class ProfileViewModel: NSObject {
|
||||||
|
|
||||||
init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) {
|
init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mastodonUser = CurrentValueSubject(mastodonUser)
|
self.user = mastodonUser
|
||||||
self.domain = CurrentValueSubject(context.authenticationService.activeMastodonAuthenticationBox.value?.domain)
|
self.domain = CurrentValueSubject(context.authenticationService.activeMastodonAuthenticationBox.value?.domain)
|
||||||
self.userID = CurrentValueSubject(mastodonUser?.id)
|
self.userID = CurrentValueSubject(mastodonUser?.id)
|
||||||
self.bannerImageURL = CurrentValueSubject(mastodonUser?.headerImageURL())
|
self.bannerImageURL = CurrentValueSubject(mastodonUser?.headerImageURL())
|
||||||
|
@ -98,21 +98,21 @@ class ProfileViewModel: NSObject {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// bind active authentication
|
// bind active authentication
|
||||||
context.authenticationService.activeMastodonAuthentication
|
context.authenticationService.activeMastodonAuthenticationBox
|
||||||
.sink { [weak self] activeMastodonAuthentication in
|
.sink { [weak self] authenticationBox in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let activeMastodonAuthentication = activeMastodonAuthentication else {
|
guard let authenticationBox = authenticationBox else {
|
||||||
self.domain.value = nil
|
self.domain.value = nil
|
||||||
self.currentMastodonUser.value = nil
|
self.me = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.domain.value = activeMastodonAuthentication.domain
|
self.domain.value = authenticationBox.domain
|
||||||
self.currentMastodonUser.value = activeMastodonAuthentication.user
|
self.me = authenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// query relationship
|
// query relationship
|
||||||
let userRecord = self.mastodonUser.map { user -> ManagedObjectRecord<MastodonUser>? in
|
let userRecord = $user.map { user -> ManagedObjectRecord<MastodonUser>? in
|
||||||
user.flatMap { ManagedObjectRecord<MastodonUser>(objectID: $0.objectID) }
|
user.flatMap { ManagedObjectRecord<MastodonUser>(objectID: $0.objectID) }
|
||||||
}
|
}
|
||||||
let pendingRetryPublisher = CurrentValueSubject<TimeInterval, Never>(1)
|
let pendingRetryPublisher = CurrentValueSubject<TimeInterval, Never>(1)
|
||||||
|
@ -176,18 +176,18 @@ class ProfileViewModel: NSObject {
|
||||||
extension ProfileViewModel {
|
extension ProfileViewModel {
|
||||||
private func setup() {
|
private func setup() {
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
mastodonUser.eraseToAnyPublisher(),
|
$user,
|
||||||
currentMastodonUser.eraseToAnyPublisher()
|
$me
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] mastodonUser, currentMastodonUser in
|
.sink { [weak self] user, me in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// Update view model attribute
|
// Update view model attribute
|
||||||
self.update(mastodonUser: mastodonUser)
|
self.update(mastodonUser: user)
|
||||||
self.update(mastodonUser: mastodonUser, currentMastodonUser: currentMastodonUser)
|
self.update(mastodonUser: user, currentMastodonUser: me)
|
||||||
|
|
||||||
// Setup observer for user
|
// Setup observer for user
|
||||||
if let mastodonUser = mastodonUser {
|
if let mastodonUser = user {
|
||||||
// setup observer
|
// setup observer
|
||||||
self.mastodonUserObserver = ManagedObjectObserver.observe(object: mastodonUser)
|
self.mastodonUserObserver = ManagedObjectObserver.observe(object: mastodonUser)
|
||||||
.sink { completion in
|
.sink { completion in
|
||||||
|
@ -203,7 +203,7 @@ extension ProfileViewModel {
|
||||||
switch changeType {
|
switch changeType {
|
||||||
case .update:
|
case .update:
|
||||||
self.update(mastodonUser: mastodonUser)
|
self.update(mastodonUser: mastodonUser)
|
||||||
self.update(mastodonUser: mastodonUser, currentMastodonUser: currentMastodonUser)
|
self.update(mastodonUser: mastodonUser, currentMastodonUser: me)
|
||||||
case .delete:
|
case .delete:
|
||||||
// TODO:
|
// TODO:
|
||||||
break
|
break
|
||||||
|
@ -215,7 +215,7 @@ extension ProfileViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup observer for user
|
// Setup observer for user
|
||||||
if let currentMastodonUser = currentMastodonUser {
|
if let currentMastodonUser = me {
|
||||||
// setup observer
|
// setup observer
|
||||||
self.currentMastodonUserObserver = ManagedObjectObserver.observe(object: currentMastodonUser)
|
self.currentMastodonUserObserver = ManagedObjectObserver.observe(object: currentMastodonUser)
|
||||||
.sink { completion in
|
.sink { completion in
|
||||||
|
@ -230,7 +230,7 @@ extension ProfileViewModel {
|
||||||
guard let changeType = change.changeType else { return }
|
guard let changeType = change.changeType else { return }
|
||||||
switch changeType {
|
switch changeType {
|
||||||
case .update:
|
case .update:
|
||||||
self.update(mastodonUser: mastodonUser, currentMastodonUser: currentMastodonUser)
|
self.update(mastodonUser: user, currentMastodonUser: currentMastodonUser)
|
||||||
case .delete:
|
case .delete:
|
||||||
// TODO:
|
// TODO:
|
||||||
break
|
break
|
||||||
|
@ -347,13 +347,14 @@ extension ProfileViewModel {
|
||||||
|
|
||||||
// fetch profile info before edit
|
// fetch profile info before edit
|
||||||
func fetchEditProfileInfo() -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
func fetchEditProfileInfo() -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||||
guard let currentMastodonUser = currentMastodonUser.value,
|
guard let me = me,
|
||||||
let mastodonAuthentication = currentMastodonUser.mastodonAuthentication else {
|
let mastodonAuthentication = me.mastodonAuthentication
|
||||||
|
else {
|
||||||
return Fail(error: APIService.APIError.implicit(.authenticationMissing)).eraseToAnyPublisher()
|
return Fail(error: APIService.APIError.implicit(.authenticationMissing)).eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
let authorization = Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.userAccessToken)
|
let authorization = Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.userAccessToken)
|
||||||
return context.apiService.accountVerifyCredentials(domain: currentMastodonUser.domain, authorization: authorization)
|
return context.apiService.accountVerifyCredentials(domain: me.domain, authorization: authorization)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRelationship(
|
private func updateRelationship(
|
||||||
|
|
|
@ -48,7 +48,7 @@ final class RemoteProfileViewModel: ProfileViewModel {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.mastodonUser.value = mastodonUser
|
self.user = mastodonUser
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ extension SearchDetailViewController {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
break
|
break
|
||||||
case .people:
|
case .people:
|
||||||
viewController.viewModel.userFetchedResultsController.userIDs.value = allSearchScopeViewController.viewModel.userFetchedResultsController.userIDs.value
|
viewController.viewModel.userFetchedResultsController.userIDs = allSearchScopeViewController.viewModel.userFetchedResultsController.userIDs
|
||||||
case .hashtags:
|
case .hashtags:
|
||||||
viewController.viewModel.hashtags = allSearchScopeViewController.viewModel.hashtags
|
viewController.viewModel.hashtags = allSearchScopeViewController.viewModel.hashtags
|
||||||
case .posts:
|
case .posts:
|
||||||
|
|
|
@ -155,7 +155,7 @@ extension SearchResultViewModel.State {
|
||||||
|
|
||||||
// reset data source when the search is refresh
|
// reset data source when the search is refresh
|
||||||
if offset == nil {
|
if offset == nil {
|
||||||
viewModel.userFetchedResultsController.userIDs.value = []
|
viewModel.userFetchedResultsController.userIDs = []
|
||||||
viewModel.statusFetchedResultsController.statusIDs.value = []
|
viewModel.statusFetchedResultsController.statusIDs.value = []
|
||||||
viewModel.hashtags = []
|
viewModel.hashtags = []
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ final class SearchResultViewModel {
|
||||||
|
|
||||||
context.authenticationService.activeMastodonAuthenticationBox
|
context.authenticationService.activeMastodonAuthenticationBox
|
||||||
.map { $0?.domain }
|
.map { $0?.domain }
|
||||||
.assign(to: \.value, on: userFetchedResultsController.domain)
|
.assign(to: \.domain, on: userFetchedResultsController)
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
context.authenticationService.activeMastodonAuthenticationBox
|
context.authenticationService.activeMastodonAuthenticationBox
|
||||||
|
|
|
@ -15,12 +15,43 @@ import MastodonAsset
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
|
|
||||||
class SuggestionAccountViewController: UIViewController, NeedsDependency {
|
class SuggestionAccountViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
static let collectionViewHeight: CGFloat = 24 + 64 + 24
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
var viewModel: SuggestionAccountViewModel!
|
var viewModel: SuggestionAccountViewModel!
|
||||||
|
|
||||||
|
private static func createCollectionViewLayout() -> UICollectionViewLayout {
|
||||||
|
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(64), heightDimension: .absolute(64))
|
||||||
|
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||||
|
|
||||||
|
let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
|
||||||
|
|
||||||
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
|
section.contentInsets = NSDirectionalEdgeInsets(top: 24, leading: 0, bottom: 24, trailing: 0)
|
||||||
|
section.orthogonalScrollingBehavior = .continuous
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
section.interGroupSpacing = 16
|
||||||
|
|
||||||
|
return UICollectionViewCompositionalLayout(section: section)
|
||||||
|
}
|
||||||
|
|
||||||
|
let collectionView: UICollectionView = {
|
||||||
|
let collectionViewLayout = SuggestionAccountViewController.createCollectionViewLayout()
|
||||||
|
let view = ControlContainableCollectionView(
|
||||||
|
frame: .zero,
|
||||||
|
collectionViewLayout: collectionViewLayout
|
||||||
|
)
|
||||||
|
view.register(SuggestionAccountCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: SuggestionAccountCollectionViewCell.self))
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
view.showsHorizontalScrollIndicator = false
|
||||||
|
view.showsVerticalScrollIndicator = false
|
||||||
|
view.layer.masksToBounds = false
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
let tableView: UITableView = {
|
let tableView: UITableView = {
|
||||||
let tableView = ControlContainableTableView()
|
let tableView = ControlContainableTableView()
|
||||||
|
@ -32,34 +63,6 @@ class SuggestionAccountViewController: UIViewController, NeedsDependency {
|
||||||
return tableView
|
return tableView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var tableHeader: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
|
|
||||||
view.frame = CGRect(origin: .zero, size: CGSize(width: tableView.frame.width, height: 156))
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
let followExplainLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.text = L10n.Scene.SuggestionAccount.followExplain
|
|
||||||
label.textColor = Asset.Colors.Label.primary.color
|
|
||||||
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
|
|
||||||
label.numberOfLines = 0
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
let selectedCollectionView: UICollectionView = {
|
|
||||||
let flowLayout = UICollectionViewFlowLayout()
|
|
||||||
flowLayout.scrollDirection = .horizontal
|
|
||||||
let view = ControlContainableCollectionView(frame: .zero, collectionViewLayout: flowLayout)
|
|
||||||
view.register(SuggestionAccountCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: SuggestionAccountCollectionViewCell.self))
|
|
||||||
view.backgroundColor = .clear
|
|
||||||
view.showsHorizontalScrollIndicator = false
|
|
||||||
view.showsVerticalScrollIndicator = false
|
|
||||||
view.layer.masksToBounds = false
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", (#file as NSString).lastPathComponent, #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", (#file as NSString).lastPathComponent, #line, #function)
|
||||||
}
|
}
|
||||||
|
@ -68,164 +71,135 @@ class SuggestionAccountViewController: UIViewController, NeedsDependency {
|
||||||
extension SuggestionAccountViewController {
|
extension SuggestionAccountViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
fatalError()
|
|
||||||
|
|
||||||
// setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
|
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
|
||||||
// ThemeService.shared.currentTheme
|
ThemeService.shared.currentTheme
|
||||||
// .receive(on: RunLoop.main)
|
.receive(on: RunLoop.main)
|
||||||
// .sink { [weak self] theme in
|
.sink { [weak self] theme in
|
||||||
// guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// self.setupBackgroundColor(theme: theme)
|
self.setupBackgroundColor(theme: theme)
|
||||||
// }
|
}
|
||||||
// .store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
//
|
|
||||||
// title = L10n.Scene.SuggestionAccount.title
|
title = L10n.Scene.SuggestionAccount.title
|
||||||
// navigationItem.rightBarButtonItem
|
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||||
// = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done,
|
barButtonSystemItem: UIBarButtonItem.SystemItem.done,
|
||||||
// target: self,
|
target: self,
|
||||||
// action: #selector(SuggestionAccountViewController.doneButtonDidClick(_:)))
|
action: #selector(SuggestionAccountViewController.doneButtonDidClick(_:))
|
||||||
//
|
)
|
||||||
// tableView.delegate = self
|
|
||||||
// tableView.translatesAutoresizingMaskIntoConstraints = false
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// view.addSubview(tableView)
|
view.addSubview(collectionView)
|
||||||
// NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
// tableView.topAnchor.constraint(equalTo: view.topAnchor),
|
collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
|
||||||
// tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
// tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
// tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
collectionView.heightAnchor.constraint(equalToConstant: SuggestionAccountViewController.collectionViewHeight),
|
||||||
// ])
|
])
|
||||||
// viewModel.diffableDataSource = RecommendAccountSection.tableViewDiffableDataSource(
|
defer { view.bringSubviewToFront(collectionView) }
|
||||||
// for: tableView,
|
|
||||||
// managedObjectContext: context.managedObjectContext,
|
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// viewModel: viewModel,
|
view.addSubview(tableView)
|
||||||
// delegate: self
|
NSLayoutConstraint.activate([
|
||||||
// )
|
tableView.topAnchor.constraint(equalTo: collectionView.bottomAnchor),
|
||||||
//
|
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
// viewModel.collectionDiffableDataSource = SelectedAccountSection.collectionViewDiffableDataSource(for: selectedCollectionView, managedObjectContext: context.managedObjectContext)
|
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
//
|
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
// viewModel.accounts
|
])
|
||||||
// .receive(on: DispatchQueue.main)
|
|
||||||
// .sink { [weak self] accounts in
|
collectionView.delegate = self
|
||||||
// guard let self = self else { return }
|
viewModel.setupDiffableDataSource(
|
||||||
// self.setupHeader(accounts: accounts)
|
collectionView: collectionView
|
||||||
// }
|
)
|
||||||
// .store(in: &disposeBag)
|
|
||||||
|
tableView.delegate = self
|
||||||
|
viewModel.setupDiffableDataSource(
|
||||||
|
tableView: tableView,
|
||||||
|
suggestionAccountTableViewCellDelegate: self
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
tableView.deselectRow(with: transitionCoordinator, animated: animated)
|
tableView.deselectRow(with: transitionCoordinator, animated: animated)
|
||||||
viewModel.checkAccountsFollowState()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillLayoutSubviews() {
|
|
||||||
super.viewWillLayoutSubviews()
|
|
||||||
let avatarImageViewHeight: Double = 56
|
|
||||||
let avatarImageViewCount = Int(floor((Double(view.frame.width) - 20) / (avatarImageViewHeight + 15)))
|
|
||||||
viewModel.headerPlaceholderCount.value = avatarImageViewCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupHeader(accounts: [NSManagedObjectID]) {
|
|
||||||
if accounts.isEmpty {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
followExplainLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
tableHeader.addSubview(followExplainLabel)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
followExplainLabel.topAnchor.constraint(equalTo: tableHeader.topAnchor, constant: 20),
|
|
||||||
followExplainLabel.leadingAnchor.constraint(equalTo: tableHeader.leadingAnchor, constant: 20),
|
|
||||||
tableHeader.trailingAnchor.constraint(equalTo: followExplainLabel.trailingAnchor, constant: 20),
|
|
||||||
])
|
|
||||||
|
|
||||||
selectedCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
tableHeader.addSubview(selectedCollectionView)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
selectedCollectionView.frameLayoutGuide.topAnchor.constraint(equalTo: followExplainLabel.topAnchor, constant: 20),
|
|
||||||
selectedCollectionView.frameLayoutGuide.leadingAnchor.constraint(equalTo: tableHeader.leadingAnchor, constant: 20),
|
|
||||||
selectedCollectionView.frameLayoutGuide.trailingAnchor.constraint(equalTo: tableHeader.trailingAnchor),
|
|
||||||
selectedCollectionView.frameLayoutGuide.bottomAnchor.constraint(equalTo: tableHeader.bottomAnchor),
|
|
||||||
])
|
|
||||||
selectedCollectionView.delegate = self
|
|
||||||
|
|
||||||
tableView.tableHeaderView = tableHeader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupBackgroundColor(theme: Theme) {
|
private func setupBackgroundColor(theme: Theme) {
|
||||||
view.backgroundColor = theme.systemBackgroundColor
|
view.backgroundColor = theme.systemBackgroundColor
|
||||||
tableHeader.backgroundColor = theme.systemGroupedBackgroundColor
|
collectionView.backgroundColor = theme.systemGroupedBackgroundColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SuggestionAccountViewController: UICollectionViewDelegateFlowLayout {
|
// MARK: - UICollectionViewDelegateFlowLayout
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
|
extension SuggestionAccountViewController: UICollectionViewDelegate {
|
||||||
15
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
|
||||||
CGSize(width: 56, height: 56)
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
guard let diffableDataSource = viewModel.collectionDiffableDataSource else { return }
|
// guard let diffableDataSource = viewModel.collectionDiffableDataSource else { return }
|
||||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
// guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
switch item {
|
// switch item {
|
||||||
case .accountObjectID(let accountObjectID):
|
// case .accountObjectID(let accountObjectID):
|
||||||
let mastodonUser = context.managedObjectContext.object(with: accountObjectID) as! MastodonUser
|
// let mastodonUser = context.managedObjectContext.object(with: accountObjectID) as! MastodonUser
|
||||||
let viewModel = ProfileViewModel(context: context, optionalMastodonUser: mastodonUser)
|
// let viewModel = ProfileViewModel(context: context, optionalMastodonUser: mastodonUser)
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
|
// self.coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
|
||||||
}
|
// }
|
||||||
default:
|
// default:
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - UITableViewDelegate
|
||||||
extension SuggestionAccountViewController: UITableViewDelegate {
|
extension SuggestionAccountViewController: UITableViewDelegate {
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let tableViewDiffableDataSource = viewModel.tableViewDiffableDataSource else { return }
|
||||||
guard let objectID = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
guard let item = tableViewDiffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
let mastodonUser = context.managedObjectContext.object(with: objectID) as! MastodonUser
|
switch item {
|
||||||
let viewModel = ProfileViewModel(context: context, optionalMastodonUser: mastodonUser)
|
case .account(let record):
|
||||||
DispatchQueue.main.async {
|
guard let account = record.object(in: context.managedObjectContext) else { return }
|
||||||
self.coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
|
let cachedProfileViewModel = CachedProfileViewModel(context: context, mastodonUser: account)
|
||||||
|
coordinator.present(
|
||||||
|
scene: .profile(viewModel: cachedProfileViewModel),
|
||||||
|
from: self,
|
||||||
|
transition: .show
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SuggestionAccountViewController: SuggestionAccountTableViewCellDelegate {
|
extension SuggestionAccountViewController: SuggestionAccountTableViewCellDelegate {
|
||||||
func accountButtonPressed(objectID: NSManagedObjectID, cell: SuggestionAccountTableViewCell) {
|
func accountButtonPressed(objectID: NSManagedObjectID, cell: SuggestionAccountTableViewCell) {
|
||||||
let selected = !viewModel.selectedAccounts.value.contains(objectID)
|
// let selected = !viewModel.selectedAccounts.value.contains(objectID)
|
||||||
cell.startAnimating()
|
// cell.startAnimating()
|
||||||
viewModel.followAction(objectID: objectID)?
|
// viewModel.followAction(objectID: objectID)?
|
||||||
.sink(receiveCompletion: { [weak self] completion in
|
// .sink(receiveCompletion: { [weak self] completion in
|
||||||
guard let self = self else { return }
|
// guard let self = self else { return }
|
||||||
cell.stopAnimating()
|
// cell.stopAnimating()
|
||||||
switch completion {
|
// switch completion {
|
||||||
case .failure(let error):
|
// case .failure(let error):
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: follow failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
// os_log("%{public}s[%{public}ld], %{public}s: follow failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
||||||
case .finished:
|
// case .finished:
|
||||||
var selectedAccounts = self.viewModel.selectedAccounts.value
|
// var selectedAccounts = self.viewModel.selectedAccounts.value
|
||||||
if selected {
|
// if selected {
|
||||||
selectedAccounts.append(objectID)
|
// selectedAccounts.append(objectID)
|
||||||
} else {
|
// } else {
|
||||||
selectedAccounts.removeAll { $0 == objectID }
|
// selectedAccounts.removeAll { $0 == objectID }
|
||||||
}
|
// }
|
||||||
cell.button.isSelected = selected
|
// cell.button.isSelected = selected
|
||||||
self.viewModel.selectedAccounts.value = selectedAccounts
|
// self.viewModel.selectedAccounts.value = selectedAccounts
|
||||||
}
|
// }
|
||||||
}, receiveValue: { _ in
|
// }, receiveValue: { _ in
|
||||||
})
|
// })
|
||||||
.store(in: &disposeBag)
|
// .store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SuggestionAccountViewController {
|
extension SuggestionAccountViewController {
|
||||||
@objc func doneButtonDidClick(_ sender: UIButton) {
|
@objc func doneButtonDidClick(_ sender: UIButton) {
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
if viewModel.selectedAccounts.value.count > 0 {
|
// if viewModel.selectedAccounts.value.count > 0 {
|
||||||
viewModel.delegate?.homeTimelineNeedRefresh.send()
|
// viewModel.delegate?.homeTimelineNeedRefresh.send()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// SuggestionAccountViewModel+Diffable.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK on 2022-2-10.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension SuggestionAccountViewModel {
|
||||||
|
|
||||||
|
func setupDiffableDataSource(
|
||||||
|
tableView: UITableView,
|
||||||
|
suggestionAccountTableViewCellDelegate: SuggestionAccountTableViewCellDelegate
|
||||||
|
) {
|
||||||
|
tableViewDiffableDataSource = RecommendAccountSection.tableViewDiffableDataSource(
|
||||||
|
tableView: tableView,
|
||||||
|
context: context,
|
||||||
|
configuration: RecommendAccountSection.Configuration(
|
||||||
|
suggestionAccountTableViewCellDelegate: suggestionAccountTableViewCellDelegate
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
userFetchedResultsController.$records
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] records in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let tableViewDiffableDataSource = self.tableViewDiffableDataSource else { return }
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, RecommendAccountItem>()
|
||||||
|
snapshot.appendSections([.main])
|
||||||
|
let items: [RecommendAccountItem] = records.map { RecommendAccountItem.account($0) }
|
||||||
|
snapshot.appendItems(items, toSection: .main)
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
tableViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
|
||||||
|
} else {
|
||||||
|
// Fallback on earlier versions
|
||||||
|
tableViewDiffableDataSource.applySnapshot(snapshot, animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupDiffableDataSource(
|
||||||
|
collectionView: UICollectionView
|
||||||
|
) {
|
||||||
|
collectionViewDiffableDataSource = SelectedAccountSection.collectionViewDiffableDataSource(
|
||||||
|
collectionView: collectionView,
|
||||||
|
context: context
|
||||||
|
)
|
||||||
|
|
||||||
|
userFetchedResultsController.$records
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] records in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let collectionViewDiffableDataSource = self.collectionViewDiffableDataSource else { return }
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<SelectedAccountSection, SelectedAccountItem>()
|
||||||
|
snapshot.appendSections([.main])
|
||||||
|
let items: [SelectedAccountItem] = records.map { SelectedAccountItem.account($0) }
|
||||||
|
snapshot.appendItems(items, toSection: .main)
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
collectionViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
|
||||||
|
} else {
|
||||||
|
// Fallback on earlier versions
|
||||||
|
collectionViewDiffableDataSource.applySnapshot(snapshot, animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,177 +20,175 @@ protocol SuggestionAccountViewModelDelegate: AnyObject {
|
||||||
final class SuggestionAccountViewModel: NSObject {
|
final class SuggestionAccountViewModel: NSObject {
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
weak var delegate: SuggestionAccountViewModelDelegate?
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
|
let userFetchedResultsController: UserFetchedResultsController
|
||||||
|
|
||||||
let currentMastodonUser = CurrentValueSubject<MastodonUser?, Never>(nil)
|
var viewWillAppear = PassthroughSubject<Void, Never>()
|
||||||
weak var delegate: SuggestionAccountViewModelDelegate?
|
|
||||||
// output
|
|
||||||
let accounts = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
|
||||||
var selectedAccounts = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
|
||||||
|
|
||||||
|
// output
|
||||||
|
var collectionViewDiffableDataSource: UICollectionViewDiffableDataSource<SelectedAccountSection, SelectedAccountItem>?
|
||||||
|
var tableViewDiffableDataSource: UITableViewDiffableDataSource<RecommendAccountSection, RecommendAccountItem>?
|
||||||
|
|
||||||
|
@Published var selectedAccounts: [ManagedObjectRecord<MastodonUser>] = []
|
||||||
var headerPlaceholderCount = CurrentValueSubject<Int?, Never>(nil)
|
var headerPlaceholderCount = CurrentValueSubject<Int?, Never>(nil)
|
||||||
var suggestionAccountsFallback = PassthroughSubject<Void, Never>()
|
var suggestionAccountsFallback = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
var viewWillAppear = PassthroughSubject<Void, Never>()
|
|
||||||
|
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID>? {
|
init(
|
||||||
didSet(value) {
|
context: AppContext
|
||||||
if !accounts.value.isEmpty {
|
) {
|
||||||
applyTableViewDataSource(accounts: accounts.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var collectionDiffableDataSource: UICollectionViewDiffableDataSource<SelectedAccountSection, SelectedAccountItem>?
|
|
||||||
|
|
||||||
init(context: AppContext, accounts: [NSManagedObjectID]? = nil) {
|
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.userFetchedResultsController = UserFetchedResultsController(
|
||||||
|
managedObjectContext: context.managedObjectContext,
|
||||||
|
domain: nil,
|
||||||
|
additionalTweetPredicate: nil
|
||||||
|
)
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
// Publishers.CombineLatest(
|
||||||
self.accounts,
|
// $accounts,
|
||||||
self.selectedAccounts
|
// $selectedAccounts
|
||||||
)
|
// )
|
||||||
.receive(on: RunLoop.main)
|
// .receive(on: RunLoop.main)
|
||||||
.sink { [weak self] accounts,selectedAccounts in
|
// .sink { [weak self] accounts,selectedAccounts in
|
||||||
self?.applyTableViewDataSource(accounts: accounts)
|
// self?.applyTableViewDataSource(accounts: accounts)
|
||||||
self?.applySelectedCollectionViewDataSource(accounts: selectedAccounts)
|
// self?.applySelectedCollectionViewDataSource(accounts: selectedAccounts)
|
||||||
}
|
// }
|
||||||
.store(in: &disposeBag)
|
// .store(in: &disposeBag)
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
// Publishers.CombineLatest(
|
||||||
self.selectedAccounts,
|
// self.selectedAccounts,
|
||||||
self.headerPlaceholderCount
|
// self.headerPlaceholderCount
|
||||||
)
|
// )
|
||||||
.receive(on: RunLoop.main)
|
// .receive(on: RunLoop.main)
|
||||||
.sink { [weak self] selectedAccount,count in
|
// .sink { [weak self] selectedAccount,count in
|
||||||
self?.applySelectedCollectionViewDataSource(accounts: selectedAccount)
|
// self?.applySelectedCollectionViewDataSource(accounts: selectedAccount)
|
||||||
}
|
// }
|
||||||
.store(in: &disposeBag)
|
// .store(in: &disposeBag)
|
||||||
|
//
|
||||||
|
// viewWillAppear
|
||||||
|
// .sink { [weak self] _ in
|
||||||
|
// self?.checkAccountsFollowState()
|
||||||
|
// }
|
||||||
|
// .store(in: &disposeBag)
|
||||||
|
//
|
||||||
|
// context.authenticationService.activeMastodonAuthentication
|
||||||
|
// .sink { [weak self] activeMastodonAuthentication in
|
||||||
|
// guard let self = self else { return }
|
||||||
|
// guard let activeMastodonAuthentication = activeMastodonAuthentication else {
|
||||||
|
// self.currentMastodonUser.value = nil
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// self.currentMastodonUser.value = activeMastodonAuthentication.user
|
||||||
|
// }
|
||||||
|
// .store(in: &disposeBag)
|
||||||
|
|
||||||
viewWillAppear
|
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
.sink { [weak self] _ in
|
|
||||||
self?.checkAccountsFollowState()
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
if let accounts = accounts {
|
|
||||||
self.accounts.value = accounts
|
|
||||||
}
|
|
||||||
|
|
||||||
context.authenticationService.activeMastodonAuthentication
|
|
||||||
.sink { [weak self] activeMastodonAuthentication in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let activeMastodonAuthentication = activeMastodonAuthentication else {
|
|
||||||
self.currentMastodonUser.value = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.currentMastodonUser.value = activeMastodonAuthentication.user
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
if accounts == nil || (accounts ?? []).isEmpty {
|
|
||||||
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
||||||
|
|
||||||
context.apiService.suggestionAccountV2(domain: activeMastodonAuthenticationBox.domain, query: nil, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
|
||||||
.sink { [weak self] completion in
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
if let apiError = error as? Mastodon.API.Error {
|
|
||||||
if apiError.httpResponseStatus == .notFound {
|
|
||||||
self?.suggestionAccountsFallback.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: fetch recommendAccountV2 failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
|
||||||
case .finished:
|
|
||||||
// handle isFetchingLatestTimeline in fetch controller delegate
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} receiveValue: { [weak self] response in
|
|
||||||
let ids = response.value.map(\.account.id)
|
|
||||||
self?.receiveAccounts(ids: ids)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
suggestionAccountsFallback
|
|
||||||
.sink(receiveValue: { [weak self] _ in
|
|
||||||
self?.requestSuggestionAccount()
|
|
||||||
})
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestSuggestionAccount() {
|
|
||||||
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
||||||
context.apiService.suggestionAccount(domain: activeMastodonAuthenticationBox.domain, query: nil, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
|
||||||
.sink { completion in
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: fetch recommendAccount failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
|
||||||
case .finished:
|
|
||||||
// handle isFetchingLatestTimeline in fetch controller delegate
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} receiveValue: { [weak self] response in
|
|
||||||
let ids = response.value.map(\.id)
|
|
||||||
self?.receiveAccounts(ids: ids)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyTableViewDataSource(accounts: [NSManagedObjectID]) {
|
|
||||||
assert(Thread.isMainThread)
|
|
||||||
guard let dataSource = diffableDataSource else { return }
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, NSManagedObjectID>()
|
|
||||||
snapshot.appendSections([.main])
|
|
||||||
snapshot.appendItems(accounts, toSection: .main)
|
|
||||||
dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applySelectedCollectionViewDataSource(accounts: [NSManagedObjectID]) {
|
|
||||||
assert(Thread.isMainThread)
|
|
||||||
guard let count = headerPlaceholderCount.value else { return }
|
|
||||||
guard let dataSource = collectionDiffableDataSource else { return }
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<SelectedAccountSection, SelectedAccountItem>()
|
|
||||||
snapshot.appendSections([.main])
|
|
||||||
let placeholderCount = count - accounts.count
|
|
||||||
let accountItems = accounts.map { SelectedAccountItem.accountObjectID(accountObjectID: $0) }
|
|
||||||
snapshot.appendItems(accountItems, toSection: .main)
|
|
||||||
|
|
||||||
if placeholderCount > 0 {
|
|
||||||
for _ in 0 ..< placeholderCount {
|
|
||||||
snapshot.appendItems([SelectedAccountItem.placeHolder(uuid: UUID())], toSection: .main)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func receiveAccounts(ids: [String]) {
|
|
||||||
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let userFetchRequest = MastodonUser.sortedFetchRequest
|
userFetchedResultsController.domain = authenticationBox.domain
|
||||||
userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids)
|
|
||||||
let mastodonUsers: [MastodonUser]? = {
|
Task {
|
||||||
let userFetchRequest = MastodonUser.sortedFetchRequest
|
var userIDs: [MastodonUser.ID] = []
|
||||||
userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids)
|
|
||||||
userFetchRequest.returnsObjectsAsFaults = false
|
|
||||||
do {
|
do {
|
||||||
return try self.context.managedObjectContext.fetch(userFetchRequest)
|
let response = try await context.apiService.suggestionAccountV2(
|
||||||
|
query: nil,
|
||||||
|
authenticationBox: authenticationBox
|
||||||
|
)
|
||||||
|
userIDs = response.value.map { $0.account.id }
|
||||||
|
} catch let error as Mastodon.API.Error where error.httpResponseStatus == .notFound {
|
||||||
|
let response = try await context.apiService.suggestionAccount(
|
||||||
|
query: nil,
|
||||||
|
authenticationBox: authenticationBox
|
||||||
|
)
|
||||||
|
userIDs = response.value.map { $0.id }
|
||||||
} catch {
|
} catch {
|
||||||
assertionFailure(error.localizedDescription)
|
os_log("%{public}s[%{public}ld], %{public}s: fetch recommendAccountV2 failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
if let users = mastodonUsers {
|
guard !userIDs.isEmpty else { return }
|
||||||
let sortedUsers = users.sorted { (user1, user2) -> Bool in
|
userFetchedResultsController.userIDs = userIDs
|
||||||
(ids.firstIndex(of: user1.id) ?? 0) < (ids.firstIndex(of: user2.id) ?? 0)
|
|
||||||
}
|
|
||||||
accounts.value = sortedUsers.map(\.objectID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// .sink { [weak self] completion in
|
||||||
|
// switch completion {
|
||||||
|
// case .failure(let error):
|
||||||
|
// if let apiError = error as? Mastodon.API.Error {
|
||||||
|
// if apiError.httpResponseStatus == .notFound {
|
||||||
|
// self?.suggestionAccountsFallback.send()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// case .finished:
|
||||||
|
// // handle isFetchingLatestTimeline in fetch controller delegate
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// } receiveValue: { [weak self] response in
|
||||||
|
// let ids = response.value.map(\.account.id)
|
||||||
|
// self?.receiveAccounts(ids: ids)
|
||||||
|
// }
|
||||||
|
// .store(in: &disposeBag)
|
||||||
|
//
|
||||||
|
// suggestionAccountsFallback
|
||||||
|
// .sink(receiveValue: { [weak self] _ in
|
||||||
|
// self?.requestSuggestionAccount()
|
||||||
|
// })
|
||||||
|
// .store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func applyTableViewDataSource(accounts: [NSManagedObjectID]) {
|
||||||
|
// assert(Thread.isMainThread)
|
||||||
|
// guard let dataSource = diffableDataSource else { return }
|
||||||
|
// var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, NSManagedObjectID>()
|
||||||
|
// snapshot.appendSections([.main])
|
||||||
|
// snapshot.appendItems(accounts, toSection: .main)
|
||||||
|
// dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func applySelectedCollectionViewDataSource(accounts: [NSManagedObjectID]) {
|
||||||
|
// assert(Thread.isMainThread)
|
||||||
|
// guard let count = headerPlaceholderCount.value else { return }
|
||||||
|
// guard let dataSource = collectionDiffableDataSource else { return }
|
||||||
|
// var snapshot = NSDiffableDataSourceSnapshot<SelectedAccountSection, SelectedAccountItem>()
|
||||||
|
// snapshot.appendSections([.main])
|
||||||
|
// let placeholderCount = count - accounts.count
|
||||||
|
// let accountItems = accounts.map { SelectedAccountItem.accountObjectID(accountObjectID: $0) }
|
||||||
|
// snapshot.appendItems(accountItems, toSection: .main)
|
||||||
|
//
|
||||||
|
// if placeholderCount > 0 {
|
||||||
|
// for _ in 0 ..< placeholderCount {
|
||||||
|
// snapshot.appendItems([SelectedAccountItem.placeHolder(uuid: UUID())], toSection: .main)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func receiveAccounts(userIDs: [MastodonUser.ID]) {
|
||||||
|
// guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// let request = MastodonUser.sortedFetchRequest
|
||||||
|
// request.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: userIDs)
|
||||||
|
// let mastodonUsers: [MastodonUser]? = {
|
||||||
|
// let userFetchRequest = MastodonUser.sortedFetchRequest
|
||||||
|
// userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids)
|
||||||
|
// userFetchRequest.returnsObjectsAsFaults = false
|
||||||
|
// do {
|
||||||
|
// return try self.context.managedObjectContext.fetch(userFetchRequest)
|
||||||
|
// } catch {
|
||||||
|
// assertionFailure(error.localizedDescription)
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
// if let users = mastodonUsers {
|
||||||
|
// let sortedUsers = users.sorted { (user1, user2) -> Bool in
|
||||||
|
// (ids.firstIndex(of: user1.id) ?? 0) < (ids.firstIndex(of: user2.id) ?? 0)
|
||||||
|
// }
|
||||||
|
// accounts.value = sortedUsers.map(\.objectID)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func followAction(objectID: NSManagedObjectID) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>? {
|
func followAction(objectID: NSManagedObjectID) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>? {
|
||||||
fatalError()
|
fatalError()
|
||||||
|
@ -203,8 +201,8 @@ final class SuggestionAccountViewModel: NSObject {
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAccountsFollowState() {
|
// func checkAccountsFollowState() {
|
||||||
fatalError()
|
// fatalError()
|
||||||
// guard let currentMastodonUser = currentMastodonUser.value else {
|
// guard let currentMastodonUser = currentMastodonUser.value else {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
@ -229,5 +227,5 @@ final class SuggestionAccountViewModel: NSObject {
|
||||||
// }.map(\.objectID)
|
// }.map(\.objectID)
|
||||||
//
|
//
|
||||||
// selectedAccounts.value = followingUsers
|
// selectedAccounts.value = followingUsers
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ extension SuggestionAccountTableViewCell {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func config(with account: MastodonUser, isSelected: Bool) {
|
func config(with account: MastodonUser) {
|
||||||
if let url = account.avatarImageURL() {
|
if let url = account.avatarImageURL() {
|
||||||
_imageView.af.setImage(
|
_imageView.af.setImage(
|
||||||
withURL: url,
|
withURL: url,
|
||||||
|
|
|
@ -14,68 +14,62 @@ import OSLog
|
||||||
|
|
||||||
extension APIService {
|
extension APIService {
|
||||||
func suggestionAccount(
|
func suggestionAccount(
|
||||||
domain: String,
|
|
||||||
query: Mastodon.API.Suggestions.Query?,
|
query: Mastodon.API.Suggestions.Query?,
|
||||||
mastodonAuthenticationBox: MastodonAuthenticationBox
|
authenticationBox: MastodonAuthenticationBox
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> {
|
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> {
|
||||||
fatalError()
|
|
||||||
// let authorization = mastodonAuthenticationBox.userAuthorization
|
let response = try await Mastodon.API.Suggestions.get(
|
||||||
//
|
session: session,
|
||||||
// return Mastodon.API.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization)
|
domain: authenticationBox.domain,
|
||||||
// .flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> in
|
query: query,
|
||||||
// let log = OSLog.api
|
authorization: authenticationBox.userAuthorization
|
||||||
// return self.backgroundManagedObjectContext.performChanges {
|
).singleOutput()
|
||||||
// response.value.forEach { user in
|
|
||||||
// let (mastodonUser,isCreated) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: user, userCache: nil, networkDate: Date(), log: log)
|
let managedObjectContext = backgroundManagedObjectContext
|
||||||
// let flag = isCreated ? "+" : "-"
|
try await managedObjectContext.performChanges {
|
||||||
// os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)%s", (#file as NSString).lastPathComponent, #line, #function, flag, mastodonUser.id, mastodonUser.username)
|
for entity in response.value {
|
||||||
// }
|
_ = Persistence.MastodonUser.createOrMerge(
|
||||||
// }
|
in: managedObjectContext,
|
||||||
// .setFailureType(to: Error.self)
|
context: Persistence.MastodonUser.PersistContext(
|
||||||
// .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Account]> in
|
domain: authenticationBox.domain,
|
||||||
// switch result {
|
entity: entity,
|
||||||
// case .success:
|
cache: nil,
|
||||||
// return response
|
networkDate: response.networkDate
|
||||||
// case .failure(let error):
|
)
|
||||||
// throw error
|
)
|
||||||
// }
|
} // end for … in
|
||||||
// }
|
}
|
||||||
// .eraseToAnyPublisher()
|
|
||||||
// }
|
return response
|
||||||
// .eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func suggestionAccountV2(
|
func suggestionAccountV2(
|
||||||
domain: String,
|
|
||||||
query: Mastodon.API.Suggestions.Query?,
|
query: Mastodon.API.Suggestions.Query?,
|
||||||
mastodonAuthenticationBox: MastodonAuthenticationBox
|
authenticationBox: MastodonAuthenticationBox
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]>, Error> {
|
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]> {
|
||||||
fatalError()
|
let response = try await Mastodon.API.V2.Suggestions.get(
|
||||||
// let authorization = mastodonAuthenticationBox.userAuthorization
|
session: session,
|
||||||
//
|
domain: authenticationBox.domain,
|
||||||
// return Mastodon.API.V2.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization)
|
query: query,
|
||||||
// .flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]>, Error> in
|
authorization: authenticationBox.userAuthorization
|
||||||
// let log = OSLog.api
|
).singleOutput()
|
||||||
// return self.backgroundManagedObjectContext.performChanges {
|
|
||||||
// response.value.forEach { suggestionAccount in
|
let managedObjectContext = backgroundManagedObjectContext
|
||||||
// let user = suggestionAccount.account
|
try await managedObjectContext.performChanges {
|
||||||
// let (mastodonUser,isCreated) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: user, userCache: nil, networkDate: Date(), log: log)
|
for entity in response.value {
|
||||||
// let flag = isCreated ? "+" : "-"
|
_ = Persistence.MastodonUser.createOrMerge(
|
||||||
// os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)%s", (#file as NSString).lastPathComponent, #line, #function, flag, mastodonUser.id, mastodonUser.username)
|
in: managedObjectContext,
|
||||||
// }
|
context: Persistence.MastodonUser.PersistContext(
|
||||||
// }
|
domain: authenticationBox.domain,
|
||||||
// .setFailureType(to: Error.self)
|
entity: entity.account,
|
||||||
// .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]> in
|
cache: nil,
|
||||||
// switch result {
|
networkDate: response.networkDate
|
||||||
// case .success:
|
)
|
||||||
// return response
|
)
|
||||||
// case .failure(let error):
|
} // end for … in
|
||||||
// throw error
|
}
|
||||||
// }
|
|
||||||
// }
|
return response
|
||||||
// .eraseToAnyPublisher()
|
|
||||||
// }
|
|
||||||
// .eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue