parent
d51519e4a1
commit
5381371839
@ -0,0 +1,147 @@
|
|||||||
|
//
|
||||||
|
// OtherMessageViewController.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2024/3/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
class OtherMessageViewModel: ViewModel, ViewModelType {
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
let viewWillAppear: ControlEvent<Bool>
|
||||||
|
let modelSelected: Driver<MessageSectionItem>
|
||||||
|
let footerRefresh: Observable<Void>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let items: BehaviorRelay<[MessageSection]>
|
||||||
|
let modelSelected: Driver<MessageSectionItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = BehaviorRelay<[MessageSection]>.init(value: [])
|
||||||
|
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
input.viewWillAppear.subscribe { _ in
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Output.init(items: items,
|
||||||
|
modelSelected: input.modelSelected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestMessageList() -> Observable<MessageList> {
|
||||||
|
return self.provider.messageList()
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OtherMessageViewController: TableViewController {
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func makeUI() {
|
||||||
|
super.makeUI()
|
||||||
|
|
||||||
|
|
||||||
|
tableView.register(MessageActivitiesViewCell.self, forCellReuseIdentifier: "MessageActivitiesViewCell")
|
||||||
|
|
||||||
|
self.emptyDataSetDescription = "暂无活动消息!"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel() {
|
||||||
|
super.bindViewModel()
|
||||||
|
|
||||||
|
guard let viewModel = viewModel as? OtherMessageViewModel else { return }
|
||||||
|
|
||||||
|
let input = OtherMessageViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||||
|
modelSelected: tableView.rx.modelSelected(MessageSectionItem.self).asDriver(),
|
||||||
|
footerRefresh: footerRefreshTrigger)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let output = viewModel.transform(input: input)
|
||||||
|
let dataSource = PrivateMessageViewController.dataSource()
|
||||||
|
|
||||||
|
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
output.modelSelected.drive { [weak self] messageSectionItem in
|
||||||
|
|
||||||
|
// switch messageSectionItem {
|
||||||
|
// case .customMessage(let customMessageType):
|
||||||
|
// self?.navigateToScreen(customMessageType: customMessageType)
|
||||||
|
// case .messageItem(let message): break
|
||||||
|
// case .activitiesItem(let message): break
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension OtherMessageViewController {
|
||||||
|
|
||||||
|
static func dataSource() -> RxTableViewSectionedReloadDataSource<MessageSection> {
|
||||||
|
return RxTableViewSectionedReloadDataSource<MessageSection>(
|
||||||
|
configureCell: { dataSource, tableView, indexPath, item in
|
||||||
|
switch dataSource[indexPath] {
|
||||||
|
case .customMessage(let customMessageType):
|
||||||
|
let cell: MessageCellView = tableView.dequeueReusableCell(withIdentifier: "MessageCellView", for: indexPath) as! MessageCellView
|
||||||
|
|
||||||
|
cell.customMessageType = customMessageType
|
||||||
|
|
||||||
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
case .messageItem(let message):
|
||||||
|
let cell: MessageCellView = tableView.dequeueReusableCell(withIdentifier: "MessageCellView", for: indexPath) as! MessageCellView
|
||||||
|
|
||||||
|
cell.message = message
|
||||||
|
|
||||||
|
return cell
|
||||||
|
case .activitiesItem(let message):
|
||||||
|
let cell: MessageActivitiesViewCell = tableView.dequeueReusableCell(withIdentifier: "MessageActivitiesViewCell", for: indexPath) as! MessageActivitiesViewCell
|
||||||
|
|
||||||
|
cell.message = message
|
||||||
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,216 @@
|
|||||||
|
//
|
||||||
|
// PrivateMessageViewController.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2024/3/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import FSPopoverView
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateMessageViewModel: ViewModel, ViewModelType {
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
let viewWillAppear: ControlEvent<Bool>
|
||||||
|
let modelSelected: Driver<MessageSectionItem>
|
||||||
|
let footerRefresh: Observable<Void>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let items: BehaviorRelay<[MessageSection]>
|
||||||
|
let modelSelected: Driver<MessageSectionItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = BehaviorRelay<[MessageSection]>.init(value: [])
|
||||||
|
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
input.viewWillAppear.subscribe { _ in
|
||||||
|
|
||||||
|
self.requestMessageList().subscribe { messageList in
|
||||||
|
var items = [messageList.comment, messageList.thumbup, messageList.follow]
|
||||||
|
|
||||||
|
let comment = MessageSectionItem.customMessage(customMessageType: .comment(messageList.comment?.sendUserNickName ?? ""))
|
||||||
|
let thumbup = MessageSectionItem.customMessage(customMessageType: .like(messageList.thumbup?.sendUserNickName ?? ""))
|
||||||
|
let follow = MessageSectionItem.customMessage(customMessageType: .follow(messageList.follow?.sendUserNickName ?? ""))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.items.accept([MessageSection.message(items: [comment, thumbup, follow])])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} onError: { error in
|
||||||
|
|
||||||
|
}.disposed(by: self.rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Output.init(items: items,
|
||||||
|
modelSelected: input.modelSelected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestMessageList() -> Observable<MessageList> {
|
||||||
|
return self.provider.messageList()
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateMessageViewController: TableViewController {
|
||||||
|
private let menuView: PopoverListView = {
|
||||||
|
let menuView = PopoverListView.init(scrollDirection: .horizontal)
|
||||||
|
do {
|
||||||
|
let features: [PopoverMenu] = [.blockList, .delete]
|
||||||
|
let items: [FSPopoverListItem] = features.map { feature in
|
||||||
|
let item = FSPopoverListTextItem(scrollDirection: .horizontal)
|
||||||
|
item.title = feature.description
|
||||||
|
item.titleFont = UIFont.systemFont(ofSize: 12)
|
||||||
|
item.isSeparatorHidden = false
|
||||||
|
item.titleColor = .white
|
||||||
|
item.contentInset = .init(top: 9, left: 15, bottom: 9, right: 15)
|
||||||
|
item.selectedHandler = { item in
|
||||||
|
guard let item = item as? FSPopoverListTextItem else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print(item.title ?? "")
|
||||||
|
}
|
||||||
|
item.updateLayout()
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
items.last?.isSeparatorHidden = true
|
||||||
|
menuView.shadowOpacity = 0
|
||||||
|
menuView.shadowRadius = 0
|
||||||
|
menuView.shadowColor = .clear
|
||||||
|
menuView.items = items
|
||||||
|
menuView.cornerRadius = 3
|
||||||
|
menuView.transitioningDelegate = nil
|
||||||
|
|
||||||
|
menuView.arrowSize = CGSize.init(width: 18, height: 5)
|
||||||
|
menuView.backgroundColor = .primaryText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func makeUI() {
|
||||||
|
super.makeUI()
|
||||||
|
|
||||||
|
|
||||||
|
tableView.register(MessageCellView.self, forCellReuseIdentifier: "MessageCellView")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel() {
|
||||||
|
super.bindViewModel()
|
||||||
|
|
||||||
|
guard let viewModel = viewModel as? PrivateMessageViewModel else { return }
|
||||||
|
|
||||||
|
let input = PrivateMessageViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||||
|
modelSelected: tableView.rx.modelSelected(MessageSectionItem.self).asDriver(),
|
||||||
|
footerRefresh: footerRefreshTrigger)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let output = viewModel.transform(input: input)
|
||||||
|
let dataSource = PrivateMessageViewController.dataSource()
|
||||||
|
|
||||||
|
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
output.modelSelected.drive { [weak self] messageSectionItem in
|
||||||
|
|
||||||
|
switch messageSectionItem {
|
||||||
|
case .customMessage(let customMessageType):
|
||||||
|
self?.navigateToScreen(customMessageType: customMessageType)
|
||||||
|
case .messageItem(let message): break
|
||||||
|
case .activitiesItem(let message): break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigateToScreen(customMessageType: CustomMessageType) {
|
||||||
|
guard let viewModel = viewModel as? PrivateMessageViewModel else { return }
|
||||||
|
|
||||||
|
switch customMessageType {
|
||||||
|
case .comment:
|
||||||
|
let myCommentListViewModel = MyCommentListViewModel(provider: viewModel.provider)
|
||||||
|
self.navigator.show(segue: .myCommentList(viewModel: myCommentListViewModel), sender: self)
|
||||||
|
case .like:
|
||||||
|
let myLikeListViewModel = MyLikeListViewModel(provider: viewModel.provider)
|
||||||
|
self.navigator.show(segue: .myLikeList(viewModel: myLikeListViewModel), sender: self)
|
||||||
|
case .follow:
|
||||||
|
let followersViewModel = FollowersViewModel(provider: viewModel.provider)
|
||||||
|
self.navigator.show(segue: .followers(viewModel: followersViewModel), sender: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PrivateMessageViewController {
|
||||||
|
|
||||||
|
static func dataSource() -> RxTableViewSectionedReloadDataSource<MessageSection> {
|
||||||
|
return RxTableViewSectionedReloadDataSource<MessageSection>(
|
||||||
|
configureCell: { dataSource, tableView, indexPath, item in
|
||||||
|
switch dataSource[indexPath] {
|
||||||
|
case .customMessage(let customMessageType):
|
||||||
|
let cell: MessageCellView = tableView.dequeueReusableCell(withIdentifier: "MessageCellView", for: indexPath) as! MessageCellView
|
||||||
|
|
||||||
|
cell.customMessageType = customMessageType
|
||||||
|
|
||||||
|
|
||||||
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
case .messageItem(let message):
|
||||||
|
let cell: MessageCellView = tableView.dequeueReusableCell(withIdentifier: "MessageCellView", for: indexPath) as! MessageCellView
|
||||||
|
|
||||||
|
cell.message = message
|
||||||
|
|
||||||
|
return cell
|
||||||
|
case .activitiesItem(let message):
|
||||||
|
let cell: MessageActivitiesViewCell = tableView.dequeueReusableCell(withIdentifier: "MessageActivitiesViewCell", for: indexPath) as! MessageActivitiesViewCell
|
||||||
|
|
||||||
|
cell.message = message
|
||||||
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,218 @@
|
|||||||
|
//
|
||||||
|
// BlackListViewController.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2024/3/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import FSPopoverView
|
||||||
|
|
||||||
|
|
||||||
|
class BlackListViewModel: ViewModel, ViewModelType {
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
let viewWillAppear: ControlEvent<Bool>
|
||||||
|
let headerRefresh: Observable<Void>
|
||||||
|
let footerRefresh: Observable<Void>
|
||||||
|
let modelSelected: Driver<User>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let items: BehaviorRelay<[FollowersSection]>
|
||||||
|
let modelSelected: Driver<User>
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = BehaviorRelay<[FollowersSection]>.init(value: [])
|
||||||
|
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
input.viewWillAppear.subscribe { _ in
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
input.headerRefresh
|
||||||
|
.flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
|
||||||
|
guard let self = self else { return Observable.just([]) }
|
||||||
|
self.page = 1
|
||||||
|
|
||||||
|
return self.requestBlockList(page: page, size: 10)
|
||||||
|
.trackActivity(self.headerLoading)
|
||||||
|
|
||||||
|
})
|
||||||
|
.subscribe(onNext: { (items) in
|
||||||
|
self.items.accept([FollowersSection.init(items: items)])
|
||||||
|
}).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
input.footerRefresh
|
||||||
|
.flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
|
||||||
|
guard let self = self else { return Observable.just([]) }
|
||||||
|
self.page += 1
|
||||||
|
|
||||||
|
return self.requestBlockList(page: page, size: 10)
|
||||||
|
.trackActivity(self.headerLoading)
|
||||||
|
})
|
||||||
|
.subscribe(onNext: { (items) in
|
||||||
|
|
||||||
|
var arr = self.items.value.first?.items
|
||||||
|
arr?.append(contentsOf: items)
|
||||||
|
|
||||||
|
self.items.accept([FollowersSection.init(items: arr ?? [])])
|
||||||
|
}).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Output.init(items: items,
|
||||||
|
modelSelected: input.modelSelected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestBlockList(page: Int, size: Int) -> Observable<[User]> {
|
||||||
|
return self.provider.blackList(page: page, size: size)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestLike(objectId: String, collectType: LikeType) -> Observable<Void> {
|
||||||
|
return self.provider.like(objectId: objectId, collectType: collectType.rawValue)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestCancelLike(objectId: String, collectType: LikeType) -> Observable<Void> {
|
||||||
|
return self.provider.cancelLike(objectId: objectId, collectType: collectType.rawValue)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BlackListViewController: TableViewController {
|
||||||
|
private let menuView: PopoverListView = {
|
||||||
|
let menuView = PopoverListView.init(scrollDirection: .horizontal)
|
||||||
|
do {
|
||||||
|
let features: [PopoverMenu] = [.blockList, .delete]
|
||||||
|
let items: [FSPopoverListItem] = features.map { feature in
|
||||||
|
let item = FSPopoverListTextItem(scrollDirection: .horizontal)
|
||||||
|
item.title = feature.description
|
||||||
|
item.titleFont = UIFont.systemFont(ofSize: 12)
|
||||||
|
item.isSeparatorHidden = false
|
||||||
|
item.titleColor = .white
|
||||||
|
item.contentInset = .init(top: 9, left: 15, bottom: 9, right: 15)
|
||||||
|
item.selectedHandler = { item in
|
||||||
|
guard let item = item as? FSPopoverListTextItem else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print(item.title ?? "")
|
||||||
|
}
|
||||||
|
item.updateLayout()
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
items.last?.isSeparatorHidden = true
|
||||||
|
menuView.shadowOpacity = 0
|
||||||
|
menuView.shadowRadius = 0
|
||||||
|
menuView.shadowColor = .clear
|
||||||
|
menuView.items = items
|
||||||
|
menuView.cornerRadius = 3
|
||||||
|
menuView.transitioningDelegate = nil
|
||||||
|
|
||||||
|
menuView.arrowSize = CGSize.init(width: 18, height: 5)
|
||||||
|
menuView.backgroundColor = .primaryText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func makeUI() {
|
||||||
|
super.makeUI()
|
||||||
|
|
||||||
|
|
||||||
|
tableView.register(FollowingViewCell.self, forCellReuseIdentifier: "FollowingViewCell")
|
||||||
|
emptyDataSetDescription = "你还没有把人拉黑!"
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel() {
|
||||||
|
super.bindViewModel()
|
||||||
|
|
||||||
|
guard let viewModel = viewModel as? BlackListViewModel else { return }
|
||||||
|
|
||||||
|
let input = BlackListViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||||
|
headerRefresh: headerRefreshTrigger,
|
||||||
|
footerRefresh: footerRefreshTrigger, modelSelected: tableView.rx.modelSelected(User.self).asDriver())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let output = viewModel.transform(input: input)
|
||||||
|
let dataSource = BlackListViewController.dataSource { cell, user in
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
output.modelSelected.drive { [weak self] messageSectionItem in
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BlackListViewController {
|
||||||
|
//TODO
|
||||||
|
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, User) -> Void) -> RxTableViewSectionedReloadDataSource<FollowersSection> {
|
||||||
|
return RxTableViewSectionedReloadDataSource<FollowersSection>(
|
||||||
|
configureCell: { dataSource, tableView, indexPath, item in
|
||||||
|
let cell: FollowingViewCell = tableView.dequeueReusableCell(withIdentifier: "FollowingViewCell", for: indexPath) as! FollowingViewCell
|
||||||
|
|
||||||
|
cell.user = item
|
||||||
|
|
||||||
|
|
||||||
|
cell.followingButton.setTitle("取消", for: .normal)
|
||||||
|
cell.followingButton.setTitle("添加", for: .selected)
|
||||||
|
if item.relation == .mutualFollowing {
|
||||||
|
cell.followingButton.isSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.buttonTapCallback = {audioTrack in
|
||||||
|
buttonTapHandler(cell, audioTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,237 @@
|
|||||||
|
//
|
||||||
|
// MyFollowersViewController.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2024/3/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import FSPopoverView
|
||||||
|
|
||||||
|
|
||||||
|
class MyFollowersViewModel: ViewModel, ViewModelType {
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
let viewWillAppear: ControlEvent<Bool>
|
||||||
|
let headerRefresh: Observable<Void>
|
||||||
|
let footerRefresh: Observable<Void>
|
||||||
|
let modelSelected: Driver<User>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let items: BehaviorRelay<[FollowersSection]>
|
||||||
|
let modelSelected: Driver<User>
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = BehaviorRelay<[FollowersSection]>.init(value: [])
|
||||||
|
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
input.viewWillAppear.subscribe { _ in
|
||||||
|
self.requestFollowersList(page: self.page, size: 10)
|
||||||
|
.subscribe { users in
|
||||||
|
self.items.accept([FollowersSection.init(items: users)])
|
||||||
|
|
||||||
|
} onError: { error in
|
||||||
|
|
||||||
|
}.disposed(by: self.rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
input.headerRefresh
|
||||||
|
.flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
|
||||||
|
guard let self = self else { return Observable.just([]) }
|
||||||
|
self.page = 1
|
||||||
|
|
||||||
|
return self.requestFollowersList(page: page, size: 10)
|
||||||
|
.trackActivity(self.headerLoading)
|
||||||
|
|
||||||
|
})
|
||||||
|
.subscribe(onNext: { (items) in
|
||||||
|
self.items.accept([FollowersSection.init(items: items)])
|
||||||
|
}).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
input.footerRefresh
|
||||||
|
.flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
|
||||||
|
guard let self = self else { return Observable.just([]) }
|
||||||
|
self.page += 1
|
||||||
|
|
||||||
|
return self.requestFollowersList(page: page, size: 10)
|
||||||
|
.trackActivity(self.headerLoading)
|
||||||
|
})
|
||||||
|
.subscribe(onNext: { (items) in
|
||||||
|
|
||||||
|
var arr = self.items.value.first?.items
|
||||||
|
arr?.append(contentsOf: items)
|
||||||
|
|
||||||
|
self.items.accept([FollowersSection.init(items: arr ?? [])])
|
||||||
|
}).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Output.init(items: items,
|
||||||
|
modelSelected: input.modelSelected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestFollowersList(page: Int, size: Int) -> Observable<[User]> {
|
||||||
|
return self.provider.followerList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: page, size: size)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestLike(objectId: String, collectType: LikeType) -> Observable<Void> {
|
||||||
|
return self.provider.like(objectId: objectId, collectType: collectType.rawValue)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func requestCancelLike(objectId: String, collectType: LikeType) -> Observable<Void> {
|
||||||
|
return self.provider.cancelLike(objectId: objectId, collectType: collectType.rawValue)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.trackError(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MyFollowersViewController: TableViewController {
|
||||||
|
private let menuView: PopoverListView = {
|
||||||
|
let menuView = PopoverListView.init(scrollDirection: .horizontal)
|
||||||
|
do {
|
||||||
|
let features: [PopoverMenu] = [.blockList, .delete]
|
||||||
|
let items: [FSPopoverListItem] = features.map { feature in
|
||||||
|
let item = FSPopoverListTextItem(scrollDirection: .horizontal)
|
||||||
|
item.title = feature.description
|
||||||
|
item.titleFont = UIFont.systemFont(ofSize: 12)
|
||||||
|
item.isSeparatorHidden = false
|
||||||
|
item.titleColor = .white
|
||||||
|
item.contentInset = .init(top: 9, left: 15, bottom: 9, right: 15)
|
||||||
|
item.selectedHandler = { item in
|
||||||
|
guard let item = item as? FSPopoverListTextItem else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print(item.title ?? "")
|
||||||
|
}
|
||||||
|
item.updateLayout()
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
items.last?.isSeparatorHidden = true
|
||||||
|
menuView.shadowOpacity = 0
|
||||||
|
menuView.shadowRadius = 0
|
||||||
|
menuView.shadowColor = .clear
|
||||||
|
menuView.items = items
|
||||||
|
menuView.cornerRadius = 3
|
||||||
|
menuView.transitioningDelegate = nil
|
||||||
|
|
||||||
|
menuView.arrowSize = CGSize.init(width: 18, height: 5)
|
||||||
|
menuView.backgroundColor = .primaryText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func makeUI() {
|
||||||
|
super.makeUI()
|
||||||
|
|
||||||
|
|
||||||
|
tableView.register(FollowingViewCell.self, forCellReuseIdentifier: "FollowingViewCell")
|
||||||
|
emptyDataSetDescription = "还没有人关注你噢!"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel() {
|
||||||
|
super.bindViewModel()
|
||||||
|
|
||||||
|
guard let viewModel = viewModel as? MyFollowersViewModel else { return }
|
||||||
|
|
||||||
|
let input = MyFollowersViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||||
|
headerRefresh: headerRefreshTrigger,
|
||||||
|
footerRefresh: footerRefreshTrigger, modelSelected: tableView.rx.modelSelected(User.self).asDriver())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let output = viewModel.transform(input: input)
|
||||||
|
let dataSource = MyFollowersViewController.dataSource { cell, user in
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
output.modelSelected.drive { [weak self] user in
|
||||||
|
|
||||||
|
let personalViewModel = PersonalViewModel.init(userID: user.id, provider: viewModel.provider)
|
||||||
|
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
|
||||||
|
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MyFollowersViewController {
|
||||||
|
//TODO
|
||||||
|
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, User) -> Void) -> RxTableViewSectionedReloadDataSource<FollowersSection> {
|
||||||
|
return RxTableViewSectionedReloadDataSource<FollowersSection>(
|
||||||
|
configureCell: { dataSource, tableView, indexPath, item in
|
||||||
|
let cell: FollowingViewCell = tableView.dequeueReusableCell(withIdentifier: "FollowingViewCell", for: indexPath) as! FollowingViewCell
|
||||||
|
|
||||||
|
cell.user = item
|
||||||
|
|
||||||
|
|
||||||
|
cell.followingButton.setTitle("回关", for: .normal)
|
||||||
|
cell.followingButton.setTitle("互相关注", for: .selected)
|
||||||
|
if item.relation == .mutualFollowing {
|
||||||
|
cell.followingButton.isSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// if dataSource[indexPath.row].followersType == .followers {
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// cell.followingButton.setTitle("取消", for: .normal)
|
||||||
|
// cell.followingButton.setTitle("添加", for: .selected)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
cell.buttonTapCallback = {audioTrack in
|
||||||
|
buttonTapHandler(cell, audioTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue