Refactor the message module, the focus module

dev
wenlei 11 months ago
parent d51519e4a1
commit 5381371839

@ -25,6 +25,10 @@
770FC3F62B92F4380023DE28 /* AVPlayer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3F22B92F4380023DE28 /* AVPlayer+Rx.swift */; };
770FC3F72B92F4380023DE28 /* AVPlayerItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3F32B92F4380023DE28 /* AVPlayerItem+Rx.swift */; };
770FC3F82B92F4380023DE28 /* AVPlayerLayer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3F42B92F4380023DE28 /* AVPlayerLayer+Rx.swift */; };
770FC3FA2B9319FC0023DE28 /* PrivateMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3F92B9319FC0023DE28 /* PrivateMessageViewController.swift */; };
770FC3FC2B931A370023DE28 /* OtherMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3FB2B931A370023DE28 /* OtherMessageViewController.swift */; };
770FC3FE2B9325B60023DE28 /* MyFollowersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3FD2B9325B60023DE28 /* MyFollowersViewController.swift */; };
770FC4002B9327E20023DE28 /* BlackListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770FC3FF2B9327E20023DE28 /* BlackListViewController.swift */; };
771233EC2B6B6476009FAF01 /* UIButton+IndieMusic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 771233EB2B6B6476009FAF01 /* UIButton+IndieMusic.swift */; };
771233EE2B6B8CE6009FAF01 /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 771233ED2B6B8CE6009FAF01 /* NavigationBar.swift */; };
77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77165D732B464493002AE0A5 /* BarButtonItem.swift */; };
@ -293,6 +297,10 @@
770FC3F22B92F4380023DE28 /* AVPlayer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVPlayer+Rx.swift"; sourceTree = "<group>"; };
770FC3F32B92F4380023DE28 /* AVPlayerItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVPlayerItem+Rx.swift"; sourceTree = "<group>"; };
770FC3F42B92F4380023DE28 /* AVPlayerLayer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVPlayerLayer+Rx.swift"; sourceTree = "<group>"; };
770FC3F92B9319FC0023DE28 /* PrivateMessageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateMessageViewController.swift; sourceTree = "<group>"; };
770FC3FB2B931A370023DE28 /* OtherMessageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherMessageViewController.swift; sourceTree = "<group>"; };
770FC3FD2B9325B60023DE28 /* MyFollowersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyFollowersViewController.swift; sourceTree = "<group>"; };
770FC3FF2B9327E20023DE28 /* BlackListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackListViewController.swift; sourceTree = "<group>"; };
771233EB2B6B6476009FAF01 /* UIButton+IndieMusic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+IndieMusic.swift"; sourceTree = "<group>"; };
771233ED2B6B8CE6009FAF01 /* NavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
77165D732B464493002AE0A5 /* BarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonItem.swift; sourceTree = "<group>"; };
@ -696,6 +704,8 @@
children = (
77C9B9DA2B4BC40F0006C83F /* FollowersViewController.swift */,
77C9B9DC2B4BC5D20006C83F /* FollowersViewModel.swift */,
770FC3FD2B9325B60023DE28 /* MyFollowersViewController.swift */,
770FC3FF2B9327E20023DE28 /* BlackListViewController.swift */,
);
path = Followers;
sourceTree = "<group>";
@ -1148,6 +1158,8 @@
children = (
77FB7A7A2B4A4FC900B64030 /* MessageViewController.swift */,
77FB7A7C2B4A4FD400B64030 /* MessageViewModel.swift */,
770FC3F92B9319FC0023DE28 /* PrivateMessageViewController.swift */,
770FC3FB2B931A370023DE28 /* OtherMessageViewController.swift */,
);
path = Message;
sourceTree = "<group>";
@ -1441,6 +1453,7 @@
77FA0B302B0C4D5A00404C5E /* ShareActionController.swift in Sources */,
778B8AA82AF8ED0E0034AFD4 /* Color.swift in Sources */,
770228EB2B55174D00E07F7A /* InternationalNumberViewModel.swift in Sources */,
770FC3FA2B9319FC0023DE28 /* PrivateMessageViewController.swift in Sources */,
778B8AAE2AF8ED0E0034AFD4 /* TextView.swift in Sources */,
778B8A9B2AF8ECFC0034AFD4 /* AuthManager.swift in Sources */,
778B8AAF2AF8ED0E0034AFD4 /* UIColor+IndieMusic.swift in Sources */,
@ -1495,6 +1508,7 @@
7773EBFA2B8B3D4D0054CFE6 /* CircularIndicatorView.swift in Sources */,
7751D37A2B450BB100F1F2BD /* AgreementViewController.swift in Sources */,
778B8AAB2AF8ED0E0034AFD4 /* UIView+Borders.swift in Sources */,
770FC3FC2B931A370023DE28 /* OtherMessageViewController.swift in Sources */,
778B8AC12AF8ED280034AFD4 /* TableView.swift in Sources */,
778B8A9C2AF8ECFC0034AFD4 /* LogManager.swift in Sources */,
778B8A992AF8ECFC0034AFD4 /* Reachability.swift in Sources */,
@ -1528,6 +1542,7 @@
778B8A862AF8ECE50034AFD4 /* SearchViewModel.swift in Sources */,
77C9B9DD2B4BC5D20006C83F /* FollowersViewModel.swift in Sources */,
778B8A802AF8ECE50034AFD4 /* MineViewController.swift in Sources */,
770FC3FE2B9325B60023DE28 /* MyFollowersViewController.swift in Sources */,
770FC3EF2B92C8130023DE28 /* Array+IndieMusic.swift in Sources */,
778B8A8F2AF8ECF20034AFD4 /* EmptyModel.swift in Sources */,
778B8ABD2AF8ED280034AFD4 /* View.swift in Sources */,
@ -1603,6 +1618,7 @@
77A60D862B5B97C300D4E435 /* CachingAVURLAsset.swift in Sources */,
778B8A9A2AF8ECFC0034AFD4 /* AudioManager.swift in Sources */,
774A180E2B07000C00F56DF1 /* JournalDetailView.swift in Sources */,
770FC4002B9327E20023DE28 /* BlackListViewController.swift in Sources */,
77AC35712B6CE8E200D046C2 /* ShareCardViewController.swift in Sources */,
778B8A842AF8ECE50034AFD4 /* HomeTabBarController.swift in Sources */,
77DFA9C52B4E8388005B8B13 /* MineDownloadViewController.swift in Sources */,

@ -101,44 +101,28 @@ class NavigationController: UINavigationController {
extension NavigationController {
override func pushViewController(_ viewController: UIViewController, animated: Bool)
{
guard let tabbar = self.tabBarController as? HomeTabBarController else { return }
if !children.isEmpty {
// viewController.hidesBottomBarWhenPushed = true
// if children.count > 1 {
// viewController.hidesBottomBarWhenPushed = false
// }
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(false, animated: true)
if viewController is JournalDetailController {
let isShowPlayBar = AudioManager.sharedInstance.currentTrack.value != nil
tabbar.toggleBars(showTabBar: false, showPlayerBar: isShowPlayBar, animated: true)
} else if viewController is LoginViewController {
AudioManager.sharedInstance.pause()
tabbar.toggleBars(showTabBar: false, showPlayerBar: false, animated: true)
} else if viewController is CommentViewController {
tabbar.toggleBars(showTabBar: false, showPlayerBar: false, animated: true)
} else {
tabbar.toggleBars(showTabBar: false, showPlayerBar: true, animated: true)
}
// viewController.hidesBottomBarWhenPushed = true
// let leftButton = UIButton.init()
// leftButton.setTitle("", for: .normal)
// leftButton.setImage(UIImage.init(named: "nav_back_btn"), for: .normal)
// leftButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
//
// let navLeftButton = UIBarButtonItem.init(customView: leftButton)
// navLeftButton.accessibilityLabel = ""
// viewController.navigationItem.leftBarButtonItem = navLeftButton
//
// let leftBarBtn = UIBarButtonItem(title: "", style: .plain, target: self,action: #selector(backAction))
// leftBarBtn.image = UIImage(named: "nav_back_icon")
// self.navigationItem.leftBarButtonItem = leftBarBtn
// let button = UIButton.init()
// button.setImage(UIImage(named:"nav_back_btn"), for: .normal)
//// button.setTitle("1", for: .normal)
// button.addTarget(self, action: #selector(backAction), for: .touchUpInside)
//
// let leftBarBtn = UIBarButtonItem(customView: button)
//
// //
// let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil,
// action: nil)
// spacer.width = -10
//
// self.navigationItem.leftBarButtonItem = leftBarBtn
}
super.pushViewController(viewController, animated: animated)
}

@ -62,6 +62,8 @@ struct BaseDimensions {
static let bottomHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height > 20.0 ? 34.0 : 0
/// tab
static let tabBarHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height > 20.0 ? 83.0 : 49.0
/// Bar
static let playBarHeight: CGFloat = 66
static let inset: CGFloat = 8
static let borderWidth: CGFloat = 1

@ -221,6 +221,8 @@ class AudioManager {
player.play()
NotificationCenter.default.post(name: .notiPlayResume, object: nil)
} else if let track = playlist.value.first {
playTrack(track: track)
}
}

@ -8,13 +8,10 @@
import Foundation
import RxDataSources
enum FollowersType: Codable {
case followers
case blackList
}
struct FollowersSection {
var followersType: FollowersType
// var followersType: FollowersType
var items: [User]
}

@ -18,6 +18,18 @@ enum CustomMessageType: Codable {
case like(String)
case follow(String)
var content: String {
switch self {
case .comment(let string):
return string
case .like(let string):
return string
case .follow(let string):
return string
}
}
var description: String {
switch self {
case .comment:
@ -55,9 +67,9 @@ enum CustomMessageType: Codable {
struct MessageList: Codable {
let comment: Message
let follow: Message
let thumbup: Message
let comment: Message?
let follow: Message?
let thumbup: Message?
}

@ -133,11 +133,16 @@ class HomeTabBarController: UITabBarController, Navigatable {
}
playerTabBar.snp.makeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top).offset(BaseDimensions.tabBarHeight)
make.left.right.equalTo(view)
make.bottom.equalTo(view)
if !self.customeTabBar.isHidden {
make.height.equalTo(BaseDimensions.bottomHeight + BaseDimensions.tabBarHeight + BaseDimensions.playBarHeight)
} else {
make.height.equalTo(BaseDimensions.bottomHeight + BaseDimensions.playBarHeight)
}
}
}
override func viewDidLayoutSubviews() {
@ -275,7 +280,6 @@ class HomeTabBarController: UITabBarController, Navigatable {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
@ -283,180 +287,55 @@ class HomeTabBarController: UITabBarController, Navigatable {
}
func showAllBar(_ isShowing: Bool, animated: Bool) {
func toggleBars(showTabBar: Bool, showPlayerBar: Bool, animated: Bool) {
if isAnimating { return }
isAnimating = true
if isShowing {
customeTabBar.isHidden = false
if AudioManager.sharedInstance.currentTrack != nil {
playerTabBar.isHidden = false
}
if showTabBar {
self.customeTabBar.isHidden = false
customeTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.left.right.equalTo(view)
make.bottom.equalTo(view)
make.height.equalTo(BaseDimensions.tabBarHeight)
}
playerTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top)
}
} else {
customeTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(view).offset(self.tabBar.bounds.height)
make.left.right.equalTo(view)
make.bottom.equalTo(view).offset(BaseDimensions.tabBarHeight)
make.height.equalTo(BaseDimensions.tabBarHeight)
}
playerTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top).offset(BaseDimensions.tabBarHeight)
}
}
var animationDuration = 0.3
if animated == false {
animationDuration = 0
}
UIView.animate(withDuration: animationDuration) {
if isShowing {
self.customeTabBar.alpha = 1
self.playerTabBar.alpha = 1
} else {
self.customeTabBar.alpha = 0
self.playerTabBar.alpha = 0
}
self.view.layoutIfNeeded()
} completion: { completed in
self.isTabBarShowing = isShowing
self.isPlayerBarShowing = isShowing
self.customeTabBar.isHidden = !isShowing
if AudioManager.sharedInstance.currentTrack != nil {
self.playerTabBar.isHidden = !isShowing
}
self.isAnimating = false
}
}
open func showTabBar(_ isShowing: Bool, animated: Bool) {
if isAnimating { return }
isAnimating = true
if isShowing {
customeTabBar.isHidden = false
customeTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
if showPlayerBar {
playerTabBar.snp.remakeConstraints { make in
make.left.right.equalTo(view)
make.bottom.equalTo(view)
make.height.equalTo(BaseDimensions.tabBarHeight)
if showTabBar {
make.height.equalTo( BaseDimensions.tabBarHeight + BaseDimensions.playBarHeight)
} else {
make.height.equalTo(BaseDimensions.bottomHeight + BaseDimensions.playBarHeight)
}
}
} else {
customeTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(view).offset(self.tabBar.bounds.height)
make.height.equalTo(BaseDimensions.tabBarHeight)
playerTabBar.snp.remakeConstraints { make in
make.left.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top).offset(BaseDimensions.tabBarHeight)
}
}
var animationDuration = 0.3
if animated == false {
animationDuration = 0
}
UIView.animate(withDuration: animationDuration,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.25,
options: .curveEaseInOut,
animations: {
if isShowing {
self.customeTabBar.alpha = 1
} else {
self.customeTabBar.alpha = 0
}
self.view.layoutIfNeeded()
}) { (completed) in
self.isTabBarShowing = isShowing
self.customeTabBar.isHidden = !isShowing
self.isAnimating = false
}
}
open func showPlayerBar(_ isShowing: Bool, animated: Bool) {
if isAnimating { return }
isAnimating = true
if isShowing {
playerTabBar.isHidden = false
playerTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top)
}
} else {
playerTabBar.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(customeTabBar.snp.top).offset(BaseDimensions.tabBarHeight)
}
let animationDuration = animated ? 0.35 : 0
}
var animationDuration = 0.3
if animated == false {
animationDuration = 0
}
UIView.animate(withDuration: animationDuration, animations: {
UIView.animate(withDuration: animationDuration,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.25,
options: .curveEaseInOut,
animations: {
if isShowing {
self.playerTabBar.alpha = 1
} else {
self.playerTabBar.alpha = 0
}
self.view.layoutIfNeeded()
}) { (completed) in
self.isPlayerBarShowing = isShowing
self.playerTabBar.isHidden = !isShowing
self.view.layoutIfNeeded()
}) { completed in
self.isTabBarShowing = showTabBar
self.isPlayerBarShowing = showPlayerBar
self.customeTabBar.isHidden = !showTabBar
self.playerTabBar.isHidden = !showPlayerBar
self.isAnimating = false
}
}

