Update joural detail module

dev
wenlei 11 months ago
parent 106f56637e
commit b62de39551

@ -186,6 +186,7 @@
77C9B9EF2B4C2A910006C83F /* AudioTrackListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9EE2B4C2A910006C83F /* AudioTrackListViewController.swift */; };
77C9B9F12B4C2B3A0006C83F /* AudioTrackListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9F02B4C2B3A0006C83F /* AudioTrackListViewModel.swift */; };
77CEFEFC2B81EC600071B671 /* PresentAndDismissTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEFEFB2B81EC600071B671 /* PresentAndDismissTransition.swift */; };
77CEFEFE2B82F18A0071B671 /* CommentToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEFEFD2B82F18A0071B671 /* CommentToolView.swift */; };
77DFA9C52B4E8388005B8B13 /* MineDownloadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77DFA9C42B4E8388005B8B13 /* MineDownloadViewController.swift */; };
77DFA9C72B4E83B0005B8B13 /* MineDownloadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77DFA9C62B4E83B0005B8B13 /* MineDownloadViewModel.swift */; };
77FA0B282B0B3E1E00404C5E /* Journal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FA0B272B0B3E1E00404C5E /* Journal.swift */; };
@ -436,6 +437,7 @@
77C9B9EE2B4C2A910006C83F /* AudioTrackListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioTrackListViewController.swift; sourceTree = "<group>"; };
77C9B9F02B4C2B3A0006C83F /* AudioTrackListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioTrackListViewModel.swift; sourceTree = "<group>"; };
77CEFEFB2B81EC600071B671 /* PresentAndDismissTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentAndDismissTransition.swift; sourceTree = "<group>"; };
77CEFEFD2B82F18A0071B671 /* CommentToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentToolView.swift; sourceTree = "<group>"; };
77DFA9C42B4E8388005B8B13 /* MineDownloadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MineDownloadViewController.swift; sourceTree = "<group>"; };
77DFA9C62B4E83B0005B8B13 /* MineDownloadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MineDownloadViewModel.swift; sourceTree = "<group>"; };
77FA0B272B0B3E1E00404C5E /* Journal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Journal.swift; sourceTree = "<group>"; };
@ -732,6 +734,7 @@
77CEFEFB2B81EC600071B671 /* PresentAndDismissTransition.swift */,
770228F02B57AD2C00E07F7A /* RefreshLoadingView.swift */,
775D075D2B5E5BCA009270D3 /* GradientLayerLabel.swift */,
77CEFEFD2B82F18A0071B671 /* CommentToolView.swift */,
);
path = Common;
sourceTree = "<group>";
@ -1503,6 +1506,7 @@
77AC35732B6E1ACE00D046C2 /* ShareCardViewModel.swift in Sources */,
77FA0B3E2B0C573600404C5E /* Filter.swift in Sources */,
77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */,
77CEFEFE2B82F18A0071B671 /* CommentToolView.swift in Sources */,
7751D3662B42BE7F00F1F2BD /* FeedbackViewController.swift in Sources */,
77A60D822B5B97A100D4E435 /* AssetDataManager.swift in Sources */,
77FB7A7B2B4A4FC900B64030 /* MessageViewController.swift in Sources */,

@ -0,0 +1,132 @@
//
// CommentToolView.swift
// IndieMusic
//
// Created by WenLei on 2024/2/19.
//
import UIKit
class CommentToolView: UIView {
let commentCountButton: CommentCountButton = {
let commentCountButton = CommentCountButton.init()
commentCountButton.setContentHuggingPriority(.required, for: .horizontal)
commentCountButton.setContentCompressionResistancePriority(.required, for: .horizontal)
return commentCountButton
}()
let containerView: UIView = {
let containerView = UIView.init()
containerView.layer.cornerRadius = 19
containerView.backgroundColor = .init(hex: 0x000000, alpha: 0.05)
return containerView
}()
let textField: UITextField = {
let textField = UITextField.init()
textField.font = UIFont.systemFont(ofSize: 15)
textField.attributedPlaceholder = NSAttributedString(
string: "说点想说的",
attributes: [NSAttributedString.Key.foregroundColor: UIColor.secondaryText()]
)
textField.returnKeyType = .send
return textField
}()
var lineView: UIView = {
let lineView = UIView.init()
lineView.backgroundColor = .separator()
return lineView
}()
var isShowButton: Bool = true
init(isShowButton: Bool = true, frame: CGRect) {
super.init(frame: frame)
makeUI()
self.isShowButton = isShowButton
commentCountButton.isHidden = !isShowButton
textField.isUserInteractionEnabled = !isShowButton
}
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
backgroundColor = .white
addSubview(lineView)
addSubview(containerView)
containerView.addSubview(textField)
addSubview(commentCountButton)
}
override func layoutSubviews() {
super.layoutSubviews()
lineView.snp.makeConstraints { make in
make.top.equalTo(self)
make.left.equalTo(self)
make.right.equalTo(self)
make.height.equalTo(1)
}
if isShowButton {
commentCountButton.snp.remakeConstraints { make in
make.right.equalTo(self).offset(-6)
make.top.equalTo(self).offset(20)
make.height.equalTo(24)
}
containerView.snp.remakeConstraints { make in
make.right.equalTo(commentCountButton.snp.left).offset(-14)
make.centerY.equalTo(commentCountButton)
make.left.equalTo(self).offset(18)
make.height.equalTo(38)
}
textField.snp.remakeConstraints { make in
make.right.equalTo(containerView).offset(-24)
make.left.equalTo(containerView).offset(24)
make.top.equalTo(containerView).offset(6)
make.bottom.equalTo(containerView).offset(-6)
}
} else {
containerView.snp.remakeConstraints { make in
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(14)
make.right.equalTo(self).offset(-18)
make.height.equalTo(38)
}
textField.snp.remakeConstraints { make in
make.right.equalTo(containerView).offset(-24)
make.left.equalTo(containerView).offset(24)
make.top.equalTo(containerView).offset(6)
make.bottom.equalTo(containerView).offset(-6)
}
}
}
}

@ -22,6 +22,7 @@ class TableViewController: ViewController, UIScrollViewDelegate {
let view = TableView(frame: CGRect(), style: .grouped)
// view.emptyDataSetSource = self
// view.emptyDataSetDelegate = self
view.separatorColor = .clear
view.rx.setDelegate(self).disposed(by: rx.disposeBag)
return view
}()

@ -23,10 +23,8 @@ struct Journal: Codable, IdentifiableType, Equatable {
let editor: String?
let title: String?
let ipLocation: String?
let summary: String?
var isExpand: Bool?
var trackCount: Int?
var identity: String {
return id

@ -29,17 +29,21 @@ enum LikeType: String {
}
struct AudioSection {
var count: Int
var dowloadState: Bool
var isLike: Bool
}
enum JournalSection {
case audio(header: Journal?, items: [JournalItem])
case journal(header: Journal?, items: [JournalItem])
case audioSection(header: AudioSection?, items: [JournalItem])
case journalSection(header: String, items: [JournalItem])
}
enum JournalItem {
case audioItem(model: AudioTrack)
case journaItem(model: Journal)
case journaItem(items: [Journal])
}
@ -48,30 +52,22 @@ extension JournalSection: SectionModelType {
var items: [JournalItem] {
switch self {
case .audio( _, let items):
return items.map { $0 }
case .journal(_, let items):
return items.map { $0 }
case .audioSection( _, let items):
return items
case .journalSection( _, let items):
return items
}
}
var header: Journal? {
switch self {
case .audio( let header, _):
return header
case .journal(let header, _):
return header
}
}
init(original: JournalSection, items: [JournalItem]) {
switch original {
case .audio(let header, let items):
self = .audio(header: header, items: items)
case .journal(let header, let items):
self = .journal(header: header, items: items)
case .audioSection(let header, let items):
self = .audioSection(header: header, items: items)
case .journalSection(let header, let items):
self = .journalSection(header: header, items: items)
}
}
}

@ -8,6 +8,7 @@
import UIKit
import FSPagerView
import CHIPageControl
import RollingNotice_Swift
class HomeNumberView: UIView {
lazy var numberLabel: UILabel = {
@ -154,6 +155,19 @@ class HomeViewCell: UITableViewCell {
return commentLabel
}()
lazy var rollingNoticeView: GYRollingNoticeView = {
let rollingNoticeView = GYRollingNoticeView.init()
rollingNoticeView.backgroundColor = .red
rollingNoticeView.stayInterval = 5
rollingNoticeView.dataSource = self
// rollingNoticeView.delegate = self
rollingNoticeView.register(HomeNoticeViewCell.self, forCellReuseIdentifier: "HomeNoticeViewCell")
rollingNoticeView.stopRoll()
rollingNoticeView.isHidden = true
return rollingNoticeView
}()
lazy var gradientLayerView: GradientLayerView = {
let gradientLayerView = GradientLayerView.init()
@ -184,38 +198,28 @@ class HomeViewCell: UITableViewCell {
didSet {
guard let journal = journal else { return }
titleLabel.text = journal.title
subTitleLabel.text = journal.content ?? ""
commentLabel.text = journal.commentList?.first?.content ?? "暂无评论,快去抢沙发吧!"
subTitleLabel.text = journal.summary ?? ""
commentLabel.text = "暂无评论,快去抢沙发吧!"
homeNumberView.numberLabel.text = journal.journalNo
titleImageView.kf.setImage(with: URL.init(string: journal.image ?? ""))
if journal.commentList?.isEmpty == false {
commentLabel.isHidden = true
rollingNoticeView.isHidden = false
rollingNoticeView.reloadDataAndStartRoll()
commentCountButton.commentCountLabel.text = journal.totalCommentReply?.commentDigital(aimCount: 999)
multiUserAvatarView.journal = journal
multiUserAvatarView.isHidden = false
multiUserAvatarView.snp.remakeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.bottom.equalTo(containerView).offset(-21)
}
commentLabel.snp.remakeConstraints { make in
make.left.equalTo(multiUserAvatarView.snp.right).offset(9)
make.right.equalTo(commentCountButton.snp.left).offset(-50)
make.centerY.equalTo(multiUserAvatarView)
}
} else {
rollingNoticeView.stopRoll()
commentLabel.isHidden = false
rollingNoticeView.isHidden = true
multiUserAvatarView.isHidden = true
commentLabel.snp.remakeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.right.equalTo(commentCountButton.snp.left).offset(-50)
make.centerY.equalTo(commentCountButton)
}
}
@ -239,9 +243,6 @@ class HomeViewCell: UITableViewCell {
}
func makeUI() {
// contentView.layer.cornerRadius = 6
// contentView.layer.masksToBounds = true
contentView.backgroundColor = .backgroundColor()
containerView.backgroundColor = .white
@ -256,6 +257,7 @@ class HomeViewCell: UITableViewCell {
containerView.addSubview(subTitleLabel)
containerView.addSubview(multiUserAvatarView)
containerView.addSubview(commentLabel)
containerView.addSubview(rollingNoticeView)
containerView.addSubview(commentCountButton)
contentView.addSubview(homeNoLoginView)
@ -313,9 +315,21 @@ class HomeViewCell: UITableViewCell {
}
commentLabel.snp.makeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.right.equalTo(commentCountButton.snp.left).offset(-50)
make.centerY.equalTo(multiUserAvatarView)
}
multiUserAvatarView.snp.remakeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.bottom.equalTo(containerView).offset(-21)
}
rollingNoticeView.snp.remakeConstraints { make in
make.left.equalTo(multiUserAvatarView.snp.right).offset(9)
make.right.equalTo(commentCountButton.snp.left).offset(-50)
make.centerY.equalTo(multiUserAvatarView)
make.height.equalTo(30)
}
@ -338,6 +352,27 @@ class HomeViewCell: UITableViewCell {
}
extension HomeViewCell: GYRollingNoticeViewDataSource {
func numberOfRowsFor(roolingView: GYRollingNoticeView) -> Int {
return self.journal?.commentList?.count ?? 0
}
func rollingNoticeView(roolingView: GYRollingNoticeView, cellAtIndex index: Int) -> GYNoticeViewCell {
let cell = roolingView.dequeueReusableCell(withIdentifier: "HomeNoticeViewCell") as! HomeNoticeViewCell
if let commentList = self.journal?.commentList, commentList.count >= index {
cell.titleLabel.text = self.journal?.commentList?[index].content ?? "暂无评论,快去抢沙发吧!"
}
return cell
}
}
class HomeSectionView: UIView {
@ -652,3 +687,42 @@ class HomePagerView: UIView {
}
class HomeNoticeViewCell: GYNoticeViewCell {
var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.boldSystemFont(ofSize: 14)
titleLabel.textColor = .secondaryText()
return titleLabel
}()
required init(reuseIdentifier: String?, textLabelLeading: CGFloat = 10, textLabelTrailing: CGFloat = 10) {
super.init(reuseIdentifier: reuseIdentifier, textLabelLeading: textLabelLeading, textLabelTrailing: textLabelTrailing)
setupSubviews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
contentView.addSubview(titleLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.snp.makeConstraints { (make) in
make.left.equalTo(contentView).offset(5)
make.centerY.equalTo(contentView)
}
}
}

@ -119,8 +119,9 @@ class HomeViewController: TableViewController {
switch homeSectionItem {
case .journalDetil(model: let journal):
let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
let value = BehaviorRelay<Journal>.init(value: journal)
let journalDetailViewModel = JournalDetailViewModel.init(journal: value, provider: viewModel.provider)
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
@ -272,7 +273,7 @@ extension HomeViewController: FSPagerViewDataSource {
func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell {
let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "bannerCell", at: index)
let item = carouselItems[index]
// cell.imageView?.contentMode = .scaleAspectFit
cell.imageView?.kf.setImage(with: URL.init(string: item.imgPath ?? ""))
cell.imageView?.backgroundColor = .init(hex: 0xf5f5f5)

@ -13,69 +13,13 @@ import ETNavBarTransparent
import SVProgressHUD
import RxOptional
class JournalDetailController: TableViewController {
let journalCollapsibleHeaderView: JournalCollapsibleHeaderView = {
let journalCollapsibleHeaderView = JournalCollapsibleHeaderView.init()
class JournalDetailController: ViewController, UIScrollViewDelegate {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let viewModel = self.viewModel as? JournalDetailViewModel
if sectionIndex == 0 {
let singleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
let singleColumnItem = NSCollectionLayoutItem(layoutSize: singleColumnItemSize)
// singleColumnItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
let singleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: singleColumnItemSize, subitems: [singleColumnItem])
let singleColumnSection = NSCollectionLayoutSection(group: singleColumnGroup)
// header
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(viewModel?.headerHeight ?? 100))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
singleColumnSection.boundarySupplementaryItems = [header]
// singleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
return singleColumnSection
} else {
let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147))
let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize)
// doubleColumnItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 7.5, bottom: 0, trailing: 7.5)
let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2)
doubleColumnGroup.interItemSpacing = .fixed(10)
let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
doubleColumnSection.boundarySupplementaryItems = [header]
doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
doubleColumnSection.interGroupSpacing = 24
return doubleColumnSection
}
}
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.register(JournalAudioViewCell.self, forCellWithReuseIdentifier: "JournalAudioViewCell")
collectionView.register(JournalAudioHeaderView.self, forSupplementaryViewOfKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalAudioHeaderView")
collectionView.register(JournalDetailHeaderView.self, forSupplementaryViewOfKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalDetailHeaderView")
collectionView.backgroundColor = .white
return collectionView
return journalCollapsibleHeaderView
}()
weak var audioHeaderView: JournalAudioHeaderView?
var commentToolView: CommentToolView = {
let commentToolView = CommentToolView.init()
@ -83,15 +27,13 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
@ -102,18 +44,24 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
super.viewWillDisappear(animated)
navBarBgAlpha = 1
}
override func makeUI() {
super.makeUI()
collectionView.rx.setDelegate(self).disposed(by: rx.disposeBag)
view.addSubview(commentToolView)
view.addSubview(collectionView)
// updateText(text: "")
tableView.tableHeaderView = journalCollapsibleHeaderView
tableView.tableFooterView = UIView()
tableView.register(JournalAudioViewCell.self, forCellReuseIdentifier: "JournalAudioViewCell")
tableView.register(JournalContainerViewCell.self, forCellReuseIdentifier: "JournalContainerViewCell")
tableView.register(JournalAudioSectionView.self, forHeaderFooterViewReuseIdentifier: "JournalAudioSectionView")
tableView.register(JournalDetailSectionView.self, forHeaderFooterViewReuseIdentifier: "JournalDetailSectionView")
tableView.mj_header = nil
tableView.mj_footer = nil
}
@ -125,169 +73,66 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
let viewDidLoad = Observable.of(())
let input = JournalDetailViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
viewDidLoad: viewDidLoad,
selection: collectionView.rx.itemSelected.asDriver(),
modelSelected: tableView.rx.modelSelected(JournalItem.self).asDriver(),
notiPlayAudioTrack: NotificationCenter.default.rx.notification(.notiPlayAudioTrack))
// dropButtonTrigger: headerView.dropButton.rx.tap.asDriver(),
// playButtonTrigger: headerView.playButton.rx.tap.asDriver(),
// shareButtonTrigger: headerView.playButton.rx.tap.asDriver())//TODO
let output = viewModel.transform(input: input)
let dataSource = RxCollectionViewSectionedReloadDataSource<JournalSection>(
configureCell: { dataSource, collectionView, indexPath, item in
let dataSource = RxTableViewSectionedReloadDataSource<JournalSection>(
configureCell: { dataSource, tableView, indexPath, item in
switch dataSource[indexPath] {
case .audioItem(let audioTrack):
let cell: JournalAudioViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
let cell: JournalAudioViewCell = tableView.dequeueReusableCell(withIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
cell.audioTrack = audioTrack
cell.buttonTapCallback = { [weak self] audioTrack in
viewModel.item.accept(audioTrack)
let audioMoreActionViewModel = AudioMoreActionViewModel.init(audioTrack: viewModel.item, provider: viewModel.provider)
self?.navigator.show(segue: .audioMore(viewModel: audioMoreActionViewModel), sender: self, transition: .navigationPresent(type: .audioMore))
}
return cell
case .journaItem(let journalDetail):
let cell: JournalViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
cell.journal = journalDetail
return cell
}
},
configureSupplementaryView: { [weak self] dataSource, collectionView, kind, indexPath in
guard kind == UICollectionView.elementKindSectionHeader, let self = self else {
return UICollectionReusableView()
}
if indexPath.section == 0 {
let resuableView: JournalAudioHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalAudioHeaderView", for: indexPath) as! JournalAudioHeaderView
self.audioHeaderView = resuableView
let section = dataSource.sectionModels.first.value
resuableView.journal = section?.header
resuableView.saveButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
resuableView.titleImageView.image?.saveImageToPhotoLibrary()
.subscribe(onNext: { _ in
SVProgressHUD.showText(withStatus: "保存成功")
}, onError: { error in
}).disposed(by: self.rx.disposeBag)
}.disposed(by: self.rx.disposeBag)
case .journaItem(let journals):
let cell: JournalContainerViewCell = tableView.dequeueReusableCell(withIdentifier: "JournalContainerViewCell", for: indexPath) as! JournalContainerViewCell
let dataSource = JournalContainerViewCell.dataSource()
let header = SearchCategory.init(id: nil, nameCh: nil, nameEn: nil, image: nil, description: nil)
let sections = [MusicStyleSection.init(header: header, items: journals)]
resuableView.dropButtonTapObservable.subscribe { _ in
viewModel.isExpand.accept(!resuableView.isExpand)
viewModel.updateHeaderHeight(newHeight: viewModel.isExpand.value ? 506 + BaseDimensions.topHeight : 400 + BaseDimensions.topHeight)
// resuableView.isExpand = viewModel.isExpand.value
}.disposed(by: self.rx.disposeBag)
let items = Driver.just(sections)
items.drive(cell.collectionView.rx.items(dataSource: dataSource)).disposed(by: self.rx.disposeBag)
resuableView.playButtonTapObservable.subscribe { _ in
let audioModels: [AudioTrack] = output.items.value.flatMap { section -> [JournalItem] in
switch section {
case .audio(_, let items):
return items
default:
return []
}
}.compactMap { item -> AudioTrack? in
if case let .audioItem(model) = item {
return model
} else {
return nil
}
cell.onItemSelect = { [weak self] journal in
guard AuthManager.shared.token?.isValid == true else {
let loginViewModel = LoginViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .login(viewModel: loginViewModel), sender: self)
return
}
guard let track = audioModels.first else { return }
let value = BehaviorRelay<Journal>.init(value: journal)
let journalDetailViewModel = JournalDetailViewModel.init(journal: value, provider: viewModel.provider)
let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider)
self.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
//
AudioManager.sharedInstance.setPlaylist(list: audioModels)
AudioManager.sharedInstance.playTrack(track: track)
}
}.disposed(by: self.rx.disposeBag)
// resuableView.downLoadButtonTapObservable
// .bind(to: viewModel.downloadButtonTapped)
// .disposed(by: self.rx.disposeBag)
//
// resuableView.likeButtonTapObservable
// .bind(to: viewModel.likeButtonTapped)
// .disposed(by: self.rx.disposeBag)
//
//
// resuableView.shareButtonTapObservable
// .bind(to: viewModel.shareButtonTrigger)
// .disposed(by: self.rx.disposeBag)
resuableView.downLoadButtonTapObservable.subscribe { _ in
viewModel.downloadButtonTapped.onNext(())
}.disposed(by: self.rx.disposeBag)
resuableView.likeButtonTapObservable.subscribe { _ in
print("likeButtonTapObservable")
viewModel.likeButtonTapped.onNext(())
}.disposed(by: self.rx.disposeBag)
resuableView.shareButtonTapObservable.subscribe { _ in
viewModel.shareButtonTrigger.onNext(())
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
}.disposed(by: self.rx.disposeBag)
}
return cell
return resuableView
} else {
let resuableView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalDetailHeaderView", for: indexPath) as! JournalDetailHeaderView
return resuableView
}
}
)
output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
input.selection.drive { indexPath in
}.disposed(by: rx.disposeBag)
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.itemSelected.subscribe { [weak self] sectionItem in
output.modelSelected.drive {[weak self] journalItem in
switch sectionItem {
switch journalItem {
case .audioItem(let track):
let audioModels: [AudioTrack] = output.items.value.flatMap { section -> [JournalItem] in
switch section {
case .audio(_, let items):
case .audioSection(header: nil, items: let items):
return items
default:
return []
@ -299,82 +144,78 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
return nil
}
}
let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider)
self?.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
//
AudioManager.sharedInstance.setPlaylist(list: audioModels)
AudioManager.sharedInstance.playTrack(track: track)
}
case .journaItem(let journal):
case .journaItem(let journal): break
guard AuthManager.shared.token?.isValid == true else {
let loginViewModel = LoginViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .login(viewModel: loginViewModel), sender: self)
return
}
let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
}
}.disposed(by: rx.disposeBag)
output.toShare.subscribe { [weak self] _ in
let share = ShareActionViewModel.init(audioTrack: nil, journal: viewModel.journal, provider: viewModel.provider)
let share = ShareActionViewModel.init(audioTrack: nil, journal: viewModel.journal.value, provider: viewModel.provider)
self?.navigator.show(segue: .share(viewModel: share), sender: self, transition: .navigationPresent(type: .share))
}.disposed(by: rx.disposeBag)
output.isLike.subscribe { [weak self] isLike in
guard let self = self else { return }
self.audioHeaderView?.journalAudioSectionView.likeButton.isSelected = isLike
}.disposed(by: rx.disposeBag)
output.journalDetail.subscribe { [weak self] journal in
output.journal.subscribe { [weak self] journal in
guard let self = self else { return }
self.commentToolView.commentCountButton.commentCountLabel.text = "\(journal)"
} onError: { error in
self.journalCollapsibleHeaderView.journal = journal
}.disposed(by: rx.disposeBag)
self.updateHeader()
if let header: JournalAudioSectionView = tableView.headerView(forSection: 0) as? JournalAudioSectionView {
print("有header")
header.likeButton.isSelected = journal.haveCollect ?? false
}
}.disposed(by: rx.disposeBag)
commentToolView.containerView.rx.tapGesture().when(.recognized)
.subscribe { [weak self] tap in
guard let self = self else { return }
let commentViewModel = CommentViewModel.init(journal: viewModel.journal, provider: viewModel.provider)
let commentViewModel = CommentViewModel.init(journal: viewModel.journal.value, provider: viewModel.provider)
self.navigator.show(segue: .comment(viewModel: commentViewModel), sender: self)
}.disposed(by: rx.disposeBag)
journalCollapsibleHeaderView.dropButton.rx.tap.subscribe { _ in
self.journalCollapsibleHeaderView.isExpand.toggle()
self.updateHeader()
}.disposed(by: rx.disposeBag)
}
override func viewDidLayoutSubviews() {
@ -388,26 +229,494 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
make.height.equalTo(BaseDimensions.bottomHeight + 48)
}
collectionView.snp.makeConstraints { make in
tableView.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.bottom.equalTo(commentToolView.snp.top)
make.top.equalTo(view).offset(-BaseDimensions.topHeight)
}
}
func updateHeader() {
guard let headerView = tableView.tableHeaderView as? JournalCollapsibleHeaderView else { return }
let width = tableView.bounds.width
headerView.frame = CGRect(x: 0, y: 0, width: width, height: 1000)
headerView.layoutIfNeeded()
let size = headerView.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel)
print("header size: \(size.height)")
if headerView.frame.size.height != size.height {
headerView.frame.size.height = size.height
tableView.tableHeaderView = headerView
}
}
}
extension JournalDetailController {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let viewModel = viewModel as? JournalDetailViewModel else { return nil }
extension JournalDetailController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// sectionheader
switch section {
case 0:
let headerView = JournalAudioSectionView.init(reuseIdentifier: "JournalAudioSectionView")
headerView.countLabel.text = "\(tableView.numberOfRows(inSection: 0))"
headerView.likeButton.isSelected = viewModel.journal.value.haveCollect ?? false
headerView.downloadButton.rx.tap.subscribe { _ in
}.disposed(by: rx.disposeBag)
headerView.likeButton.rx.tap.subscribe { _ in
viewModel.likeButtonTapped.accept(())
}.disposed(by: rx.disposeBag)
headerView.shareButton.rx.tap.subscribe { _ in
viewModel.shareButtonTrigger.accept(())
}.disposed(by: rx.disposeBag)
return headerView
case 1:
let headerView = JournalDetailSectionView.init(reuseIdentifier: "JournalDetailSectionView")
return headerView
default:
return nil
}
return CGSize(width: collectionView.frame.width, height: 100)
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
// header
return 44
}
}
//class JournalDetailController: ViewController, UIScrollViewDelegate {
//
// lazy var collectionView: UICollectionView = {
//
// let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
// let viewModel = self.viewModel as? JournalDetailViewModel
////
//// case collapsible(journal: Journal)
//// case audioSection(header: AudioSection, items: [JournalItem])
//// case journalSection(header: String, items: [JournalItem])
//
//
//
// if sectionIndex == 0 {
// let singleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
// let singleColumnItem = NSCollectionLayoutItem(layoutSize: singleColumnItemSize)
//
// let singleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: singleColumnItemSize, subitems: [singleColumnItem])
//
// let singleColumnSection = NSCollectionLayoutSection(group: singleColumnGroup)
//
// // header
// let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(viewModel?.headerHeight ?? 100))
// let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
// singleColumnSection.boundarySupplementaryItems = [header]
//
// return singleColumnSection
// } else if sectionIndex == 1 {
// let singleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
// let singleColumnItem = NSCollectionLayoutItem(layoutSize: singleColumnItemSize)
//
// let singleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: singleColumnItemSize, subitems: [singleColumnItem])
//
// let singleColumnSection = NSCollectionLayoutSection(group: singleColumnGroup)
//
// // header
// let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
// let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
// singleColumnSection.boundarySupplementaryItems = [header]
//
// return singleColumnSection
//
// } else {
// let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147))
// let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize)
//// doubleColumnItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 7.5, bottom: 0, trailing: 7.5)
//
// let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2)
// doubleColumnGroup.interItemSpacing = .fixed(10)
//
// let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup)
//
// let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
// let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
// doubleColumnSection.boundarySupplementaryItems = [header]
//
// doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
// doubleColumnSection.interGroupSpacing = 24
//
// return doubleColumnSection
//
// }
// }
//
// let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
//
// collectionView.register(JournalCollapsibleViewCell.self, forCellWithReuseIdentifier: "JournalCollapsibleViewCell")
// collectionView.register(JournalAudioSectionViewCell.self, forCellWithReuseIdentifier: "JournalAudioSectionViewCell")
// collectionView.register(JournalDetailSectionViewCell.self, forCellWithReuseIdentifier: "JournalDetailSectionViewCell")
// collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
// collectionView.register(JournalAudioViewCell.self, forCellWithReuseIdentifier: "JournalAudioViewCell")
//
//
// collectionView.backgroundColor = .white
//
//
//
// return collectionView
// }()
//
//
// var commentToolView: CommentToolView = {
// let commentToolView = CommentToolView.init()
//
// return commentToolView
// }()
//
//
//
// override func viewDidLoad() {
// super.viewDidLoad()
//
//
//
//
// }
//
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated)
//
// navBarBgAlpha = 0
// }
//
// override func viewWillDisappear(_ animated: Bool) {
// super.viewWillDisappear(animated)
//
// navBarBgAlpha = 1
//
// }
//
// override func makeUI() {
// super.makeUI()
//
// collectionView.rx.setDelegate(self).disposed(by: rx.disposeBag)
//
// view.addSubview(commentToolView)
// view.addSubview(collectionView)
//
//// updateText(text: "")
//
// }
//
// override func bindViewModel() {
// super.bindViewModel()
//
// guard let viewModel = viewModel as? JournalDetailViewModel else { return }
//
// let viewDidLoad = Observable.of(())
// let input = JournalDetailViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
// viewDidLoad: viewDidLoad,
// selection: collectionView.rx.itemSelected.asDriver(),
// notiPlayAudioTrack: NotificationCenter.default.rx.notification(.notiPlayAudioTrack))
//
//// dropButtonTrigger: headerView.dropButton.rx.tap.asDriver(),
//// playButtonTrigger: headerView.playButton.rx.tap.asDriver(),
//// shareButtonTrigger: headerView.playButton.rx.tap.asDriver())//TODO
//
// let output = viewModel.transform(input: input)
//
// let dataSource = RxCollectionViewSectionedReloadDataSource<JournalSection>(
// configureCell: { dataSource, collectionView, indexPath, item in
// switch dataSource[indexPath] {
// case .audioItem(let audioTrack):
// let cell: JournalAudioViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
//
// cell.audioTrack = audioTrack
// cell.buttonTapCallback = { [weak self] audioTrack in
// viewModel.item.accept(audioTrack)
// let audioMoreActionViewModel = AudioMoreActionViewModel.init(audioTrack: viewModel.item, provider: viewModel.provider)
//
// self?.navigator.show(segue: .audioMore(viewModel: audioMoreActionViewModel), sender: self, transition: .navigationPresent(type: .audioMore))
//
//
// }
//
// return cell
// case .journaItem(let journalDetail):
// let cell: JournalViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
//
// cell.journal = journalDetail
//
// return cell
//
// }
// },
// configureSupplementaryView: { [weak self] dataSource, collectionView, kind, indexPath in
// guard kind == UICollectionView.elementKindSectionHeader, let self = self else {
// return UICollectionReusableView()
// }
// if indexPath.section == 0 {
// let resuableView: JournalAudioHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalAudioHeaderView", for: indexPath) as! JournalAudioHeaderView
// self.audioHeaderView = resuableView
//
// let section = dataSource.sectionModels.first.value
// resuableView.journal = section?.header
//
//
// resuableView.saveButton.rx.tap.subscribe { [weak self] _ in
// guard let self = self else { return }
//
// resuableView.titleImageView.image?.saveImageToPhotoLibrary()
// .subscribe(onNext: { _ in
// SVProgressHUD.showText(withStatus: "")
//
// }, onError: { error in
//
// }).disposed(by: self.rx.disposeBag)
//
// }.disposed(by: self.rx.disposeBag)
//
//
//
// resuableView.dropButtonTapObservable.subscribe { _ in
//
// viewModel.isExpand.accept(!resuableView.isExpand)
// viewModel.updateHeaderHeight(newHeight: viewModel.isExpand.value ? 506 + BaseDimensions.topHeight : 400 + BaseDimensions.topHeight)
//
//// resuableView.isExpand = viewModel.isExpand.value
// }.disposed(by: self.rx.disposeBag)
//
// resuableView.playButtonTapObservable.subscribe { _ in
//
//
// let audioModels: [AudioTrack] = output.items.value.flatMap { section -> [JournalItem] in
// switch section {
// case .audio(_, let items):
// return items
// default:
// return []
// }
// }.compactMap { item -> AudioTrack? in
// if case let .audioItem(model) = item {
// return model
// } else {
// return nil
// }
// }
//
// guard let track = audioModels.first else { return }
//
//
// let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider)
// self.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
//
//
//
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// //
// AudioManager.sharedInstance.setPlaylist(list: audioModels)
// AudioManager.sharedInstance.playTrack(track: track)
// }
//
// }.disposed(by: self.rx.disposeBag)
//
//
//// resuableView.downLoadButtonTapObservable
//// .bind(to: viewModel.downloadButtonTapped)
//// .disposed(by: self.rx.disposeBag)
////
//// resuableView.likeButtonTapObservable
//// .bind(to: viewModel.likeButtonTapped)
//// .disposed(by: self.rx.disposeBag)
////
////
//// resuableView.shareButtonTapObservable
//// .bind(to: viewModel.shareButtonTrigger)
//// .disposed(by: self.rx.disposeBag)
//
//
//
// resuableView.downLoadButtonTapObservable.subscribe { _ in
// viewModel.downloadButtonTapped.onNext(())
//
// }.disposed(by: self.rx.disposeBag)
// resuableView.likeButtonTapObservable.subscribe { _ in
// print("likeButtonTapObservable")
// viewModel.likeButtonTapped.onNext(())
//
// }.disposed(by: self.rx.disposeBag)
//
// resuableView.shareButtonTapObservable.subscribe { _ in
// viewModel.shareButtonTrigger.onNext(())
//
// }.disposed(by: self.rx.disposeBag)
//
//
// return resuableView
//
// } else {
// let resuableView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalDetailHeaderView", for: indexPath) as! JournalDetailHeaderView
//
// return resuableView
//
// }
// }
// )
//
//
// output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
//
// input.selection.drive { indexPath in
//
//
// }.disposed(by: rx.disposeBag)
//
//
// output.itemSelected.subscribe { [weak self] sectionItem in
//
// switch sectionItem {
// case .audioItem(let track):
// let audioModels: [AudioTrack] = output.items.value.flatMap { section -> [JournalItem] in
// switch section {
// case .audio(_, let items):
// return items
// default:
// return []
// }
// }.compactMap { item -> AudioTrack? in
// if case let .audioItem(model) = item {
// return model
// } else {
// return nil
// }
// }
//
//
// let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider)
// self?.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
//
//
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// //
// AudioManager.sharedInstance.setPlaylist(list: audioModels)
// AudioManager.sharedInstance.playTrack(track: track)
// }
//
//
//
//
//
// case .journaItem(let journal):
//
// guard AuthManager.shared.token?.isValid == true else {
// let loginViewModel = LoginViewModel.init(provider: viewModel.provider)
// self?.navigator.show(segue: .login(viewModel: loginViewModel), sender: self)
// return
// }
//
// let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
//
// self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
//
// }
//
//
//
// }.disposed(by: rx.disposeBag)
//
//
// output.toShare.subscribe { [weak self] _ in
//
// let share = ShareActionViewModel.init(audioTrack: nil, journal: viewModel.journal, provider: viewModel.provider)
// self?.navigator.show(segue: .share(viewModel: share), sender: self, transition: .navigationPresent(type: .share))
//
// }.disposed(by: rx.disposeBag)
//
// output.isLike.subscribe { [weak self] isLike in
// guard let self = self else { return }
// self.audioHeaderView?.journalAudioSectionView.likeButton.isSelected = isLike
// }.disposed(by: rx.disposeBag)
//
//
// output.journalDetail.subscribe { [weak self] journal in
// guard let self = self else { return }
// self.commentToolView.commentCountButton.commentCountLabel.text = "\(journal)"
// } onError: { error in
//
// }.disposed(by: rx.disposeBag)
//
//
//
//
// commentToolView.containerView.rx.tapGesture().when(.recognized)
// .subscribe { [weak self] tap in
// guard let self = self else { return }
//
// let commentViewModel = CommentViewModel.init(journal: viewModel.journal, provider: viewModel.provider)
//
// self.navigator.show(segue: .comment(viewModel: commentViewModel), sender: self)
//
// }.disposed(by: rx.disposeBag)
//
//
//
//
//
//
// }
//
//
//
//
// override func viewDidLayoutSubviews() {
// super.viewDidLayoutSubviews()
//
//
// commentToolView.snp.makeConstraints { make in
// make.left.equalTo(view)
// make.right.equalTo(view)
// make.bottom.equalTo(view)
// make.height.equalTo(BaseDimensions.bottomHeight + 48)
// }
//
// collectionView.snp.makeConstraints { make in
// make.left.equalTo(view)
// make.right.equalTo(view)
// make.bottom.equalTo(commentToolView.snp.top)
// make.top.equalTo(view).offset(-BaseDimensions.topHeight)
// }
//
//
// }
//
//}
//
//
//extension JournalDetailController: UICollectionViewDelegateFlowLayout {
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// // sectionheader
//
//
//
//
// return CGSize(width: collectionView.frame.width, height: 100)
// }
//}

@ -9,11 +9,11 @@ import UIKit
import ESTMusicIndicator
import RxSwift
import RxCocoa
import RxDataSources
import DateToolsSwift
import FSPopoverView
class JournalAudioHeaderView: UICollectionReusableView {
class JournalCollapsibleHeaderView: UIView {
var titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.isUserInteractionEnabled = true
@ -138,14 +138,6 @@ class JournalAudioHeaderView: UICollectionReusableView {
return gradientLayerView
}()
let journalAudioSectionView: JournalAudioSectionView = {
let journalAudioSectionView = JournalAudioSectionView.init()
return journalAudioSectionView
}()
let saveButton: UIButton = {
let saveButton = UIButton.init()
saveButton.titleLabel?.font = UIFont.systemFont(ofSize: 12)
@ -165,10 +157,6 @@ class JournalAudioHeaderView: UICollectionReusableView {
return popoverView
}()
var isExpand = false {
didSet {
@ -190,24 +178,12 @@ class JournalAudioHeaderView: UICollectionReusableView {
}
}
var dropButtonTapObservable: Observable<Void> {
return dropButton.rx.tap.asObservable()
}
var playButtonTapObservable: Observable<Void> {
return playButton.rx.tap.asObservable()
}
var downLoadButtonTapObservable: Observable<Void> {
return journalAudioSectionView.downloadButton.rx.tap.asObservable()
}
var likeButtonTapObservable: Observable<Void> {
return journalAudioSectionView.likeButton.rx.tap.asObservable()
}
var shareButtonTapObservable: Observable<Void> {
return journalAudioSectionView.shareButton.rx.tap.asObservable()
}
var journal: Journal? {
didSet {
@ -230,14 +206,8 @@ class JournalAudioHeaderView: UICollectionReusableView {
}
self.isExpand = journal.isExpand ?? false
playButton.isSelected = AudioManager.sharedInstance.currentTrack?.journalNo == journal.journalNo
self.journalAudioSectionView.countLabel.text = "\(journal.trackCount ?? 0)"
self.journalAudioSectionView.likeButton.isSelected = journal.haveCollect ?? false
}
}
@ -284,10 +254,7 @@ class JournalAudioHeaderView: UICollectionReusableView {
addSubview(titleImageView)
addSubview(containerView)
addSubview(playButton)
addSubview(journalAudioSectionView)
// contentLabel.layer.insertSublayer(gradientLayer, at: 0)
titleImageView.snp.makeConstraints { make in
make.left.equalTo(self)
@ -302,17 +269,10 @@ class JournalAudioHeaderView: UICollectionReusableView {
make.top.equalTo(containerView).offset(-29)
}
journalAudioSectionView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.bottom.equalTo(self)
make.height.equalTo(30)
}
containerView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.bottom.equalTo(journalAudioSectionView.snp.top)
make.bottom.equalTo(self)
make.top.equalTo(titleImageView.snp.bottom).offset(-24)
}
@ -366,9 +326,8 @@ class JournalAudioHeaderView: UICollectionReusableView {
dropButton.snp.remakeConstraints { make in
make.left.equalTo(containerView).offset(18)
make.top.equalTo(contentLabel.snp.bottom).offset(16)
make.bottom.equalTo(containerView.snp.bottom).offset(-35)
make.bottom.equalTo(containerView.snp.bottom).offset(-23)
}
// gradientLayer.frame = self.contentLabel.frame
}
@ -376,17 +335,13 @@ class JournalAudioHeaderView: UICollectionReusableView {
override func layoutSubviews() {
super.layoutSubviews()
// gradientLayer.frame = self.contentLabel.frame
// print("gradientLayer: \(gradientLayer.isHidden) \(gradientLayer.frame)")
}
}
extension JournalAudioHeaderView: FSPopoverViewDataSource {
extension JournalCollapsibleHeaderView: FSPopoverViewDataSource {
func backgroundView(for popoverView: FSPopoverView) -> UIView? {
let view = UIView()
view.backgroundColor = .primaryText()
@ -414,52 +369,12 @@ extension JournalAudioHeaderView: FSPopoverViewDataSource {
}
class JournalDetailHeaderView: UICollectionReusableView {
var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.textColor = .primaryText()
titleLabel.text = "推荐期刊"
return titleLabel
}()
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
addSubview(titleLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.snp.makeConstraints { make in
make.left.equalTo(self)
make.centerY.equalTo(self)
make.right.equalTo(self)
}
}
}
class JournalAudioSectionView: UIView {
class JournalAudioSectionView: UITableViewHeaderFooterView {
var countLabel: UILabel = {
let countLabel = UILabel.init()
countLabel.font = UIFont.systemFont(ofSize: 15)
countLabel.text = "10首"
countLabel.text = "共0首"
return countLabel
}()
@ -487,9 +402,9 @@ class JournalAudioSectionView: UIView {
return shareButton
}()
override init(frame: CGRect) {
super.init(frame: frame)
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
makeUI()
}
@ -502,44 +417,81 @@ class JournalAudioSectionView: UIView {
func makeUI() {
backgroundColor = .white
addSubview(countLabel)
addSubview(downloadButton)
addSubview(likeButton)
addSubview(shareButton)
contentView.addSubview(countLabel)
contentView.addSubview(downloadButton)
contentView.addSubview(likeButton)
contentView.addSubview(shareButton)
}
override func layoutSubviews() {
super.layoutSubviews()
shareButton.snp.makeConstraints { make in
make.right.equalTo(self).offset(-18)
make.centerY.equalTo(self)
make.right.equalTo(contentView).offset(-18)
make.centerY.equalTo(contentView)
}
likeButton.snp.makeConstraints { make in
make.right.equalTo(shareButton.snp.left).offset(-15)
make.centerY.equalTo(self)
make.centerY.equalTo(contentView)
}
downloadButton.snp.makeConstraints { make in
make.right.equalTo(likeButton.snp.left).offset(-15)
make.centerY.equalTo(self)
make.centerY.equalTo(contentView)
}
countLabel.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.left.equalTo(contentView).offset(18)
make.right.equalTo(downloadButton.snp.left).offset(-15).priority(.low)
make.centerY.equalTo(self)
make.centerY.equalTo(contentView)
}
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
class JournalDetailSectionView: UITableViewHeaderFooterView {
var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.textColor = .primaryText()
titleLabel.text = "推荐期刊"
return titleLabel
}()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
addSubview(titleLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.centerY.equalTo(self)
make.right.equalTo(self)
}
}
}
class JournalAudioViewCell: UICollectionViewCell {
class JournalAudioViewCell: UITableViewCell {
lazy var coverView: UIImageView = {
let coverView = UIImageView.init()
@ -619,12 +571,13 @@ class JournalAudioViewCell: UICollectionViewCell {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@ -694,22 +647,41 @@ class JournalAudioViewCell: UICollectionViewCell {
}
class JournalDetailToolView: UIView {
var commentCountButton: CommentCountButton = {
let commentCountButton = CommentCountButton.init()
class JournalContainerViewCell: UITableViewCell {
lazy var collectionView: UICollectionView = {
return commentCountButton
}()
var commentTextView: UITextView = {
let commentTextView = UITextView.init()
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147))
let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize)
let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2)
doubleColumnGroup.interItemSpacing = .fixed(10)
let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup)
doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
doubleColumnSection.interGroupSpacing = 24
return doubleColumnSection
}
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
return commentTextView
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.backgroundColor = .white
collectionView.isScrollEnabled = false
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
var onItemSelect: ((Journal) -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
makeUI()
}
@ -719,77 +691,92 @@ class JournalDetailToolView: UIView {
}
func makeUI() {
addSubview(commentCountButton)
addSubview(commentTextView)
contentView.addSubview(collectionView)
collectionView.rx.modelSelected(Journal.self)
.subscribe(onNext: { [weak self] journal in
self?.onItemSelect?(journal)
})
.disposed(by: rx.disposeBag)
}
override func layoutSubviews() {
super.layoutSubviews()
commentCountButton.snp.makeConstraints { make in
make.right.equalTo(self).offset(-24)
make.centerY.equalTo(self)
}
// Item
let collectionViewWidth = BaseDimensions.screenWidth - 2 * 18
//
let totalSpacingWidth: CGFloat = (2 * 18) + (2 * 18) // item
// item
let adjustedItemWidth = (collectionViewWidth - totalSpacingWidth) / 2
commentTextView.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(14)
make.height.equalTo(38)
make.right.equalTo(commentCountButton.snp.left).offset(-15)
let rowsPerSection = ceil(Double(collectionView.numberOfItems(inSection: 0)) / Double(2))
let totalSectionHeight = CGFloat(rowsPerSection) * adjustedItemWidth + CGFloat(rowsPerSection - 1) * 18 + 36
print("collectionView height \(rowsPerSection) \(totalSectionHeight)")
collectionView.snp.remakeConstraints { make in
make.edges.equalTo(contentView)
make.height.equalTo(totalSectionHeight)
}
}
}
extension JournalContainerViewCell {
static func dataSource() -> RxCollectionViewSectionedReloadDataSource<MusicStyleSection> {
return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
configureCell: { dataSource, collectionView, indexPath, item in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
cell.journal = item
return cell
}
)
}
}
class CommentToolView: UIView {
let commentCountButton: CommentCountButton = {
let commentCountButton = CommentCountButton.init()
commentCountButton.setContentHuggingPriority(.required, for: .horizontal)
commentCountButton.setContentCompressionResistancePriority(.required, for: .horizontal)
return commentCountButton
}()
let containerView: UIView = {
let containerView = UIView.init()
containerView.layer.cornerRadius = 19
containerView.backgroundColor = .init(hex: 0x000000, alpha: 0.05)
class JournalViewCell: UICollectionViewCell {
let titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.layer.cornerRadius = 8
titleImageView.layer.masksToBounds = true
return containerView
titleImageView.backgroundColor = .backgroundColor()
return titleImageView
}()
let textField: UITextField = {
let textField = UITextField.init()
textField.font = UIFont.systemFont(ofSize: 15)
textField.attributedPlaceholder = NSAttributedString(
string: "说点想说的",
attributes: [NSAttributedString.Key.foregroundColor: UIColor.secondaryText()]
)
textField.returnKeyType = .send
return textField
let volLabel: UILabel = {
let volLabel = UILabel.init()
volLabel.font = UIFont.systemFont(ofSize: 12)
volLabel.textColor = .init(hex: 0x000000, alpha: 0.4)
return volLabel
}()
var lineView: UIView = {
let lineView = UIView.init()
lineView.backgroundColor = .separator()
return lineView
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .medium)
return titleLabel
}()
var isShowButton: Bool = true
init(isShowButton: Bool = true, frame: CGRect) {
super.init(frame: frame)
makeUI()
self.isShowButton = isShowButton
commentCountButton.isHidden = !isShowButton
textField.isUserInteractionEnabled = !isShowButton
var journal: Journal? {
didSet {
guard let journal = journal else { return }
titleImageView.kf.setImage(with: URL.init(string: journal.image ?? ""))
volLabel.text = "VOL·\(journal.journalNo ?? "")"
titleLabel.text = journal.title
}
}
override init(frame: CGRect) {
@ -803,65 +790,184 @@ class CommentToolView: UIView {
}
func makeUI() {
backgroundColor = .white
addSubview(lineView)
addSubview(containerView)
containerView.addSubview(textField)
addSubview(commentCountButton)
contentView.addSubview(titleImageView)
contentView.addSubview(volLabel)
contentView.addSubview(titleLabel)
titleImageView.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.right.equalTo(contentView)
make.top.equalTo(contentView)
make.height.equalTo(100)
}
volLabel.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.top.equalTo(titleImageView.snp.bottom).offset(9)
make.right.equalTo(contentView)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.right.equalTo(contentView)
make.top.equalTo(volLabel.snp.bottom).offset(2)
}
}
}
class JournalAudioCollectionViewCell: UICollectionViewCell {
lazy var coverView: UIImageView = {
let coverView = UIImageView.init()
coverView.layer.cornerRadius = 3
coverView.layer.masksToBounds = true
coverView.backgroundColor = .gray
return coverView
}()
override func layoutSubviews() {
super.layoutSubviews()
lazy var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
lineView.snp.makeConstraints { make in
make.top.equalTo(self)
make.left.equalTo(self)
make.right.equalTo(self)
make.height.equalTo(1)
}
return titleLabel
}()
lazy var detailLabel: UILabel = {
let detailLabel = UILabel.init()
detailLabel.font = UIFont.systemFont(ofSize: 12)
detailLabel.textColor = .init(hex: 0x000000, alpha: 0.6)
if isShowButton {
commentCountButton.snp.remakeConstraints { make in
make.right.equalTo(self).offset(-6)
make.top.equalTo(self).offset(20)
make.height.equalTo(24)
}
containerView.snp.remakeConstraints { make in
make.right.equalTo(commentCountButton.snp.left).offset(-14)
make.centerY.equalTo(commentCountButton)
make.left.equalTo(self).offset(18)
make.height.equalTo(38)
return detailLabel
}()
lazy var moreButtin: UIButton = {
let menuButtin = UIButton.init()
menuButtin.setImage(UIImage.init(named: "play_more_btn"), for: .normal)
}
textField.snp.remakeConstraints { make in
make.right.equalTo(containerView).offset(-24)
make.left.equalTo(containerView).offset(24)
make.top.equalTo(containerView).offset(6)
make.bottom.equalTo(containerView).offset(-6)
menuButtin.setContentHuggingPriority(.required, for: .horizontal)
menuButtin.setContentCompressionResistancePriority(.required, for: .horizontal)
}
} else {
containerView.snp.remakeConstraints { make in
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(14)
make.right.equalTo(self).offset(-18)
make.height.equalTo(38)
menuButtin.addTarget(self, action: #selector(menuButtinClick), for: .touchUpInside)
return menuButtin
}()
var lineView: UIView = {
let lineView = UIView.init()
lineView.backgroundColor = .backgroundColor()
lineView.isHidden = true
return lineView
}()
let musicIndicator: ESTMusicIndicatorView = {
let musicIndicator = ESTMusicIndicatorView.init()
musicIndicator.tintColor = .red
}
return musicIndicator
}()
var buttonTapCallback: ((AudioTrack) -> ())?
var audioTrack: AudioTrack? {
didSet {
guard let audioTrack = audioTrack else { return }
textField.snp.remakeConstraints { make in
make.right.equalTo(containerView).offset(-24)
make.left.equalTo(containerView).offset(24)
make.top.equalTo(containerView).offset(6)
make.bottom.equalTo(containerView).offset(-6)
titleLabel.text = audioTrack.title
detailLabel.text = (audioTrack.artist ?? "") + "/" + (audioTrack.album ?? "")
coverView.kf.setImage(with: URL.init(string: audioTrack.pic ?? ""))
if AudioManager.sharedInstance.currentTrack?.id == audioTrack.id {
musicIndicator.isHidden = false
musicIndicator.state = AudioManager.sharedInstance.isPlaying() ? .playing : .paused
} else {
musicIndicator.isHidden = true
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI () {
contentView.addSubview(coverView)
contentView.addSubview(titleLabel)
contentView.addSubview(detailLabel)
contentView.addSubview(moreButtin)
contentView.addSubview(musicIndicator)
contentView.addSubview(lineView)
coverView.snp.makeConstraints { make in
make.left.equalTo(contentView).offset(18)
make.top.equalTo(contentView).offset(15)
make.size.equalTo(CGSize.init(width: 64, height: 64))
make.bottom.equalTo(contentView).offset(-23)
}
moreButtin.snp.makeConstraints { make in
make.right.equalTo(contentView).offset(-18)
make.centerY.equalTo(coverView)
}
musicIndicator.snp.makeConstraints { make in
make.right.equalTo(moreButtin.snp.left).offset(-18)
make.centerY.equalTo(coverView)
make.size.equalTo(CGSize.init(width: 24, height: 24))
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(coverView.snp.right).offset(15)
make.top.equalTo(coverView).offset(10)
make.right.equalTo(moreButtin.snp.left).offset(-10)
}
detailLabel.snp.makeConstraints { make in
make.left.equalTo(coverView.snp.right).offset(15)
make.top.equalTo(titleLabel.snp.bottom).offset(10)
make.right.equalTo(musicIndicator.snp.left).offset(-6)
}
lineView.snp.makeConstraints { make in
make.left.equalTo(contentView).offset(18)
make.right.equalTo(contentView).offset(-18)
make.bottom.equalTo(contentView)
make.height.equalTo(1)
}
}
@objc func menuButtinClick() {
if let buttonTapCallback = self.buttonTapCallback,
let audioTrack = self.audioTrack {
buttonTapCallback(audioTrack)
}
}
}

@ -14,58 +14,47 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let viewDidLoad: Observable<Void>
let selection: Driver<IndexPath>
let modelSelected: Driver<JournalItem>
let notiPlayAudioTrack: Observable<Notification>
// let dropButtonTrigger: Driver<Void>
// let playButtonTrigger: Driver<Void>
// let shareButtonTrigger: Driver<Void>
// let moreButtonTrigger: Driver<AudioTrack>
}
struct Output {
let items: BehaviorRelay<[JournalSection]>
let selection: Driver<IndexPath>
let itemSelected: PublishSubject<JournalItem>
let modelSelected: Driver<JournalItem>
let journalDetail: PublishSubject<Journal>
let journal: BehaviorRelay<Journal>
let currentPlaying: PublishSubject<AudioTrack>
let dowloadState: Driver<Float>
let isLike: BehaviorRelay<Bool>
let isExpand: BehaviorRelay<Bool>
let toShare: PublishSubject<Void>
}
let itemSelected = PublishSubject<JournalItem>()
// let items = BehaviorRelay<[JournalSection]>.init(value: [])
let items = BehaviorRelay<[JournalSection]>.init(value: [])
var journal: Journal
var headerHeight: CGFloat = 506 + BaseDimensions.topHeight
let isExpand = BehaviorRelay<Bool>.init(value: false)
var journal: BehaviorRelay<Journal>
let playButtonTrigger = PublishSubject<Void>()
let downloadButtonTapped = PublishSubject<Void>()
let likeButtonTapped = PublishSubject<Void>()
let shareButtonTrigger = PublishSubject<Void>()
let playButtonTrigger = PublishRelay<Void>()
let downloadButtonTapped = PublishRelay<Void>()
let likeButtonTapped = PublishRelay<Void>()
let shareButtonTrigger = PublishRelay<Void>()
let toShare: PublishSubject<Void> = .init()
let item: BehaviorRelay<AudioTrack?> = .init(value: nil)
init(journal: Journal, provider: IndieMusicAPI) {
init(journal: BehaviorRelay<Journal>, provider: IndieMusicAPI) {
self.journal = journal
super.init(provider: provider)
}
func transform(input: Input) -> Output {
let elements = BehaviorRelay<[JournalSection]>(value: [])
let journal = PublishSubject<Journal>.init()
let journal = PublishRelay<Journal>.init()
let currentPlaying: PublishSubject<AudioTrack> = .init()
let dowloadState: Driver<Float> = .just(0)
@ -80,7 +69,7 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
input.viewDidLoad.subscribe { [weak self] _ in
guard let self = self else { return }
guard let journalNo = self.journal.journalNo else { return }
guard let journalNo = self.journal.value.journalNo else { return }
//
var audioSection: JournalSection?
@ -95,7 +84,7 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
if let journal = journalSection {
newSections.append(journal)
}
elements.accept(newSections)
self.items.accept(newSections)
}
//
@ -104,105 +93,66 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
guard let self = self else { return }
let audioArray = audioTrackArray.map { JournalItem.audioItem(model: $0) }
self.journal.trackCount = audioArray.count
isLike.accept(self.journal.haveCollect ?? false)
audioSection = JournalSection.audio(header: self.journal, items: audioArray)
let header = AudioSection.init(count: audioArray.count, dowloadState: false, isLike: false)
audioSection = JournalSection.audioSection(header: header, items: audioArray)
updateElements()
}, onError: { error in
//
}).disposed(by: self.rx.disposeBag)
//
self.requestJournalRecommend(journalID: self.journal.id)
self.requestJournalRecommend(journalID: self.journal.value.id)
.subscribe(onNext: { [weak self] journals in
guard let self = self else { return }
let journalArray = journals.map { JournalItem.journaItem(model: $0) }
journalSection = JournalSection.journal(header: nil, items: journalArray)
// let journalArray = journals.map { }
journalSection = JournalSection.journalSection(header: "期刊推荐", items: [JournalItem.journaItem(items: journals)])
updateElements()
}, onError: { error in
//
}).disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
input.selection.drive { [weak self] indexPath in
guard let self = self else { return }
let sectionItem = elements.value[indexPath.section].items[indexPath.row]
self.itemSelected.onNext(sectionItem)
}.disposed(by: rx.disposeBag)
isExpand.subscribe { [weak self] isExpand in
guard let self = self else { return }
var new = elements.value
if var header = new.first?.header {
header.isExpand = isExpand
if let items = new.first?.items {
new[0] = .audio(header: header, items: items)
elements.accept(new)
}
}
}.disposed(by: rx.disposeBag)
input.notiPlayAudioTrack.subscribe { [weak self] noti in
guard let track = noti.element?.object as? AudioTrack else { return }
var new = elements.value
if var header = new.first?.header {
if let items = new.first?.items {
new[0] = .audio(header: header, items: items)
elements.accept(new)
}
}
// guard let track = noti.element?.object as? AudioTrack else { return }
// var new = items.value
// if var header = new.first?.header {
// if let items = new.first?.items {
// new[0] = .audio(header: header, items: items)
// items.accept(new)
// }
// }
}.disposed(by: rx.disposeBag)
likeButtonTapped.subscribe { [weak self] _ in
guard let self = self else { return }
guard let self = self, let isLike = self.journal.value.haveCollect else { return }
if isLike.value {
self.requestCancelLike(journalNo: self.journal.id)
if isLike {
self.requestCancelLike(journalNo: self.journal.value.id)
.subscribe { [weak self] _ in
var new = elements.value
if var header = new.first?.header {
header.haveCollect = false
if let items = new.first?.items {
new[0] = .audio(header: header, items: items)
elements.accept(new)
}
}
isLike.accept(false)
guard var new = self?.journal.value else { return }
new.haveCollect = false
self?.journal.accept(new)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
} else {
self.requestLike(journalNo: self.journal.id)
self.requestLike(journalNo: self.journal.value.id)
.subscribe { [weak self] _ in
guard var new = self?.journal.value else { return }
new.haveCollect = true
var new = elements.value
if var header = new.first?.header {
header.haveCollect = true
if let items = new.first?.items {
new[0] = .audio(header: header, items: items)
elements.accept(new)
}
}
isLike.accept(true)
self?.journal.accept(new)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
@ -224,50 +174,37 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
item.subscribe { [weak self] audioTrack in
guard let audioTrack = audioTrack.element else { return }
var new = elements.value
if var header = new.first?.header {
header.haveCollect = false
if let items = new.first?.items {
new[0] = .audio(header: header, items: items)
elements.accept(new)
}
}
//
// var new = items.value
// if var header = new.first?.header {
// header.haveCollect = false
//
// if let items = new.first?.items {
// new[0] = .audio(header: header, items: items)
// items.accept(new)
// }
// }
}.disposed(by: rx.disposeBag)
// input.dropButtonTrigger.drive { _ in
// self.isExpand.accept(!self.isExpand.value)
// }.disposed(by: rx.disposeBag)
//
// input.playButtonTrigger.drive { _ in
//
// }.disposed(by: rx.disposeBag)
//
// input.shareButtonTrigger.drive { _ in
//
// }.disposed(by: rx.disposeBag)
return Output.init(items: elements,
selection: input.selection,
itemSelected: itemSelected,
journalDetail: journal,
return Output.init(items: items,
modelSelected: input.modelSelected,
journal: self.journal,
currentPlaying: currentPlaying,
dowloadState: dowloadState,
isLike: isLike,
isExpand: isExpand,
toShare: toShare
)
}
func sectionType(for sectionIndex: Int) -> JournalItem? {
guard sectionIndex < self.items.value.count else { return nil }
let section = items.value[sectionIndex]
// item
//
return section.items.first
}
func requestMusic(journalNo: String) -> Observable<[AudioTrack]> {
@ -276,9 +213,6 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
.trackError(error)
}
func updateHeaderHeight(newHeight: CGFloat) {
headerHeight = newHeight
}
func requestLike(journalNo: String) -> Observable<Void> {

@ -102,9 +102,9 @@ class MineJournalViewController: ViewController {
output.itemSelected.subscribe { [weak self] journal in
guard let journal = journal.element else { return }
let value = BehaviorRelay<Journal>.init(value: journal)
let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
let journalDetailViewModel = JournalDetailViewModel.init(journal: value, provider: viewModel.provider)
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
}.disposed(by: rx.disposeBag)

@ -31,7 +31,7 @@ class PersonalViewController: ViewController {
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.register(JournalAudioViewCell.self, forCellWithReuseIdentifier: "JournalAudioViewCell")
collectionView.register(JournalAudioCollectionViewCell.self, forCellWithReuseIdentifier: "JournalAudioCollectionViewCell")
collectionView.register(PersonalHeaderView.self, forSupplementaryViewOfKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "PersonalHeaderView")
@ -133,7 +133,7 @@ class PersonalViewController: ViewController {
configureCell: { dataSource, collectionView, indexPath, item in
switch dataSource[indexPath] {
case .audioItem(let audioTrack):
let cell: JournalAudioViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
let cell: JournalAudioCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioCollectionViewCell", for: indexPath) as! JournalAudioCollectionViewCell
cell.audioTrack = audioTrack
cell.buttonTapCallback = { [weak self] audioTrack in

@ -41,7 +41,7 @@ class AudioTrackListViewModel: ViewModel, ViewModelType {
let arr = playlist.map { audioTrack in
return JournalItem.audioItem(model: audioTrack)
}
self.items.accept([JournalSection.audio(header: nil, items: arr)])
self.items.accept([JournalSection.audioSection(header: nil, items: arr)])
}
}.disposed(by: rx.disposeBag)

@ -14,7 +14,7 @@ class MusicStyleViewController: ViewController {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let viewModel = self.viewModel as? JournalDetailViewModel
let viewModel = self.viewModel as? MusicStyleViewModel
let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147))
let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize)
@ -152,8 +152,10 @@ class MusicStyleViewController: ViewController {
output.modelSelected.drive { [weak self] journal in
let value = BehaviorRelay<Journal>.init(value: journal)
let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
let journalDetailViewModel = JournalDetailViewModel.init(journal: value, provider: viewModel.provider)
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
@ -397,79 +399,3 @@ class MusicStyleHeaderView: UICollectionReusableView {
}
}
class JournalViewCell: UICollectionViewCell {
let titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.layer.cornerRadius = 8
titleImageView.layer.masksToBounds = true
titleImageView.backgroundColor = .backgroundColor()
return titleImageView
}()
let volLabel: UILabel = {
let volLabel = UILabel.init()
volLabel.font = UIFont.systemFont(ofSize: 12)
volLabel.textColor = .init(hex: 0x000000, alpha: 0.4)
return volLabel
}()
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .medium)
return titleLabel
}()
var journal: Journal? {
didSet {
guard let journal = journal else { return }
titleImageView.kf.setImage(with: URL.init(string: journal.image ?? ""))
volLabel.text = "VOL·\(journal.journalNo ?? "")"
titleLabel.text = journal.title
}
}
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
contentView.addSubview(titleImageView)
contentView.addSubview(volLabel)
contentView.addSubview(titleLabel)
titleImageView.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.right.equalTo(contentView)
make.top.equalTo(contentView)
make.height.equalTo(100)
}
volLabel.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.top.equalTo(titleImageView.snp.bottom).offset(9)
make.right.equalTo(contentView)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(contentView)
make.right.equalTo(contentView)
make.top.equalTo(volLabel.snp.bottom).offset(2)
}
}
}

@ -45,7 +45,7 @@ class SearchResultsController: ViewController {
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.register(JournalAudioViewCell.self, forCellWithReuseIdentifier: "JournalAudioViewCell")
collectionView.register(JournalAudioCollectionViewCell.self, forCellWithReuseIdentifier: "JournalAudioCollectionViewCell")
collectionView.backgroundColor = .white
@ -144,7 +144,10 @@ class SearchResultsController: ViewController {
}
case .journal(let journal):
let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider)
let value = BehaviorRelay<Journal>.init(value: journal)
let journalDetailViewModel = JournalDetailViewModel.init(journal: value, provider: viewModel.provider)
self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self)
@ -194,7 +197,7 @@ extension SearchResultsController {
switch dataSource[indexPath] {
case .single(let audioTrack):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioCollectionViewCell", for: indexPath) as! JournalAudioCollectionViewCell
cell.audioTrack = audioTrack
return cell

@ -36,7 +36,7 @@ enum APIConfig {
case messageList(Int, Int)
case collectSongList([String: Any])
case journalCollectList(String, Int, Int)
case journalCollectList([String: Any])
case followingList(String, Int, Int)
case followerList(String, Int, Int)
case blackList(Int, Int)
@ -106,8 +106,8 @@ extension APIConfig: TargetType {
return "luoo-user/userMessage/list/\(page)/\(size)"
case .collectSongList:
return "luoo-music/song/collect"
case .journalCollectList(let userId, let page, let size):
return "luoo-music/journal/collect/\(userId)/\(page)/\(size)"
case .journalCollectList:
return "luoo-music/journal/collect"
case .followingList(let userId, let page, let size):
return "luoo-user/my/follows/\(userId)/\(page)/\(size)"
case .followerList(let userId, let page, let size):
@ -178,10 +178,10 @@ extension APIConfig: TargetType {
var task: Task {
var parameters: [String: Any] = [:]
switch self {
case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack, .myThumbupList, .myCommentReplyList:
case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack, .myThumbupList, .myCommentReplyList:
return .requestPlain
case .login(let dic), .journalList(let dic), .sendsms(let dic), .autoLogin(let dic), .editUserInfo(let dic), .like(let dic), .cancelLike(let dic), .logout(let dic), .checkVersion(let dic), .commentLike(_, let dic), .sendComment(let dic), .collectSongList(let dic):
case .login(let dic), .journalList(let dic), .sendsms(let dic), .autoLogin(let dic), .editUserInfo(let dic), .like(let dic), .cancelLike(let dic), .logout(let dic), .checkVersion(let dic), .commentLike(_, let dic), .sendComment(let dic), .collectSongList(let dic), .journalCollectList(let dic):
parameters = dic
return .requestParameters(parameters: parameters, encoding: parameterEncoding)

@ -216,7 +216,12 @@ extension RestApi {
}
func journalCollectList(userId: String, page: Int, size: Int) -> Single<[Journal]> {
return requestObject(.journalCollectList(userId, page, size), with: "data.rows", type: [Journal].self)
let dic = ["userId": userId,
"pageNum": page,
"pageSize": size
] as [String : Any]
return requestObject(.journalCollectList(dic), with: "data.rows", type: [Journal].self)
}
func followingList(userId: String, page: Int, size: Int) -> Single<[User]> {

@ -28,6 +28,7 @@ target 'IndieMusic' do
pod 'IQKeyboardManagerSwift'
pod 'lottie-ios'
pod 'MarqueeLabel'
pod 'RollingNotice-Swift'
pod 'PINCache'
pod 'DeviceKit', '~> 5.2'
pod 'FSPopoverView'

Loading…
Cancel
Save