@ -53,12 +53,19 @@ class HomeViewController: TableViewController, ScrollableNavBar {
self.navigationItem.leftBarButtonItem = nil
if let tabbar = self.tabBarController as? HomeTabBarController {
if tabbar.customeTabBar.isHidden == true && tabbar.playerTabBar.isHidden == true {
tabbar.showAllBar(true, animated: true)
} else if tabbar.playerTabBar.isHidden == true {
tabbar.playerTabBar.audioTrack = AudioManager.sharedInstance.currentTrack.value
tabbar.showPlayerBar(true, animated: true)
}
// if tabbar.customeTabBar.isHidden == true && tabbar.playerTabBar.isHidden == true {
// tabbar.showAllBar(true, animated: true)
// } else if tabbar.playerTabBar.isHidden == true {
//
// tabbar.showPlayerBar(true, animated: true)
// }
let isShowPlayBar = AudioManager.sharedInstance.currentTrack.value != nil
tabbar.playerTabBar.audioTrack = AudioManager.sharedInstance.currentTrack.value
tabbar.toggleBars(showTabBar: true, showPlayerBar: isShowPlayBar, animated: true)
}
if let navigationBar = self.navigationController?.navigationBar as? NavigationBar {

@ -308,6 +308,15 @@ class JournalDetailController: TableViewController {
}.disposed(by: rx.disposeBag)
AudioManager.sharedInstance.playlist.subscribe { audioTracks in
let isShow = audioTracks.element?.isEmpty == false
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.toggleBars(showTabBar: false, showPlayerBar: isShow, animated: true)
}
}.disposed(by: rx.disposeBag)
}
@ -316,13 +325,19 @@ class JournalDetailController: TableViewController {
super.viewDidLayoutSubviews()
commentToolView.snp.makeConstraints { make in
commentToolView.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(view)
if AudioManager.sharedInstance.playlist.value.isEmpty == false {
make.bottom.equalTo(view).offset(-BaseDimensions.playBarHeight - 10)
} else {
make.bottom.equalTo(view)
}
make.height.equalTo(BaseDimensions.bottomHeight + 48)
}
tableView.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)

@ -1076,11 +1076,33 @@ class JournalAudioCollectionViewCell: UICollectionViewCell {
coverView.kf.setImage(with: URL.init(string: audioTrack.pic ?? ""))
musicIndicator.state = AudioManager.sharedInstance.isPlaying() ? .playing : .paused
if AudioManager.sharedInstance.currentTrack.value?.id == audioTrack.id {
musicIndicator.state = AudioManager.sharedInstance.isPlaying() ? .playing : .paused
} else {
self.musicIndicator.state = .stopped
}
AudioManager.sharedInstance.isPlayingState.subscribe { [weak self] isPlaying in
if AudioManager.sharedInstance.currentTrack.value?.id == audioTrack.id {
self?.musicIndicator.state = AudioManager.sharedInstance.isPlaying() ? .playing : .paused
} else {
self?.musicIndicator.state = .stopped
}
}.disposed(by: disposeBag)
}
}
var disposeBag = DisposeBag()
//
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
override init(frame: CGRect) {
super.init(frame: frame)

@ -12,6 +12,11 @@ import RxDataSources
import FSPopoverView
class MessageViewController: ViewController, UIScrollViewDelegate {
var pageViewController: UIPageViewController!
var pages = [UIViewController]()
let currentPageIndex = BehaviorRelay<Int>(value: 0)
let messageTopView: MessageTopView = {
let messageTopView = MessageTopView.init()
@ -19,59 +24,6 @@ class MessageViewController: ViewController, UIScrollViewDelegate {
return messageTopView
}()
var tableView: UITableView = {
let tableView = UITableView.init()
tableView.register(MessageCellView.self, forCellReuseIdentifier: "MessageCellView")
tableView.register(MessageActivitiesViewCell.self, forCellReuseIdentifier: "MessageActivitiesViewCell")
return tableView
}()
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
}()
let headerRefreshTrigger = PublishSubject<Void>()
let footerRefreshTrigger = PublishSubject<Void>()
let isHeaderLoading = BehaviorRelay(value: false)
let isFooterLoading = BehaviorRelay(value: false)
let messageType = BehaviorRelay<MessageType>.init(value: .message)
override func viewDidLoad() {
super.viewDidLoad()
@ -81,21 +33,39 @@ class MessageViewController: ViewController, UIScrollViewDelegate {
override func makeUI() {
super.makeUI()
self.navigationItem.title = "消息列表"
messageTopView.segmentControl.titleBtnOnClick = { [weak self] (label, index) in
if index == 0 {
self?.messageType.accept(.message)
self?.goToNextPage()
} else {
self?.messageType.accept(.activities)
self?.goToPreviousPage()
}
}
self.navigationItem.title = "消息列表"
tableView.rx.setDelegate(self).disposed(by: rx.disposeBag)
guard let viewModel = viewModel as? MessageViewModel else { return }
let privateMessageViewModel = PrivateMessageViewModel.init(provider: viewModel.provider)
let privateMessage = PrivateMessageViewController.init(viewModel: privateMessageViewModel, navigator: self.navigator)
let otherMessageViewModel = OtherMessageViewModel.init(provider: viewModel.provider)
let otherMessage = OtherMessageViewController.init(viewModel: otherMessageViewModel, navigator: self.navigator)
pages.append(privateMessage)
pages.append(otherMessage)
setupPageViewController()
view.addSubview(messageTopView)
view.addSubview(tableView)
view.backgroundColor = .white
}
override func bindViewModel() {
@ -103,40 +73,26 @@ class MessageViewController: ViewController, UIScrollViewDelegate {
// self.segmentControl.currentSearchType = .single
guard let viewModel = viewModel as? MessageViewModel else { return }
let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
let input = MessageViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: refresh,
footerRefresh: footerRefreshTrigger,
messageType: messageType,
modelSelected: tableView.rx.modelSelected(MessageSectionItem.self).asDriver())
let input = MessageViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
let output = viewModel.transform(input: input)
let dataSource = MessageViewController.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
pageViewController.rx.didFinishAnimating.subscribe { [weak self] (isFinished, previousViewControllers, completed) in
if let privateMessage = self?.pageViewController.viewControllers?.first as? PrivateMessageViewController {
self?.messageTopView.segmentControl.selectedIndex(0, animated: true)
} else {
self?.messageTopView.segmentControl.selectedIndex(1, animated: true)
}
}.disposed(by: rx.disposeBag)
}.disposed(by: rx.disposeBag)
}
@ -146,22 +102,19 @@ class MessageViewController: ViewController, UIScrollViewDelegate {
messageTopView.snp.makeConstraints { make in
make.top.equalTo(view)
make.left.equalTo(view)
make.right.equalTo(view)
make.height.equalTo(49)
make.top.equalTo(view)
}
tableView.snp.makeConstraints { make in
make.top.equalTo(messageTopView.snp.bottom)
pageViewController.view.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(messageTopView.snp.bottom)
make.bottom.equalTo(view)
}
}
func navigateToScreen(customMessageType: CustomMessageType) {
guard let viewModel = viewModel as? MessageViewModel else { return }
@ -179,7 +132,36 @@ class MessageViewController: ViewController, UIScrollViewDelegate {
}
private func setupPageViewController() {
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = self
if let firstViewController = pages.first {
pageViewController.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
addChild(pageViewController)
view.addSubview(pageViewController.view)
pageViewController.didMove(toParent: self)
}
func goToNextPage() {
guard let currentViewController = pageViewController?.viewControllers?.first,
let nextPage = pages.first else {
return
}
pageViewController?.setViewControllers([nextPage], direction: .reverse, animated: true, completion: nil)
}
func goToPreviousPage() {
guard let currentViewController = pageViewController?.viewControllers?.first,
let previousPage = pages.last else {
return
}
pageViewController?.setViewControllers([previousPage], direction: .forward, animated: true, completion: nil)
}
}
@ -221,6 +203,47 @@ extension MessageViewController {
}
extension MessageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard pages.count > previousIndex else {
return nil
}
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let pagesCount = pages.count
guard pagesCount != nextIndex else {
return nil
}
guard pagesCount > nextIndex else {
return nil
}
return pages[nextIndex]
}
}
class MessageTopView: UIView {
@ -282,13 +305,15 @@ class MessageTopView: UIView {
segmentControl.snp.makeConstraints { make in
make.width.equalTo(185)
make.height.equalTo(44)
make.center.equalTo(self)
make.top.equalTo(self)
make.centerX.equalTo(self)
}
lineView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.bottom.equalTo(self)
make.top.equalTo(segmentControl.snp.bottom)
make.height.equalTo(6)
}
@ -370,7 +395,13 @@ class MessageCellView: UITableViewCell {
avatarView.image = UIImage.init(named: customMessageType.image)
nameLabel.text = customMessageType.description
detailLabel.text = customMessageType.detail
if customMessageType.content == "" {
detailLabel.text = "暂无\(customMessageType.description)"
} else {
detailLabel.text = customMessageType.detail
}
}
}

@ -14,20 +14,10 @@ class MessageViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let messageType: BehaviorRelay<MessageType>
let modelSelected: Driver<MessageSectionItem>
// let messageType: BehaviorRelay<MessageType>
}
struct Output {
let messageItems: PublishRelay<[MessageSection]>
let activitiesItems: PublishRelay<[MessageSection]>
let items: BehaviorRelay<[MessageSection]>
let selection: PublishSubject<MessageSectionItem>
let modelSelected: Driver<MessageSectionItem>
}
@ -86,71 +76,63 @@ class MessageViewModel: ViewModel, ViewModelType {
// self.items.accept([MessageSection.activities(items: new)]) }
// }).disposed(by: rx.disposeBag)
input.messageType.subscribe { [weak self] messageType in
guard let self = self, let messageType = messageType.element else { return }
self.messageType.accept(messageType)
print("messageType \(messageType)")
self.page = 1
switch messageType {
case .message:
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.messageItems.accept([MessageSection.message(items: [comment, thumbup, follow])])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
case .activities: break
}
}.disposed(by: rx.disposeBag)
let cuttentItems = Observable.merge(messageItems.asObservable(), activitiesItems.asObservable())
cuttentItems.subscribe { [weak self] followersSections in
guard let self = self else { return }
// input.messageType.subscribe { [weak self] messageType in
// guard let self = self, let messageType = messageType.element else { return }
// self.messageType.accept(messageType)
//
// print("messageType \(messageType)")
// self.page = 1
// switch messageType {
// case .message:
//
// 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.messageItems.accept([MessageSection.message(items: [comment, thumbup, follow])])
// } onError: { error in
//
// }.disposed(by: self.rx.disposeBag)
//
// case .activities: break
// }
//
//
//
// }.disposed(by: rx.disposeBag)
if self.messageType.value == .activities {
} else {
self.items.accept(followersSections)
}
}.disposed(by: rx.disposeBag)
// let cuttentItems = Observable.merge(messageItems.asObservable(), activitiesItems.asObservable())
//
// cuttentItems.subscribe { [weak self] followersSections in
// guard let self = self else { return }
//
// if self.messageType.value == .activities {
//
// } else {
// self.items.accept(followersSections)
// }
//
// }.disposed(by: rx.disposeBag)
return Output.init(messageItems: messageItems,
activitiesItems: activitiesItems,
items: items,
selection: selection,
modelSelected: input.modelSelected)
return Output.init()
}
func requestMessageList() -> Observable<MessageList> {
return self.provider.messageList()
.trackActivity(loading)
.trackError(error)
}
// func requestActivitiesList(page: Int, size: Int) -> Observable<[Message]> {
// return self.provider.messageList(page: page, size: size)

@ -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
}
}
)
}
}

@ -42,7 +42,7 @@ class MineViewController: TableViewController {
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(true, animated: true)
tabbar.toggleBars(showTabBar: true, showPlayerBar: true, animated: true)
}
if AudioManager.sharedInstance.playlist.value.count > 0 {

@ -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
}
)
}
}

@ -11,6 +11,11 @@ import RxCocoa
import RxDataSources
class FollowersViewController: ViewController, UIScrollViewDelegate {
var pageViewController: UIPageViewController!
var pages = [UIViewController]()
let currentPageIndex = BehaviorRelay<Int>(value: 0)
let followersTopView: FollowersTopView = {
@ -18,21 +23,6 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
return followersTopView
}()
var tableView: UITableView = {
let tableView = UITableView.init()
tableView.register(FollowingViewCell.self, forCellReuseIdentifier: "FollowingViewCell")
return tableView
}()
let followersType = BehaviorRelay<FollowersType>.init(value: .followers)
let headerRefreshTrigger = PublishSubject<Void>()
let footerRefreshTrigger = PublishSubject<Void>()
// let isHeaderLoading = BehaviorRelay<(RefreshType, Bool)>.init(value: (.refresh, false))
// let isFooterLoading = BehaviorRelay<(RefreshType, Bool)>.init(value: (.loadMore, false))
override func viewDidLoad() {
super.viewDidLoad()
@ -42,21 +32,36 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
override func makeUI() {
super.makeUI()
self.navigationItem.title = "粉丝"
followersTopView.segmentControl.titleBtnOnClick = { [weak self] (label, index) in
if index == 0 {
self?.followersType.accept(.followers)
self?.goToNextPage()
} else {
self?.followersType.accept(.blackList)
self?.goToPreviousPage()
}
}
self.navigationItem.title = "粉丝"
tableView.rx.setDelegate(self).disposed(by: rx.disposeBag)
guard let viewModel = viewModel as? FollowersViewModel else { return }
let myFollowersViewModel = MyFollowersViewModel.init(provider: viewModel.provider)
let myFollowers = MyFollowersViewController.init(viewModel: myFollowersViewModel, navigator: self.navigator)
let blackListViewModel = BlackListViewModel.init(provider: viewModel.provider)
let blackList = BlackListViewController.init(viewModel: blackListViewModel, navigator: self.navigator)
pages.append(myFollowers)
pages.append(blackList)
setupPageViewController()
view.addSubview(followersTopView)
view.addSubview(tableView)
view.backgroundColor = .white
}
@ -66,33 +71,24 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
guard let viewModel = viewModel as? FollowersViewModel else { return }
let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
let input = FollowersViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: refresh,
footerRefresh: footerRefreshTrigger,
followersType: self.followersType,
modelSelected: tableView.rx.modelSelected(User.self).asDriver())
let input = FollowersViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
let output = viewModel.transform(input: input)
let dataSource = FollowersViewController.dataSource { [weak self] cell, user in
viewModel.itemSelected.onNext(user)
}
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.modelSelected.drive { [weak self] user in
pageViewController.rx.didFinishAnimating.subscribe { [weak self] (isFinished, previousViewControllers, completed) in
let personalViewModel = PersonalViewModel.init(userID: user.id, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
if let myFollowers = self?.pageViewController.viewControllers?.first as? MyFollowersViewController {
self?.followersTopView.segmentControl.selectedIndex(0, animated: true)
} else {
self?.followersTopView.segmentControl.selectedIndex(1, animated: true)
}
}.disposed(by: rx.disposeBag)
}
@ -108,16 +104,45 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
}
tableView.snp.makeConstraints { make in
make.top.equalTo(followersTopView.snp.bottom)
pageViewController.view.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(followersTopView.snp.bottom)
make.bottom.equalTo(view)
}
}
private func setupPageViewController() {
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = self
if let firstViewController = pages.first {
pageViewController.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
addChild(pageViewController)
view.addSubview(pageViewController.view)
pageViewController.didMove(toParent: self)
}
func goToNextPage() {
guard let currentViewController = pageViewController?.viewControllers?.first,
let nextPage = pages.first else {
return
}
pageViewController?.setViewControllers([nextPage], direction: .reverse, animated: true, completion: nil)
}
func goToPreviousPage() {
guard let currentViewController = pageViewController?.viewControllers?.first,
let previousPage = pages.last else {
return
}
pageViewController?.setViewControllers([previousPage], direction: .forward, animated: true, completion: nil)
}
}
@ -131,18 +156,9 @@ extension FollowersViewController {
cell.user = item
if dataSource[indexPath.row].followersType == .followers {
cell.followingButton.setTitle("取消", for: .normal)
cell.followingButton.setTitle("添加", for: .selected)
cell.followingButton.setTitle("回关", for: .normal)
cell.followingButton.setTitle("互相关注", for: .selected)
if item.relation == .mutualFollowing {
cell.followingButton.isSelected = true
}
} else {
cell.followingButton.setTitle("取消", for: .normal)
cell.followingButton.setTitle("添加", for: .selected)
}
cell.buttonTapCallback = {audioTrack in
@ -155,6 +171,49 @@ extension FollowersViewController {
}
}
extension FollowersViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard pages.count > previousIndex else {
return nil
}
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let pagesCount = pages.count
guard pagesCount != nextIndex else {
return nil
}
guard pagesCount > nextIndex else {
return nil
}
return pages[nextIndex]
}
}
class FollowersTopView: UIView {
lazy var segmentControl: ScrollSegmentView = {

@ -13,30 +13,14 @@ class FollowersViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let followersType: BehaviorRelay<FollowersType>
let modelSelected: Driver<User>
}
struct Output {
let followersItems: BehaviorRelay<[FollowersSection]>
let blocklistItems: BehaviorRelay<[FollowersSection]>
let items: BehaviorRelay<[FollowersSection]>
let modelSelected: Driver<User>
}
let followersItems = BehaviorRelay<[FollowersSection]>.init(value: [])
let blocklistItems = BehaviorRelay<[FollowersSection]>.init(value: [])
let items = BehaviorRelay<[FollowersSection]>.init(value: [])
let itemSelected = PublishSubject<User>()
let followersType = BehaviorRelay<FollowersType>.init(value: .followers)
func transform(input: Input) -> Output {
@ -44,29 +28,24 @@ class FollowersViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
input.followersType.subscribe { [weak self] followersType in
guard let self = self else { return }
self.followersType.accept(followersType)
}.disposed(by: rx.disposeBag)
input.headerRefresh.withLatestFrom(input.followersType)
.flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
guard let self = self else { return Observable.just([]) }
self.page = 1
if followersType == .followers {
return self.requestFollowersList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
.trackActivity(self.headerLoading)
} else {
return self.requestBlockList(page: self.page, size: 10)
.trackActivity(self.headerLoading)
}
})
.subscribe(onNext: { (items) in
self.items.accept([FollowersSection.init(followersType: self.followersType.value, items: items)])
}).disposed(by: rx.disposeBag)
// input.headerRefresh.withLatestFrom(input.followersType)
// .flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
// guard let self = self else { return Observable.just([]) }
// self.page = 1
//
// if followersType == .followers {
// return self.requestFollowersList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
// .trackActivity(self.headerLoading)
// } else {
// return self.requestBlockList(page: self.page, size: 10)
// .trackActivity(self.headerLoading)
// }
//
// })
// .subscribe(onNext: { (items) in
// self.items.accept([FollowersSection.init(followersType: self.followersType.value, items: items)])
// }).disposed(by: rx.disposeBag)
// input.footerRefresh.withLatestFrom(input.followersType)
// .flatMapLatest({ [weak self] (followersType) -> Observable<[User]> in
@ -202,18 +181,10 @@ class FollowersViewModel: ViewModel, ViewModelType {
return Output.init(followersItems: followersItems,
blocklistItems: blocklistItems,
items: self.items,
modelSelected: input.modelSelected)
return Output.init()
}
func requestFollowersList(userId: String, page: Int, size: Int) -> Observable<[User]> {
return self.provider.followerList(userId: userId, page: page, size: size)
.trackActivity(loading)
.trackError(error)
}
func requestBlockList(page: Int, size: Int) -> Observable<[User]> {

@ -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
}
)
}
}

@ -10,9 +10,9 @@ import MarqueeLabel
class PlayerTabBar: UIControl {
var blurEffectView: BlurEffectView = {
let blurEffectView = BlurEffectView.init()
blurEffectView.imageView.isHidden = true
let blurEffectView = BlurEffectView.init(style: .systemMaterial, frame: .zero)
// blurEffectView.isHidden = true
blurEffectView.backgroundColor = .white
return blurEffectView
}()
@ -27,7 +27,6 @@ class PlayerTabBar: UIControl {
private var containerView: UIView = {
var containerView = UIView.init()
containerView.backgroundColor = .white
return containerView
}()
@ -85,10 +84,6 @@ class PlayerTabBar: UIControl {
super.init(frame: frame)
makeUI()
// titleLabel.text = ""
// artistLabel.text = ""
// progressView.progress = 0
}
required init?(coder: NSCoder) {
@ -97,8 +92,9 @@ class PlayerTabBar: UIControl {
func makeUI() {
// backgroundColor = .white
addSubview(blurEffectView)
containerView.addSubview(titleLabel)
containerView.addSubview(artistLabel)
@ -113,17 +109,24 @@ class PlayerTabBar: UIControl {
override func layoutSubviews() {
super.layoutSubviews()
coverView.snp.remakeConstraints { make in
blurEffectView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.bottom.equalTo(self)
make.top.equalTo(self).offset(12)
}
coverView.snp.makeConstraints { make in
make.left.equalTo(self).offset(30)
make.top.equalTo(self).offset(0)
make.size.equalTo(CGSize.init(width: 52, height: 52))
make.bottom.equalTo(self).offset(-14)
}
containerView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.bottom.equalTo(self)
make.top.equalTo(self).offset(12)
make.height.equalTo(54)
}
@ -154,7 +157,7 @@ class PlayerTabBar: UIControl {
progressView.snp.makeConstraints { make in
make.left.equalTo(self).offset(30)
make.right.equalTo(self).offset(-30)
make.bottom.equalTo(self)
make.top.equalTo(containerView.snp.bottom)
make.height.equalTo(2)
}

@ -101,8 +101,8 @@ class PlayerControlView: UIView {
var playButton: UIButton = {
let playButton = UIButton.init()
playButton.setImage(UIImage.init(named: "play_pause_btn"), for: .normal)
playButton.setImage(UIImage.init(named: "play_play_btn"), for: .selected)
playButton.setImage(UIImage.init(named: "play_pause_btn"), for: .selected)
playButton.setImage(UIImage.init(named: "play_play_btn"), for: .normal)
return playButton
}()

@ -129,14 +129,18 @@ class PlayerViewController: ViewController {
viewModel.isPlaying
.bind(to: self.playerControlView.playButton.rx.isSelected)
.disposed(by: rx.disposeBag)
// viewModel.isPlaying
// .bind(to: self.playerControlView.playButton.rx.isSelected)
// .disposed(by: rx.disposeBag)
//
// viewModel.isPlaying.subscribe { [weak self] isPlaying in
// self?.playerControlView.playButton.isSelected = isPlaying
//
// } .disposed(by: rx.disposeBag)
//
viewModel.isPlaying.subscribe { [weak self] isPlaying in
self?.playerControlView.playButton.isSelected = isPlaying
AudioManager.sharedInstance.isPlayingState.bind(to: self.playerControlView.playButton.rx.isSelected).disposed(by: rx.disposeBag)
} .disposed(by: rx.disposeBag)
output.currentAudioTrack.subscribe { [weak self] audioTrack in
@ -162,25 +166,6 @@ class PlayerViewController: ViewController {
}.disposed(by: rx.disposeBag)
// output.progress.subscribe { [weak self] progress in
// guard let self = self else { return }
//
// if !self.playerScrollView.playerInfoView.playerSlider.isDrop {
// self.playerScrollView.playerInfoView.playerSlider.slider.value = progress
// }
// }.disposed(by: rx.disposeBag)
//
// output.duration.subscribe { [weak self] duration in
// guard let self = self else { return }
//
// self.playerScrollView.playerInfoView.playerSlider.totalSec = duration
// }.disposed(by: rx.disposeBag)
//
// output.time.subscribe { [weak self] time in
// guard let self = self else { return }
//
// self.playerScrollView.playerInfoView.playerSlider.updateTimeLabel(currentTime: time)
// }.disposed(by: rx.disposeBag)
output.isPlaying.bind(to: self.playerControlView.playButton.rx.isSelected).disposed(by: rx.disposeBag)

@ -188,19 +188,6 @@ class PlayerViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
input.notiPlayPause.subscribe { [weak self] noti in
guard let self = self else { return }
self.isPlaying.accept(true)
}.disposed(by: rx.disposeBag)
input.notiPlayResume.subscribe { [weak self] noti in
guard let self = self else { return }
self.isPlaying.accept(false)
}.disposed(by: rx.disposeBag)
return Output.init(items: items,
shuffle: shuffle,

@ -185,7 +185,6 @@ class SongResultsViewController: CollectionViewController {
return singleColumnSection
}
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.register(JournalAudioCollectionViewCell.self, forCellWithReuseIdentifier: "JournalAudioCollectionViewCell")
collectionView.collectionViewLayout = layout

@ -53,7 +53,7 @@ class SearchViewController: ViewController, UIScrollViewDelegate {
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(true, animated: true)
tabbar.toggleBars(showTabBar: true, showPlayerBar: true, animated: true)
}
self.navigationController?.setNavigationBarHidden(true, animated: false)

Loading…
Cancel
Save