Play module modification

dev
wenlei 9 months ago
parent 2bdc1bdea7
commit 8738905797

@ -87,6 +87,7 @@
77620D942B68E65300798861 /* EditDateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77620D932B68E65300798861 /* EditDateViewController.swift */; };
77620D962B68E96C00798861 /* DateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77620D952B68E96C00798861 /* DateItem.swift */; };
77620D9A2B69DA1A00798861 /* EditSignatureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77620D992B69DA1A00798861 /* EditSignatureViewController.swift */; };
776A1F762B7A18F000F613EB /* ChinaLocalizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 776A1F782B7A18F000F613EB /* ChinaLocalizable.strings */; };
778638942B4D123D00B00AF9 /* CommentDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778638932B4D123D00B00AF9 /* CommentDetailViewModel.swift */; };
778B8A212AF8E36D0034AFD4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778B8A202AF8E36D0034AFD4 /* AppDelegate.swift */; };
778B8A232AF8E36D0034AFD4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778B8A222AF8E36D0034AFD4 /* SceneDelegate.swift */; };
@ -325,6 +326,10 @@
77620D932B68E65300798861 /* EditDateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditDateViewController.swift; sourceTree = "<group>"; };
77620D952B68E96C00798861 /* DateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateItem.swift; sourceTree = "<group>"; };
77620D992B69DA1A00798861 /* EditSignatureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSignatureViewController.swift; sourceTree = "<group>"; };
776A1F742B7A184F00F613EB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = "<group>"; };
776A1F752B7A184F00F613EB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
776A1F772B7A18F000F613EB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ChinaLocalizable.strings"; sourceTree = "<group>"; };
776A1F7A2B7A192000F613EB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ChinaLocalizable.strings; sourceTree = "<group>"; };
778638932B4D123D00B00AF9 /* CommentDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailViewModel.swift; sourceTree = "<group>"; };
778B8A1D2AF8E36D0034AFD4 /* IndieMusic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IndieMusic.app; sourceTree = BUILT_PRODUCTS_DIR; };
778B8A202AF8E36D0034AFD4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -605,6 +610,14 @@
path = EditInfo;
sourceTree = "<group>";
};
776A1F712B7A165500F613EB /* Localizable */ = {
isa = PBXGroup;
children = (
776A1F782B7A18F000F613EB /* ChinaLocalizable.strings */,
);
path = Localizable;
sourceTree = "<group>";
};
778B8A142AF8E36D0034AFD4 = {
isa = PBXGroup;
children = (
@ -664,6 +677,7 @@
778B8A512AF8EA2A0034AFD4 /* Resources */ = {
isa = PBXGroup;
children = (
776A1F712B7A165500F613EB /* Localizable */,
77620D7D2B67332600798861 /* font */,
775100A12B63442900F46109 /* Json */,
778B8A292AF8E36E0034AFD4 /* Assets.xcassets */,
@ -1131,6 +1145,7 @@
knownRegions = (
en,
Base,
"zh-Hans",
);
mainGroup = 778B8A142AF8E36D0034AFD4;
productRefGroup = 778B8A1E2AF8E36D0034AFD4 /* Products */;
@ -1149,6 +1164,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
776A1F762B7A18F000F613EB /* ChinaLocalizable.strings in Resources */,
775100A42B6344C700F46109 /* img_0.png in Resources */,
77620D832B67332600798861 /* Alibaba PuHuiTi 2.0.ttf in Resources */,
778B8A2D2AF8E36E0034AFD4 /* LaunchScreen.storyboard in Resources */,
@ -1520,10 +1536,20 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
776A1F782B7A18F000F613EB /* ChinaLocalizable.strings */ = {
isa = PBXVariantGroup;
children = (
776A1F772B7A18F000F613EB /* zh-Hans */,
776A1F7A2B7A192000F613EB /* en */,
);
name = ChinaLocalizable.strings;
sourceTree = "<group>";
};
778B8A262AF8E36D0034AFD4 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
778B8A272AF8E36D0034AFD4 /* Base */,
776A1F742B7A184F00F613EB /* zh-Hans */,
);
name = Main.storyboard;
sourceTree = "<group>";
@ -1532,6 +1558,7 @@
isa = PBXVariantGroup;
children = (
778B8A2C2AF8E36E0034AFD4 /* Base */,
776A1F752B7A184F00F613EB /* zh-Hans */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
@ -1544,6 +1571,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -1591,7 +1619,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@ -1607,6 +1635,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -1648,7 +1677,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;

@ -277,7 +277,7 @@ class Navigator {
DispatchQueue.main.async {
let nav = NavigationController(rootViewController: target)
nav.setNavigationBarHidden(true, animated: true)
nav.isPullToDismissEnabled = true
// nav.isPullToDismissEnabled = true
nav.modalPresentationStyle = .custom
nav.modalPresentationCapturesStatusBarAppearance = true

@ -12,6 +12,7 @@ class BlurEffectView: UIView {
var blurView: UIVisualEffectView = {
let blurEffect = UIBlurEffect(style: .systemThickMaterialDark)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.isUserInteractionEnabled = true
return blurView
}()

@ -49,13 +49,13 @@ class TableViewController: ViewController, UIScrollViewDelegate {
}
tableView.rx.refresh.subscribe { refreshType in
tableView.rx.refresh.subscribe { [weak self] refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
self?.headerRefreshTrigger.onNext(())
case .loadMore where AuthManager.shared.token?.isValid == true:
self.footerRefreshTrigger.onNext(())
self?.footerRefreshTrigger.onNext(())
default: break
}

@ -8,11 +8,18 @@
import Foundation
import RxDataSources
struct AudioMoreAction: Codable {
struct AudioMoreAction: Codable, IdentifiableType, Equatable {
let icon: String
let title: String
let detail: String
var identity: String {
return title
}
static func == (lhs: AudioMoreAction, rhs: AudioMoreAction) -> Bool {
return lhs.title == rhs.title
}
}
struct AudioMoreActionSection {

@ -6,9 +6,10 @@
//
import Foundation
import RxDataSources
struct AudioTrack: Codable {
let id: String?
struct AudioTrack: Codable, IdentifiableType, Equatable {
let id: String
let title: String?
let artist: String?
let album: String?
@ -22,20 +23,14 @@ struct AudioTrack: Codable {
var isPlaying: Bool?
// enum CodingKeys: String, CodingKey {
// case album
// case artists
// case availableMarkets = "available_markets"
// case discNumber = "disc_number"
// case durationMs = "duration_ms"
// case explicit
// case externalUrls = "external_urls"
// case id
// case name
// case previewUrl = "preview_url"
//
// case isPlaying
// }
var identity: String {
return id
}
static func == (lhs: AudioTrack, rhs: AudioTrack) -> Bool {
return lhs.id == rhs.id
}
}
struct TracksResponse: Codable {

@ -8,22 +8,12 @@
import Foundation
import RxDataSources
struct MyCommentList {
let avatar: String
let audioTrackImage: String
let name: String
let date: Int
let comment: String
let myComment: String
}
struct MyCommentListSection {
var items: [MyCommentList]
var items: [Comment]
}
extension MyCommentListSection: SectionModelType {
typealias Item = MyCommentList
typealias Item = Comment
init(original: MyCommentListSection, items: [Item]) {
self = original
@ -50,7 +40,7 @@ enum CommentListType {
struct Comment: Codable {
struct Comment: Codable, IdentifiableType, Equatable {
let journalId: String?
let avatar: String?
let state: Int?
@ -58,15 +48,18 @@ struct Comment: Codable {
let content: String?
let commentCount: Int?
let nickName: String?
// let publishTime: Int?
let publishTime: String?
let location: String?
let parentId: String?
let id: String?
let thumbupCount: Int?
let id: String
let thumbupCountString: String?
let journalImage: String?
let haveThumbup: Bool?
//
var parentCommentCount: Int?
private enum CodingKeys: String, CodingKey {
case journalId
case avatar
@ -75,14 +68,25 @@ struct Comment: Codable {
case content
case commentCount
case nickName
// case publishTime
case publishTime
case location
case parentId
case id = "_id"
case thumbupCount
case thumbupCountString
case journalImage
case haveThumbup
case parentCommentCount
}
var identity: String {
return id
}
static func == (lhs: Comment, rhs: Comment) -> Bool {
return lhs.id == rhs.id
}
}
@ -103,3 +107,9 @@ extension CommentSection: SectionModelType {
self.items = items
}
}
struct CommentThumbState: Codable {
let thumbState: Bool
}

@ -10,21 +10,8 @@ import RxSwift
import RxCocoa
import RxDataSources
struct Journal: Codable {
// let id: String?
// let journalNo: String?
// let title: String?
// let image: String?
// let tags: [String]?
// let content: String?
// let editor: String?
// let date: String?
// var haveCollect: Bool?
// let ipLocation: String?
//
// let commentArray: [String]?
let id: String?
struct Journal: Codable, IdentifiableType, Equatable {
let id: String
var haveCollect: Bool?
let commentList: [Comment]?
let tags: [String]?
@ -37,10 +24,18 @@ struct Journal: Codable {
let title: String?
let ipLocation: String?
var isExpand: Bool?
var trackCount: Int?
var identity: String {
return id
}
static func == (lhs: Journal, rhs: Journal) -> Bool {
return lhs.id == rhs.id && lhs.haveCollect == rhs.haveCollect
}
}

@ -8,19 +8,12 @@
import Foundation
import RxDataSources
struct Like {
let avatar: String
let name: String
let content: String
let date: Int
}
struct LikeSection {
var items: [Like]
var items: [MineThumbup]
}
extension LikeSection: SectionModelType {
typealias Item = Like
typealias Item = MineThumbup
init(original: LikeSection, items: [Item]) {
self = original

@ -13,9 +13,48 @@ enum MessageType: Codable {
case activities
}
enum CustomMessageType: Codable {
case comment(String)
case like(String)
case follow(String)
var description: String {
switch self {
case .comment:
return "评论"
case .like:
return "点赞"
case .follow:
return "关注"
}
}
var image: String {
switch self {
case .comment:
return "message_comment_icon"
case .like:
return "message_like_icon"
case .follow:
return "message_follow_icon"
}
}
var detail: String {
switch self {
case .comment(let user):
return "\(user) 回复了你的评论"
case .like(let user):
return "\(user) 点赞了你的评论"
case .follow(let user):
return "\(user) 关注了你"
}
}
}
struct Message: Codable {
struct Message: Codable, IdentifiableType, Equatable {
let sendTime: String?
let title: String?
let sendUserAvatar: String?
@ -23,13 +62,49 @@ struct Message: Codable {
let type: Int?
let content: String?
let userId: String?
let messageId: String?
let messageId: String
let sendUserNickName: String?
let haveRead: Int?
let messageType: MessageType?
init(sendTime: String? = nil,
title: String? = nil,
sendUserAvatar: String? = nil,
sendUserId: String? = nil,
type: Int? = nil,
content: String? = nil,
userId: String? = nil,
messageId: String = "",
sendUserNickName: String? = nil,
haveRead: Int? = nil,
messageType: MessageType? = nil) {
self.sendTime = sendTime
self.title = title
self.sendUserAvatar = sendUserAvatar
self.sendUserId = sendUserId
self.type = type
self.content = content
self.userId = userId
self.messageId = messageId
self.sendUserNickName = sendUserNickName
self.haveRead = haveRead
self.messageType = messageType
}
var identity: String {
return messageId
}
static func == (lhs: Message, rhs: Message) -> Bool {
return lhs.messageId == rhs.messageId
}
}
@ -41,9 +116,9 @@ enum MessageSection {
}
enum MessageSectionItem {
case customMessage(customMessageType: CustomMessageType)
case messageItem(model: Message)
case activitiesItem(model: Message)
}

@ -8,11 +8,19 @@
import Foundation
import RxDataSources
struct Mine: Codable {
struct Mine: Codable, IdentifiableType, Equatable {
let icon: String
let title: String
let detail: String
let isShowPlay: Bool
var identity: String {
return title
}
static func == (lhs: Mine, rhs: Mine) -> Bool {
return lhs.title == rhs.title
}
}
struct MineSection {
@ -88,3 +96,29 @@ extension MineDownloadSection: SectionModelType {
self.items = items
}
}
struct MineThumbup: Codable {
let id: String?
let type: Int?
let likedItemId: String?
let thumbupAt: String?
let commentContent: String?
let avatar: String?
let nickName: String?
let userId: String?
let createTime: String?
private enum CodingKeys: String, CodingKey {
case id = "_id"
case type
case likedItemId
case thumbupAt
case commentContent
case avatar
case nickName
case userId
case createTime
}
}

@ -60,8 +60,18 @@ extension PlayerShuffleType {
}
struct PlayerLyrics: Codable {
let lyrics: String?
struct PlayerLyrics: Codable, IdentifiableType, Equatable {
let lyrics: String
var identity: String {
return lyrics
}
static func == (lhs: PlayerLyrics, rhs: PlayerLyrics) -> Bool {
return lhs.lyrics == rhs.lyrics
}
}

@ -8,10 +8,18 @@
import Foundation
import RxDataSources
struct Setting: Codable {
struct Setting: Codable, IdentifiableType, Equatable {
let title: String
let detail: String
let arrowIcon: String
var identity: String {
return title
}
static func == (lhs: Setting, rhs: Setting) -> Bool {
return lhs.title == rhs.title
}
}
enum SettingType {

@ -6,6 +6,7 @@
//
import Foundation
import RxDataSources
enum UserRelationStatus: Int, Codable {
case no = 0
@ -45,14 +46,14 @@ enum SexType: Int, Codable {
}
struct User: Codable {
struct User: Codable, IdentifiableType, Equatable {
let avatar: String?
let badge: String?
let birthDay: String?
let commentReplyCount: Int?
let fansCount: Int?
let followCount: Int?
let id: String?
let id: String
let ipLocation: String?
let journalCount: Int?
let nickName: String?
@ -61,4 +62,12 @@ struct User: Codable {
let signature: String?
let songCount: Int?
let thumbUpCount: Int?
var identity: String {
return id
}
static func == (lhs: User, rhs: User) -> Bool {
return lhs.id == rhs.id
}
}

@ -42,7 +42,7 @@ class FilterViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
self.fetchFilterData().subscribe { filterMenu in
self.fetchFilterData().subscribe { [weak self] filterMenu in
let journalNoList = filterMenu.journalNoList?.map({ journalNo in
@ -54,10 +54,10 @@ class FilterViewModel: ViewModel, ViewModelType {
let selection1 = FilterSection.style(header: "语言", items: filterMenu.languageList ?? [])
let selection2 = FilterSection.journalNo(header: "期刊", items: journalNoList ?? [])
self.items.accept([selection0, selection1, selection2])
self?.items.accept([selection0, selection1, selection2])
if let filter = self.filter.value {
self.selectItem(selectedFilter: filter)
if let filter = self?.filter.value {
self?.selectItem(selectedFilter: filter)
}
} onError: { error in

@ -169,7 +169,7 @@ class HomeTabBarController: UITabBarController, Navigatable {
output.tabBarItems.delay(.milliseconds(50)).drive(onNext: { [weak self] (tabBarItems) in
if let strongSelf = self {
let controllers = tabBarItems.map { $0.getController(with: viewModel.viewModel(for: $0), navigator: strongSelf.navigator) }
strongSelf.showAllBar(true, animated: false)
// strongSelf.showAllBar(true, animated: false)
strongSelf.setViewControllers(controllers, animated: false)
}
}).disposed(by: rx.disposeBag)
@ -185,14 +185,18 @@ class HomeTabBarController: UITabBarController, Navigatable {
playerTabBar.listButton.rx.tap.subscribe { _ in
playerTabBar.listButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
let audioTrackListViewModel = AudioTrackListViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .audioTrackList(viewModel: audioTrackListViewModel), sender: self, transition: .navigationPresent(type: .audioTrackList))
}.disposed(by: rx.disposeBag)
playerTabBar.rx.controlEvent(.touchUpInside).subscribe { _ in
playerTabBar.rx.controlEvent(.touchUpInside).subscribe { [weak self] _ in
guard let self = self else { return }
guard let track = AudioManager.sharedInstance.currentTrack else { return }
let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider)
@ -216,8 +220,8 @@ class HomeTabBarController: UITabBarController, Navigatable {
}.disposed(by: rx.disposeBag)
output.progress.subscribe { progress in
self.playerTabBar.progressView.progress = progress
output.progress.subscribe { [weak self] progress in
self?.playerTabBar.progressView.progress = progress
}.disposed(by: rx.disposeBag)
@ -291,13 +295,8 @@ class HomeTabBarController: UITabBarController, Navigatable {
animationDuration = 0
}
UIView.animate(withDuration: animationDuration,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.25,
options: .curveEaseInOut,
animations: {
UIView.animate(withDuration: animationDuration) {
if isShowing {
self.customeTabBar.alpha = 1
self.playerTabBar.alpha = 1
@ -309,7 +308,8 @@ class HomeTabBarController: UITabBarController, Navigatable {
}
self.view.layoutIfNeeded()
}) { (completed) in
} completion: { completed in
self.isTabBarShowing = isShowing
self.isPlayerBarShowing = isShowing
self.customeTabBar.isHidden = !isShowing
@ -319,7 +319,19 @@ class HomeTabBarController: UITabBarController, Navigatable {
self.isAnimating = false
}
// UIView.animate(withDuration: animationDuration,
// delay: 0,
// usingSpringWithDamping: 0.7,
// initialSpringVelocity: 0,
// options: .curveEaseInOut,
// animations: {
//
// }) { (completed) in
// }
}
open func showTabBar(_ isShowing: Bool, animated: Bool) {

@ -114,7 +114,8 @@ class HomeTabBarViewModel: ViewModel, ViewModelType {
let appVersion = infoDic?["CFBundleShortVersionString"] ?? ""
guard let appVersion = infoDic?["CFBundleShortVersionString"] as? String else { return }
self.provider.checkVersion(platform: "0", deviceId: AuthManager.shared.getUUID(), appVersion: appVersion).subscribe { _ in
self.provider.checkVersion(platform: "0", deviceId: AuthManager.shared.getUUID(), appVersion: appVersion).subscribe { [weak self] _ in
guard let self = self else { return }
self.showUpdateAlert.accept(())
} onFailure: { error in

@ -131,15 +131,16 @@ class HomeViewController: TableViewController {
}.disposed(by: rx.disposeBag)
output.footerState.subscribe { footerState in
self.tableView.mj_footer?.rx.refreshFooterState.onNext(footerState)
self.tableView.reloadData()
output.footerState.subscribe { [weak self] footerState in
self?.tableView.mj_footer?.rx.refreshFooterState.onNext(footerState)
self?.tableView.reloadData()
}.disposed(by: rx.disposeBag)
output.carouselItems.subscribe { carouselItems in
output.carouselItems.subscribe { [weak self] carouselItems in
guard let carouselItems = carouselItems.element else { return }
guard let self = self else { return }
self.carouselItems = carouselItems
self.homePagerView.pageControl.numberOfPages = self.carouselItems.count
@ -153,12 +154,12 @@ class HomeViewController: TableViewController {
}.disposed(by: rx.disposeBag)
output.randomAudioTrack.subscribe { audioTrackArray in
output.randomAudioTrack.subscribe { [weak self] audioTrackArray in
guard let audioTrackArray = audioTrackArray.element,
let audioTrack = audioTrackArray.first else { return }
let playerViewModel = PlayerViewModel.init(track: audioTrack, provider: viewModel.provider)
self.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
self?.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
//
@ -170,12 +171,10 @@ class HomeViewController: TableViewController {
viewModel.filter.subscribe { filter in
viewModel.filter.subscribe { [weak self] filter in
guard let filter = filter.element else { return }
self.headerView?.filterButton.filter = filter
self?.headerView?.filterButton.filter = filter
}.disposed(by: rx.disposeBag)
@ -189,9 +188,14 @@ class HomeViewController: TableViewController {
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(view).offset(-BaseDimensions.statusBarHeight)
if AudioManager.sharedInstance.playlist?.count ?? 0 > 0 {
make.bottom.equalTo(view).offset(-66)
} else {
make.bottom.equalTo(view)
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

@ -110,8 +110,12 @@ class HomeViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
input.randomAudioTrackTrigger.subscribe { _ in
self.requestRandomAudioTrack().subscribe { audioTrackArray in
input.randomAudioTrackTrigger.subscribe { [weak self] _ in
guard let self = self else { return }
self.requestRandomAudioTrack().subscribe { [weak self] audioTrackArray in
guard let self = self else { return }
self.randomAudioTrack.accept(audioTrackArray)
} onError: { error in
@ -120,7 +124,8 @@ class HomeViewModel: ViewModel, ViewModelType {
filter.subscribe { filter in
filter.subscribe { [weak self] filter in
guard let self = self else { return }
guard let filter = filter.element else { return }
self.page = 1

@ -82,11 +82,9 @@ class AudioMoreActionController: ViewController, UIScrollViewDelegate {
}.disposed(by: rx.disposeBag)
output.audioTrack.subscribe { audioTrack in
output.audioTrack.subscribe { [weak self] audioTrack in
guard let self = self else { return }
self.audioMoreActionView.audioTrack = audioTrack
}.disposed(by: rx.disposeBag)

@ -78,7 +78,9 @@ class AudioMoreActionViewModel: ViewModel, ViewModelType {
return (indexPath, audioTrack)
}
.subscribe(onNext: { (indexPath, audioTrack) in
.subscribe(onNext: { [weak self] (indexPath, audioTrack) in
guard let self = self else { return }
switch indexPath.row {
case 0://

@ -150,7 +150,10 @@ class CommentViewCell: UITableViewCell {
commentLabel.text = comment.content
likeButton.setTitle("\(comment.thumbupCount ?? 0)", for: .normal)
likeButton.setTitle("\(comment.thumbupCountString ?? "0")", for: .normal)
likeButton.isSelected = comment.haveThumbup == true ? true : false
}
}

@ -108,14 +108,15 @@ class CommentViewController: ViewController {
}.disposed(by: self.rx.disposeBag)
cell.likeButton.rx.tap.subscribe { _ in
guard let commentID = comment.id else { return }
viewModel.likeSelected.onNext(commentID)
viewModel.likeSelected.onNext(comment.id)
}.disposed(by: self.rx.disposeBag)
cell.rx.longPressGesture().when(.recognized)
.subscribe { tap in
.subscribe { [weak self] tap in
guard let self = self else { return }
if comment.userId == UserDefaults.AccountInfo.string(forKey: .userID) {
self.menuView.items = self.setupMenuItems(features: [.copy, .report, .delete])
} else {

@ -51,7 +51,9 @@ class CommentViewModel: ViewModel, ViewModelType {
input.viewWillAppear.subscribe { _ in
self.requestHotCommentListData(journalID: self.journalID, page: self.page, size: 10)
.subscribe { commentArray in
.subscribe { [weak self] commentArray in
guard let self = self else { return }
self.handleReceivedComments(comments: commentArray)
} onError: { error in
@ -60,12 +62,14 @@ class CommentViewModel: ViewModel, ViewModelType {
input.currentCommentListType.subscribe { commentListType in
input.currentCommentListType.subscribe { [weak self] commentListType in
guard let self = self else { return }
switch commentListType {
case .hot:
self.requestHotCommentListData(journalID: self.journalID, page: self.page, size: 10)
.subscribe { comments in
.subscribe { [weak self] comments in
guard let self = self else { return }
self.handleReceivedComments(comments: comments)
@ -77,7 +81,8 @@ class CommentViewModel: ViewModel, ViewModelType {
case .latest:
self.requestLatestCommentListData(journalID: self.journalID, page: self.page, size: 10)
.subscribe { comments in
.subscribe { [weak self] comments in
guard let self = self else { return }
self.handleReceivedComments(comments: comments)
@ -111,7 +116,18 @@ class CommentViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
likeSelected.subscribe { _ in
likeSelected.subscribe { [weak self] commentID in
guard let self = self else { return }
self.requestCommentLike(commentID: commentID)
.subscribe(onNext: { commentThumbState in
}, onError: { error in
}).disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
@ -126,7 +142,7 @@ class CommentViewModel: ViewModel, ViewModelType {
self.sendCommentData(content: comment, journalId: self.journalID, parentId: nil, journalImage: nil)
.subscribe { comment in
//TODO cell
@ -152,9 +168,11 @@ class CommentViewModel: ViewModel, ViewModelType {
self.items.accept(commentSections)
for (index, commentType) in commentTypes.enumerated() {
if case let .comment(comment) = commentType, comment.commentCount != 0, let commentID = comment.id {
self.requestSubCommentData(parentId: commentID, page: 1, size: 1)
.subscribe { subCommentArray in
if case let .comment(comment) = commentType, comment.commentCount != 0 {
self.requestSubCommentData(parentId: comment.id, page: 1, size: 1)
.subscribe { [weak self] subCommentArray in
guard let self = self else { return }
self.handleReceivedSubComments(parentCommentCount: comment.commentCount ?? 0, subComments: subCommentArray, mainCommentIndex: index)
} onError: { error in
print(error)
@ -207,7 +225,7 @@ class CommentViewModel: ViewModel, ViewModelType {
func requestCommentLike(commentID: String) -> Observable<Void> {
func requestCommentLike(commentID: String) -> Observable<CommentThumbState> {
return self.provider.commentLike(commentId: commentID)
.trackError(error)
}

@ -171,7 +171,8 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
resuableView.journal = section?.header
resuableView.saveButton.rx.tap.subscribe { _ in
resuableView.saveButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
resuableView.titleImageView.image?.saveImageToPhotoLibrary()
.subscribe(onNext: { _ in
@ -333,24 +334,37 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
}.disposed(by: rx.disposeBag)
output.isLike.subscribe { isLike in
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 { tap in
guard let journalID = viewModel.journal.id else { return }
let commentViewModel = CommentViewModel.init(journalID: journalID, provider: viewModel.provider)
.subscribe { [weak self] tap in
guard let self = self else { return }
let commentViewModel = CommentViewModel.init(journalID: viewModel.journal.id, provider: viewModel.provider)
self.navigator.show(segue: .comment(viewModel: commentViewModel), sender: self)
}.disposed(by: rx.disposeBag)
}

@ -248,15 +248,16 @@ class JournalAudioHeaderView: UICollectionReusableView {
popoverView.dataSource = self
titleImageView.rx.tapGesture().when(.recognized)
.subscribe { tap in
.subscribe { [weak self] tap in
guard let self = self else { return }
self.popoverView.present(fromPoint: self.titleImageView.center, in: self, displayIn: self)
}.disposed(by: rx.disposeBag)
saveButton.rx.tap.subscribe { _ in
saveButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
self.popoverView.dismiss()
}.disposed(by: rx.disposeBag)

@ -72,13 +72,15 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
let isLike = BehaviorRelay<Bool>.init(value: false)
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { _ in
}.disposed(by: rx.disposeBag)
input.viewDidLoad.subscribe { (_) in
guard let journalNo = self.journal.journalNo, let journalID = self.journal.id else { return }
input.viewDidLoad.subscribe { [weak self] _ in
guard let self = self else { return }
guard let journalNo = self.journal.journalNo else { return }
//
var audioSection: JournalSection?
@ -98,7 +100,9 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
//
self.requestMusic(journalNo: journalNo)
.subscribe(onNext: { audioTrackArray in
.subscribe(onNext: { [weak self] audioTrackArray in
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)
@ -109,8 +113,10 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
}).disposed(by: self.rx.disposeBag)
//
self.requestJournalRecommend(journalID: journalID)
.subscribe(onNext: { journals in
self.requestJournalRecommend(journalID: self.journal.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)
updateElements()
@ -123,14 +129,18 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
input.selection.drive { indexPath in
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 { isExpand in
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
@ -157,14 +167,12 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
likeButtonTapped.subscribe { _ in
guard let id = self.journal.id else { return }
print("2222 likeButtonTapObservable")
likeButtonTapped.subscribe { [weak self] _ in
guard let self = self else { return }
if isLike.value {
self.requestCancelLike(journalNo: id)
.subscribe { _ in
self.requestCancelLike(journalNo: self.journal.id)
.subscribe { [weak self] _ in
var new = elements.value
if var header = new.first?.header {
@ -182,8 +190,8 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
}.disposed(by: self.rx.disposeBag)
} else {
self.requestLike(journalNo: id)
.subscribe { _ in
self.requestLike(journalNo: self.journal.id)
.subscribe { [weak self] _ in
var new = elements.value
if var header = new.first?.header {
@ -206,12 +214,14 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
shareButtonTrigger.subscribe { _ in
shareButtonTrigger.subscribe { [weak self] _ in
guard let self = self else { return }
self.toShare.onNext(())
}.disposed(by: rx.disposeBag)
item.subscribe { audioTrack in
item.subscribe { [weak self] audioTrack in
guard let audioTrack = audioTrack.element else { return }

@ -84,7 +84,8 @@ class ShareCardViewController: ViewController {
let output = viewModel.transform(input: input)
output.audioTrack.subscribe { audioTrack in
output.audioTrack.subscribe { [weak self] audioTrack in
guard let self = self else { return }
guard let audioTrack = audioTrack.element, audioTrack != nil else { return }
self.shareSingleView.isHidden = false
@ -93,7 +94,8 @@ class ShareCardViewController: ViewController {
}.disposed(by: rx.disposeBag)
output.journal.subscribe { journal in
output.journal.subscribe { [weak self] journal in
guard let self = self else { return }
guard let journal = journal.element, journal != nil else { return }
self.shareJournalView.isHidden = false
@ -103,7 +105,9 @@ class ShareCardViewController: ViewController {
saveButton.rx.tap.subscribe { _ in
saveButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
if let image = self.shareSingleView.snapshot(), self.shareSingleView.isHidden == false {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
SVProgressHUD.showText(withStatus: "保存成功")
@ -115,11 +119,6 @@ class ShareCardViewController: ViewController {
SVProgressHUD.showText(withStatus: "保存成功")
self.navigator.dismiss(sender: self)
}
} .disposed(by: rx.disposeBag)

@ -38,7 +38,9 @@ class InternationalNumberViewModel: ViewModel, ViewModelType {
input.viewWillAppear.subscribe { [weak self] (_) in
guard let self = self else { return }
self.requestSMSData().subscribe { array in
self.requestSMSData().subscribe { [weak self] array in
guard let self = self else { return }
guard let array = array.element else { return }
let internationalNumberSection = InternationalNumberSection.init(items: array)

@ -188,6 +188,14 @@ extension MessageViewController {
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
@ -199,6 +207,8 @@ extension MessageViewController {
cell.message = message
return cell
}
}
)
@ -348,6 +358,18 @@ class MessageCellView: UITableViewCell {
}
var customMessageType: CustomMessageType? {
didSet {
guard let customMessageType = customMessageType else { return }
avatarView.image = UIImage.init(named: customMessageType.image)
nameLabel.text = customMessageType.description
detailLabel.text = customMessageType.detail
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

@ -44,7 +44,10 @@ class MessageViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.messageType.subscribe { followersType in
input.messageType.subscribe { [weak self] followersType in
guard let self = self else { return }
self.messageType.accept(followersType)
}.disposed(by: rx.disposeBag)
@ -64,11 +67,8 @@ class MessageViewModel: ViewModel, ViewModelType {
})
.subscribe(onNext: { (items) in
if self.messageType.value == .message {
var new = items.map { message in
let new = items.map { message in
MessageSectionItem.messageItem(model: message)
}
@ -99,9 +99,6 @@ class MessageViewModel: ViewModel, ViewModelType {
}
})
.subscribe(onNext: { (items) in
if self.messageType.value == .message {
var arr = self.items.value.first?.items
var new = items.map { message in
@ -120,31 +117,45 @@ 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 else { return }
print("messageType \(messageType)")
self.page = 1
switch messageType {
case .message:
let comment = CustomMessageType.comment("测试")
let like = CustomMessageType.like("测试1")
let follow = CustomMessageType.follow("测试2")
let commentMessage = MessageSectionItem.customMessage(customMessageType: comment)
let likeMessage = MessageSectionItem.customMessage(customMessageType: like)
let followMessage = MessageSectionItem.customMessage(customMessageType: follow)
input.messageType.subscribe { messageType in
print("messageType \(messageType)")
self.page = 1
switch messageType {
case .message:
self.requestMessageList(page: self.page, size: 10)
.subscribe { messageArray in
var new = messageArray.map { message in
MessageSectionItem.messageItem(model: message)
}
self.messageItems.accept([MessageSection.message(items: [commentMessage, likeMessage, followMessage])])
self.messageItems.accept([MessageSection.message(items: new)])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
// self.requestMessageList(page: self.page, size: 10)
// .subscribe { messageArray in
// var new = messageArray.map { message in
// MessageSectionItem.messageItem(model: message)
// }
//
// self.messageItems.accept([MessageSection.message(items: new)])
// } onError: { error in
//
// }.disposed(by: self.rx.disposeBag)
case .activities:
self.requestMessageList(page: self.page, size: 10)
.subscribe { messageArray in
.subscribe { [weak self] messageArray in
guard let self = self else { return }
var new = messageArray.map { message in
MessageSectionItem.activitiesItem(model: message)
}
@ -153,9 +164,6 @@ class MessageViewModel: ViewModel, ViewModelType {
} onError: { error in
}.disposed(by: self.rx.disposeBag)
default: break
}
@ -166,10 +174,9 @@ class MessageViewModel: ViewModel, ViewModelType {
let cuttentItems = Observable.merge(messageItems.asObservable(), activitiesItems.asObservable())
cuttentItems.subscribe { followersSections in
cuttentItems.subscribe { [weak self] followersSections in
guard let self = self else { return }
self.items.accept(followersSections)
}.disposed(by: rx.disposeBag)

@ -46,8 +46,8 @@ class MineDownloadViewModel: ViewModel, ViewModelType {
self.isEditing.accept(!self.isEditing.value)
}.disposed(by: rx.disposeBag)
isEditing.subscribe { isEditing in
guard var currentItems = self.items.value.first?.items else { return }
isEditing.subscribe { [weak self] isEditing in
guard var currentItems = self?.items.value.first?.items else { return }
currentItems = currentItems.map { item in
var modifiedItem = item
@ -55,7 +55,7 @@ class MineDownloadViewModel: ViewModel, ViewModelType {
return modifiedItem
}
self.items.accept([MineDownloadSection.init(items: currentItems)])
self?.items.accept([MineDownloadSection.init(items: currentItems)])
}.disposed(by: rx.disposeBag)

@ -64,13 +64,13 @@ class MineJournalViewController: ViewController {
collectionView.rx.refresh.subscribe { refreshType in
collectionView.rx.refresh.subscribe { [weak self] refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
self?.headerRefreshTrigger.onNext(())
case .loadMore where AuthManager.shared.token?.isValid == true:
self.footerRefreshTrigger.onNext(())
self?.footerRefreshTrigger.onNext(())
default: break
}

@ -61,18 +61,15 @@ class MineSingleController: ViewController {
view.addSubview(headerView)
view.addSubview(tableView)
tableView.rx.refresh.subscribe { refreshType in
tableView.rx.refresh.subscribe { [weak self] refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
self?.headerRefreshTrigger.onNext(())
case .loadMore where AuthManager.shared.token?.isValid == true:
self.footerRefreshTrigger.onNext(())
self?.footerRefreshTrigger.onNext(())
default: break
}
}.disposed(by: rx.disposeBag)

@ -90,13 +90,11 @@ class MineViewController: TableViewController {
selection: tableView.rx.itemSelected.asDriver())
let output = viewModel.transform(input: input)
headerView.nameButton.rx.tap.subscribe { _ in
headerView.nameButton.rx.tap.subscribe { [weak self] _ in
guard AuthManager.shared.token?.isValid == false else { return }
let loginViewModel = LoginViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .login(viewModel: loginViewModel), sender: self)
self?.navigator.show(segue: .login(viewModel: loginViewModel), sender: self)
}.disposed(by: rx.disposeBag)
@ -169,15 +167,27 @@ class MineViewController: TableViewController {
}.disposed(by: rx.disposeBag)
output.user.subscribe { user in
self.headerView.user = user
output.user.subscribe { [weak self] user in
self?.headerView.user = user
}.disposed(by: rx.disposeBag)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.snp.remakeConstraints { make in
make.edges.equalTo(view)
if AudioManager.sharedInstance.playlist?.count ?? 0 > 0 {
make.bottom.equalTo(view).offset(-66 - BaseDimensions.tabBarHeight)
} else {
make.bottom.equalTo(view)
}
}
}
}

@ -32,7 +32,7 @@ class MineViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { _ in
self.requestUserInfoData().subscribe { user in
self.user.accept(user)

@ -66,7 +66,8 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
view.addSubview(tableView)
view.addSubview(noDataView)
tableView.rx.refresh.subscribe { refreshType in
tableView.rx.refresh.subscribe { [weak self] refreshType in
guard let self = self else { return }
switch refreshType.element {
case .refresh:
@ -107,9 +108,8 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.modelSelected.drive { [weak self] user in
guard let userID = user.id else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
let personalViewModel = PersonalViewModel.init(userID: user.id, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)

@ -44,7 +44,8 @@ class FollowersViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
input.followersType.subscribe { followersType in
input.followersType.subscribe { [weak self] followersType in
guard let self = self else { return }
self.followersType.accept(followersType)
}.disposed(by: rx.disposeBag)
@ -93,14 +94,17 @@ class FollowersViewModel: ViewModel, ViewModelType {
input.followersType.subscribe { followersType in
input.followersType.subscribe { [weak self] followersType in
guard let self = self else { return }
print("followersType \(followersType)")
switch followersType.element {
case .followers:
self.requestFollowersList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
.subscribe { follows in
.subscribe { [weak self] follows in
guard let self = self else { return }
self.followersItems.accept([FollowersSection.init(followersType: .followers, items: follows)])
} onError: { error in
@ -108,7 +112,9 @@ class FollowersViewModel: ViewModel, ViewModelType {
case .blackList:
self.requestBlockList(page: self.page, size: 10)
.subscribe { follows in
.subscribe { [weak self] follows in
guard let self = self else { return }
self.blocklistItems.accept([FollowersSection.init(followersType: .blackList, items: follows)])
} onError: { error in
@ -126,7 +132,8 @@ class FollowersViewModel: ViewModel, ViewModelType {
let cuttentItems = Observable.merge(followersItems.asObservable(), blocklistItems.asObservable())
cuttentItems.subscribe { followersSections in
cuttentItems.subscribe { [weak self] followersSections in
guard let self = self else { return }
self.items.accept(followersSections)
@ -137,8 +144,8 @@ class FollowersViewModel: ViewModel, ViewModelType {
return (user, followersType)
}
.subscribe { (user, followersType) in
guard let userID = user.id else { return }
.subscribe { [weak self] (user, followersType) in
guard let self = self else { return }
guard let index = self.items.value.first?.items.firstIndex(where: { $0.id == user.id }) else { return }
var updatedUsers = self.items.value
@ -148,8 +155,10 @@ class FollowersViewModel: ViewModel, ViewModelType {
switch user.relation {
case .notFollowing, .no:
self.requestLike(objectId: userID, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { _ in
self.requestLike(objectId: user.id, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { [weak self] _ in
guard let self = self else { return }
updatedUsers[0].items[index].relation = .mutualFollowing
if let items = updatedUsers.first?.items {
@ -162,8 +171,9 @@ class FollowersViewModel: ViewModel, ViewModelType {
case .following, .mutualFollowing:
self.requestCancelLike(objectId: userID, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { _ in
self.requestCancelLike(objectId: user.id, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { [weak self] _ in
guard let self = self else { return }
updatedUsers[0].items[index].relation = .notFollowing

@ -75,11 +75,10 @@ class FollowingViewController: TableViewController {
}.disposed(by: rx.disposeBag)
output.selection.drive { [weak self] selection in
guard let sectionItem = output.items.value.first?.items[selection.row] else { return }
guard let userID = sectionItem.id else { return }
output.selection.drive { [weak self] indexPath in
guard let user = output.items.value.first?.items[indexPath.row] else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
let personalViewModel = PersonalViewModel.init(userID: user.id, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)

@ -33,9 +33,12 @@ class FollowingViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { [weak self] _ in
guard let self = self else { return }
self.requestFollowingList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
.subscribe { array in
.subscribe { [weak self] array in
guard let self = self else { return }
self.items.accept([FollowingSection.init(items: array)])
} onError: { error in
@ -73,9 +76,8 @@ class FollowingViewModel: ViewModel, ViewModelType {
itemSelected.subscribe { user in
guard let user = user.element,
let userID = user.id else { return }
itemSelected.subscribe { [weak self] user in
guard let self = self, let user = user.element else { return }
guard let index = self.items.value.first?.items.firstIndex(where: { $0.id == user.id }) else { return }
var updatedUsers = self.items.value
@ -85,8 +87,10 @@ class FollowingViewModel: ViewModel, ViewModelType {
switch user.relation {
case .notFollowing:
self.requestLike(journalNo: userID)
.subscribe { _ in
self.requestLike(journalNo: user.id)
.subscribe { [weak self] _ in
guard let self = self else { return }
updatedUsers[0].items[index].relation = .mutualFollowing
if let items = updatedUsers.first?.items {
@ -99,8 +103,9 @@ class FollowingViewModel: ViewModel, ViewModelType {
case .following, .mutualFollowing:
self.requestCancelLike(journalNo: userID)
.subscribe { _ in
self.requestCancelLike(journalNo: user.id)
.subscribe { [weak self] _ in
guard let self = self else { return }
updatedUsers[0].items[index].relation = .notFollowing

@ -43,7 +43,12 @@ class MyCommentListController: TableViewController {
guard let viewModel = viewModel as? MyCommentListViewModel else { return }
let input = MyCommentListViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
let input = MyCommentListViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: refresh,
footerRefresh: footerRefreshTrigger
)
let output = viewModel.transform(input: input)
@ -74,13 +79,13 @@ class MyCommentListController: TableViewController {
extension MyCommentListController {
//TODO
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, Like) -> Void) -> RxTableViewSectionedReloadDataSource<MyCommentListSection> {
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, MineThumbup) -> Void) -> RxTableViewSectionedReloadDataSource<MyCommentListSection> {
return RxTableViewSectionedReloadDataSource<MyCommentListSection>(
configureCell: { dataSource, tableView, indexPath, item in
let cell: CommentListViewCell = tableView.dequeueReusableCell(withIdentifier: "CommentListViewCell", for: indexPath) as! CommentListViewCell
cell.myCommentList = item
cell.comment = item
return cell
}
@ -175,16 +180,16 @@ class CommentListViewCell: UITableViewCell {
var buttonTapCallback: ((User) -> ())?
var myCommentList: MyCommentList? {
var comment: Comment? {
didSet {
guard let myCommentList = myCommentList else { return }
avatarView.kf.setImage(with: URL.init(string: myCommentList.avatar))
audioTrackView.kf.setImage(with: URL.init(string: myCommentList.audioTrackImage))
nameLabel.text = myCommentList.name
dateLabel.text = "\(myCommentList.date)"
commentLabel.text = myCommentList.comment
myCommentLabel.text = "我的评论:\(myCommentList.myComment)"
guard let comment = comment else { return }
avatarView.kf.setImage(with: URL.init(string: comment.avatar ?? ""))
audioTrackView.kf.setImage(with: URL.init(string: comment.journalImage ?? ""))
nameLabel.text = comment.nickName
dateLabel.text = "\(comment.publishTime)"
commentLabel.text = comment.content
// myCommentLabel.text = "\(comment.comm)"
}
}

@ -14,6 +14,8 @@ class MyCommentListViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
}
@ -25,18 +27,52 @@ class MyCommentListViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { [weak self] _ in
guard let self = self else { return }
self.requestCommentList(page: self.page, size: 10)
.subscribe { [weak self] commentArray in
guard let self = self else { return }
}.disposed(by: rx.disposeBag)
let name = MyCommentList.init(avatar: "", audioTrackImage: "", name: "123", date: 1000, comment: "这就是生活,也是理想,也是你我的未来", myComment: "太多了,还有草东我也很")
} onError: { error in
items.accept([MyCommentListSection.init(items: [name, name, name, name])])
}.disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
input.headerRefresh.flatMapLatest({ [weak self] () -> Observable<[Comment]> in
guard let self = self else { return Observable.just([]) }
self.page = 1
return self.requestCommentList(page: self.page, size: 10)
.trackActivity(self.headerLoading)
})
.subscribe(onNext: { (items) in
self.items.accept([MyCommentListSection.init(items: items)])
}).disposed(by: rx.disposeBag)
input.footerRefresh.flatMapLatest({ [weak self] () -> Observable<[Comment]> in
guard let self = self else { return Observable.just([]) }
self.page += 1
return self.requestCommentList(page: self.page, size: 10)
.trackActivity(self.footerLoading)
})
.subscribe(onNext: { (items) in
self.items.accept([MyCommentListSection.init(items: (self.items.value.first?.items ?? []) + items)])
}).disposed(by: rx.disposeBag)
return Output.init(items: items)
}
func requestCommentList(page: Int, size: Int) -> Observable<[Comment]> {
self.provider.myCommentList(page: page, size: size)
.trackActivity(loading)
.trackError(error)
}
}

@ -44,12 +44,20 @@ class MyLikeListController: TableViewController {
guard let viewModel = viewModel as? MyLikeListViewModel else { return }
let input = MyLikeListViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
let input = MyLikeListViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: refresh,
footerRefresh: footerRefreshTrigger
)
let output = viewModel.transform(input: input)
let dataSource = MyLikeListController.dataSource { cell, like in
let dataSource = MyLikeListController.dataSource { [weak self] cell, mineThumbup in
guard let userID = mineThumbup.userId else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
}
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
@ -73,15 +81,15 @@ class MyLikeListController: TableViewController {
extension MyLikeListController {
//TODO
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, Like) -> Void) -> RxTableViewSectionedReloadDataSource<LikeSection> {
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, MineThumbup) -> Void) -> RxTableViewSectionedReloadDataSource<LikeSection> {
return RxTableViewSectionedReloadDataSource<LikeSection>(
configureCell: { dataSource, tableView, indexPath, item in
let cell: LikeViewCell = tableView.dequeueReusableCell(withIdentifier: "LikeViewCell", for: indexPath) as! LikeViewCell
cell.like = item
cell.mineThumbup = item
cell.buttonTapCallback = { like in
buttonTapHandler(cell, like)
cell.buttonTapCallback = { mineThumbup in
buttonTapHandler(cell, mineThumbup)
}
return cell
@ -108,6 +116,10 @@ class LikeViewCell: UITableViewCell {
nameLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
nameLabel.textColor = .primaryText()
nameLabel.setContentHuggingPriority(.required, for: .horizontal)
nameLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
return nameLabel
}()
@ -151,20 +163,22 @@ class LikeViewCell: UITableViewCell {
}()
var like: Like? {
var mineThumbup: MineThumbup? {
didSet {
guard let like = like else { return }
guard let mineThumbup = mineThumbup else { return }
avatarView.kf.setImage(with: URL.init(string: mineThumbup.avatar ?? ""))
nameLabel.text = mineThumbup.nickName
detailLabel.text = mineThumbup.commentContent
avatarView.image = UIImage.init(named: like.avatar)
nameLabel.text = like.name
detailLabel.text = like.content
let date = Date.init(dateString: mineThumbup.createTime ?? "", format: "yyyy-MM-dd HH:mm:ss")
dateLabel.text = "\(like.date)"
dateLabel.text = date.timeAgoSinceNow
}
}
var buttonTapCallback: ((Like) -> ())?
var buttonTapCallback: ((MineThumbup) -> ())?
@ -175,6 +189,18 @@ class LikeViewCell: UITableViewCell {
makeUI()
avatarView.rx.tapGesture().when(.recognized)
.subscribe { [weak self] tap in
guard let self = self else { return }
if let buttonTapCallback = self.buttonTapCallback,
let mineThumbup = self.mineThumbup {
buttonTapCallback(mineThumbup)
}
}.disposed(by: rx.disposeBag)
}
required init?(coder: NSCoder) {

@ -13,6 +13,8 @@ class MyLikeListViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
}
@ -28,14 +30,40 @@ class MyLikeListViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
let name = Like(avatar: "", name: "用户名", content: "签名", date: 10000)
input.headerRefresh.flatMapLatest({ [weak self] () -> Observable<[MineThumbup]> in
guard let self = self else { return Observable.just([]) }
self.page = 1
items.accept([LikeSection.init(items: [name, name, name, name])])
return self.requestLikeList(page: self.page, size: 10)
.trackActivity(self.headerLoading)
})
.subscribe(onNext: { (items) in
self.items.accept([LikeSection.init(items: items)])
}).disposed(by: rx.disposeBag)
input.footerRefresh.flatMapLatest({ [weak self] () -> Observable<[MineThumbup]> in
guard let self = self else { return Observable.just([]) }
self.page += 1
return self.requestLikeList(page: self.page, size: 10)
.trackActivity(self.footerLoading)
})
.subscribe(onNext: { (items) in
self.items.accept([LikeSection.init(items: (self.items.value.first?.items ?? []) + items)])
}).disposed(by: rx.disposeBag)
return Output.init(items: items)
}
func requestLikeList(page: Int, size: Int) -> Observable<[MineThumbup]> {
self.provider.myThumbupList(page: page, size: size)
.trackActivity(loading)
.trackError(error)
}
}

@ -18,9 +18,14 @@ class PersonalViewController: ViewController {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let viewModel = self.viewModel as? PersonalViewModel
if viewModel?.personInfoLikeType.value == .audio {
return self.createSingleColumnSection()
} else {
return self.createDoubleColumnSection()
}
return self.createSingleColumnSection()
}
@ -84,13 +89,13 @@ class PersonalViewController: ViewController {
view.addSubview(noDataView)
collectionView.rx.refresh.subscribe { refreshType in
collectionView.rx.refresh.subscribe { [weak self] refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
self?.headerRefreshTrigger.onNext(())
case .loadMore where AuthManager.shared.token?.isValid == true:
self.footerRefreshTrigger.onNext(())
self?.footerRefreshTrigger.onNext(())
default: break
}
@ -99,6 +104,8 @@ class PersonalViewController: ViewController {
}.disposed(by: rx.disposeBag)
}
override func bindViewModel() {
@ -152,6 +159,16 @@ class PersonalViewController: ViewController {
let resuableView: PersonalHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "PersonalHeaderView", for: indexPath) as! PersonalHeaderView
self.personalHeaderView = resuableView
personalHeaderView?.segmentControl.titleBtnOnClick = { [weak self] (label, index) in
if index == 0 {
self?.personInfoLikeType.accept(.audio)
} else {
self?.personInfoLikeType.accept(.journal)
}
}
let section = dataSource.sectionModels.first.value
// resuableView.user = viewModel.us
@ -175,12 +192,12 @@ class PersonalViewController: ViewController {
// }.disposed(by: rx.disposeBag)
resuableView.followingButton.rx.tap.subscribe { _ in
resuableView.followingButton.rx.tap.subscribe { [weak self] _ in
viewModel.followingButtonTrigger.accept(())
}.disposed(by: rx.disposeBag)
resuableView.messageButton.rx.tap.subscribe { _ in
resuableView.messageButton.rx.tap.subscribe { [weak self] _ in
viewModel.messageButtonTrigger.accept(())
}.disposed(by: rx.disposeBag)
@ -193,13 +210,12 @@ class PersonalViewController: ViewController {
}
)
// let collectionViewDataSource = person.collectionViewDataSource()
//
// output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
//
output.user.subscribe { user in
self.personalHeaderView?.user = user
output.user.subscribe { [weak self] user in
self?.personalHeaderView?.user = user
}.disposed(by: rx.disposeBag)
@ -285,188 +301,12 @@ extension PersonalViewController {
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
doubleColumnSection.boundarySupplementaryItems = [header]
doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
return doubleColumnSection
}
}
extension PersonalViewController {
//TODO
// static func tableViewDataSource(_ buttonTapHandler: @escaping (UITableViewCell, AudioTrack) -> Void) -> RxTableViewSectionedReloadDataSource<JournalSection> {
// return RxTableViewSectionedReloadDataSource<JournalSection>(
// configureCell: { dataSource, tableView, indexPath, item in
// let cell: SongViewCell = tableView.dequeueReusableCell(withIdentifier: "SongViewCell", for: indexPath) as! SongViewCell
//
//// cell.audioTrack = item
//
// cell.buttonTapCallback = {audioTrack in
// buttonTapHandler(cell, audioTrack)
// }
//
// return cell
// }
// )
// }
}
extension PersonalViewController {
// static func collectionViewDataSource() -> RxCollectionViewSectionedReloadDataSource<MusicStyleSection> {
// return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
// configureCell: { dataSource, collectionView, indexPath, item in
// // cell
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
// // cell
// cell.titleLabel.text = item.title
// cell.volLabel.text = item.subTitle
//
// return cell
// }
// )
// }
}
class PersonalView: UIView {
lazy var segmentControl: ScrollSegmentView = {
var style = SegmentStyle()
style.showLine = true
style.normalTitleColor = UIColor.secondaryText()
style.selectedTitleColor = UIColor.primaryText()
style.backgroundColor = UIColor.white
style.titleSelectFont = UIFont.systemFont(ofSize: 15, weight: .medium)
style.titleFont = UIFont.systemFont(ofSize: 15)
style.scrollLineHeight = 2
style.scrollLineColor = .primary()
style.coverBackgroundColor = .init(hex: 0x0d0d0d)
style.normalborderColor = UIColor.tertiaryText()
style.scrollTitle = false
style.showCover = false
style.normalborderColor = .clear
// style.scrollTitle = true
style.lineSpace = 0
let segmentControl = ScrollSegmentView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 32), segmentStyle: style, titles: ["喜欢的单曲", "收藏期刊"])
// segmentControl.scrollView.backgroundColor = .red
return segmentControl
}()
let tableView: UITableView = {
let tableView = UITableView.init()
return tableView
}()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 15
layout.minimumLineSpacing = 24
layout.sectionInset = UIEdgeInsets.init(top: 0, left: 18, bottom: 0, right: 18)
layout.itemSize = CGSize(width: (BaseDimensions.screenWidth - 18 * 2 - 15) / 2, height: 147)
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
return collectionView
}()
var currentSearchType: SearchType = .audio {
didSet {
switch currentSearchType {
case .audio:
collectionView.isHidden = true
tableView.isHidden = false
case .journal:
collectionView.isHidden = false
tableView.isHidden = true
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
segmentControl.titleBtnOnClick = { [weak self] (label, index) in
if index == 0 {
self?.currentSearchType = .audio
} else {
self?.currentSearchType = .journal
}
}
backgroundColor = .white
addSubview(segmentControl)
addSubview(tableView)
addSubview(collectionView)
}
override func layoutSubviews() {
super.layoutSubviews()
segmentControl.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(0)
make.height.equalTo(32)
make.width.equalTo(200)
}
tableView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.top.equalTo(segmentControl.snp.bottom).offset(24)
make.bottom.equalTo(self)
}
collectionView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.top.equalTo(segmentControl.snp.bottom).offset(24)
make.bottom.equalTo(self)
}
}
}
class PersonalHeaderView: UICollectionReusableView {

@ -58,14 +58,16 @@ class PersonalViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { [weak self] _ in
guard let self = self else { return }
self.requestUserData(userID: self.userID)
.subscribe { user in
self.user.accept(user)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
@ -121,14 +123,19 @@ class PersonalViewModel: ViewModel, ViewModelType {
input.personInfoLikeType.subscribe { personInfoLikeType in
input.personInfoLikeType.subscribe { [weak self] personInfoLikeType in
guard let self = self, let personInfoLikeType = personInfoLikeType.element else { return }
self.personInfoLikeType.accept(personInfoLikeType)
print("personInfoLikeType \(personInfoLikeType)")
switch personInfoLikeType.element {
switch personInfoLikeType {
case .audio:
self.requestCollectSongList(userId: self.userID, page: self.page, size: 10)
.subscribe { items in
.subscribe { [weak self] items in
guard let self = self else { return }
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
} onError: { error in
@ -136,14 +143,12 @@ class PersonalViewModel: ViewModel, ViewModelType {
//
case .journal:
self.requestJournalCollectList(userId: self.userID, page: self.page, size: 10)
.subscribe { items in
.subscribe { [weak self] items in
guard let self = self else { return }
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
default: break
}
@ -154,9 +159,9 @@ class PersonalViewModel: ViewModel, ViewModelType {
let cuttentItems = Observable.merge(audioItems.asObservable(), journalItems.asObservable())
cuttentItems.subscribe { personInfoSection in
cuttentItems.subscribe { [weak self] personInfoSection in
self.items.accept(personInfoSection)
self?.items.accept(personInfoSection)
}.disposed(by: rx.disposeBag)
@ -165,8 +170,8 @@ class PersonalViewModel: ViewModel, ViewModelType {
followingButtonTrigger
.subscribe { _ in
guard let userID = self.user.value?.id else { return }
.subscribe { [weak self] _ in
guard let self = self, let userID = self.user.value?.id else { return }
switch self.user.value?.relation {
case .notFollowing:
@ -212,9 +217,9 @@ class PersonalViewModel: ViewModel, ViewModelType {
case .blockedByMe:
self.requestCancelLike(objectId: userID, collectType: .addBlockList)
.subscribe { _ in
.subscribe { [weak self] _ in
self.updateUserRelationStatus(relation: .following)
self?.updateUserRelationStatus(relation: .following)
} onError: { error in

@ -85,13 +85,15 @@ class AudioTrackListViewController: ViewController {
}.disposed(by: rx.disposeBag)
output.reloadData.subscribe { audioTrack in
output.reloadData.subscribe { [weak self] audioTrack in
guard let self = self else { return }
self.tableView.reloadData()
}.disposed(by: rx.disposeBag)
self.audioMoreActionBottomView.closeButton.rx.tap.subscribe { _ in
self.audioMoreActionBottomView.closeButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
self.navigator.dismiss(sender: self)

@ -34,7 +34,9 @@ class AudioTrackListViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { isViewWillAppear in
input.viewWillAppear.subscribe { [weak self] isViewWillAppear in
guard let self = self else { return }
if let playlist = AudioManager.sharedInstance.playlist {
let arr = playlist.map { audioTrack in
return JournalItem.audioItem(model: audioTrack)
@ -60,7 +62,9 @@ class AudioTrackListViewModel: ViewModel, ViewModelType {
input.notiPlayAudioTrack.subscribe { noti in
input.notiPlayAudioTrack.subscribe { [weak self] noti in
guard let self = self else { return }
guard let track = noti.element?.object as? AudioTrack else { return }
self.reloadData.accept(track)

@ -288,9 +288,17 @@ class PlayerScrollView: UIScrollView {
return playerInfoView.playerSlider.slider
}
if self.bounds.contains(point) {
return self
}
// let playerInfoPoint = convert(point, to: playerInfoView)
// if playerInfoView.point(inside: playerInfoPoint, with: event) {
// return playerInfoView
// }
// if self.bounds.contains(point) {
// return self
// }
return super.hitTest(point, with: event)
@ -573,13 +581,10 @@ class PlayerInfoView: UIView {
makeUI()
// numberLabel.text = "VOL 1092"
// titleLabel.text = "fdsfds"
// artistLabel.text = "1233321"
// startTimeLabel.text = "00:00"
// endTimeLabel.text = "00:00"
//
// coverView.backgroundColor = .gray
likeButton.addTarget(self, action: #selector(likeButtonClick), for: .touchUpInside)
}
@objc func likeButtonClick() {
}
@ -606,6 +611,8 @@ class PlayerInfoView: UIView {
coverView.kf.setImage(with: URL.init(string: audioTrack.pic ?? ""))
numberLabel.text = "VOL \(audioTrack.journalNo ?? "")"
likeButton.isSelected = audioTrack.haveCollect ?? false
}
@ -656,7 +663,6 @@ class PlayerInfoView: UIView {
make.left.equalTo(self).offset(18)
make.right.equalTo(self).offset(-18)
make.top.equalTo(artistLabel.snp.bottom).offset(8)
make.height.equalTo(30)
make.bottom.equalTo(self)
}
@ -678,6 +684,11 @@ class PlayerInfoView: UIView {
return shareButton
}
let likePoint = convert(point, to: likeButton)
if likeButton.point(inside: likePoint, with: event) {
return likeButton
}
if self.bounds.contains(point) {
return self
}

@ -117,13 +117,13 @@ class PlayerViewController: ViewController {
}.disposed(by: rx.disposeBag)
viewModel.isLike
.bind(to: self.playerScrollView.playerInfoView.likeButton.rx.isSelected)
.disposed(by: rx.disposeBag)
// viewModel.isLike
// .bind(to: self.playerScrollView.playerInfoView.likeButton.rx.isSelected)
// .disposed(by: rx.disposeBag)
viewModel.isLike.subscribe { [weak self] isLike in
self?.playerScrollView.playerInfoView.likeButton.isSelected = isLike
}.disposed(by: rx.disposeBag)
// viewModel.isLike.subscribe { [weak self] isLike in
// self?.playerScrollView.playerInfoView.likeButton.isSelected = isLike
// }.disposed(by: rx.disposeBag)
output.toShare.subscribe { [weak self] _ in
@ -153,6 +153,7 @@ class PlayerViewController: ViewController {
self?.playerScrollView.audioTrack = audioTrack
self?.blurEffectView.imageView.kf.setImage(with: URL.init(string: audioTrack.pic ?? ""))
}.disposed(by: rx.disposeBag)
@ -165,7 +166,8 @@ class PlayerViewController: ViewController {
}.disposed(by: rx.disposeBag)
output.progress.subscribe { progress in
output.progress.subscribe { [weak self] progress in
guard let self = self else { return }
@ -180,11 +182,15 @@ class PlayerViewController: ViewController {
}.disposed(by: rx.disposeBag)
output.duration.subscribe { duration in
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 { time in
output.time.subscribe { [weak self] time in
guard let self = self else { return }
self.playerScrollView.playerInfoView.playerSlider.updateTimeLabel(currentTime: time)
}.disposed(by: rx.disposeBag)
@ -193,7 +199,8 @@ class PlayerViewController: ViewController {
self.playerControlView.listButton.rx.tap.subscribe { _ in
self.playerControlView.listButton.rx.tap.subscribe { [weak self] _ in
guard let self = self else { return }
let audioTrackListViewModel = AudioTrackListViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .audioTrackList(viewModel: audioTrackListViewModel), sender: self, transition: .navigationPresent(type: .audioTrackList))

@ -32,7 +32,6 @@ class PlayerViewModel: ViewModel, ViewModelType {
struct Output {
let items: BehaviorRelay<[PlayerLyricsSection]>
// let isLike: BehaviorRelay<Bool>
let shuffle: BehaviorRelay<Bool>
let audioTrack: PublishSubject<AudioTrack>
let toShare: PublishSubject<Void>
@ -48,7 +47,6 @@ class PlayerViewModel: ViewModel, ViewModelType {
let items = BehaviorRelay<[PlayerLyricsSection]>.init(value: [])
let isLike = BehaviorRelay<Bool>.init(value: false)
let isPlaying = BehaviorRelay<Bool>.init(value: false)
let shuffle = BehaviorRelay<Bool>.init(value: false)
let audioTrack = PublishSubject<AudioTrack>.init()
@ -85,11 +83,38 @@ class PlayerViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.likeButtonTrigger.drive { [weak self] _ in
input.likeButtonTrigger.asObservable()
.withLatestFrom(audioTrack) // 使 withLatestFrom audioTrack
.subscribe(onNext: { [weak self] latestAudioTrack in
guard let self = self else { return }
if latestAudioTrack.haveCollect == false {
self.requestLike(objectId: latestAudioTrack.id)
.subscribe { [weak self] _ in
guard let self = self else { return }
var new = latestAudioTrack
new.haveCollect = true
self.updateAudioTrack(audioTrack: new)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
} else {
self.requestCancelLike(objectId: latestAudioTrack.id)
.subscribe { [weak self] _ in
guard let self = self else { return }
self.isLike.accept(!self.isLike.value)
}.disposed(by: rx.disposeBag)
var new = latestAudioTrack
new.haveCollect = false
self.updateAudioTrack(audioTrack: new)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
}
})
.disposed(by: rx.disposeBag)
let lyrics = PlayerLyrics.init(lyrics: "1233211232")
@ -154,24 +179,29 @@ class PlayerViewModel: ViewModel, ViewModelType {
AudioManager.sharedInstance.changeShuffle()
}.disposed(by: rx.disposeBag)
input.notiPlayShuffle.subscribe { _ in
input.notiPlayShuffle.subscribe { [weak self] _ in
guard let self = self else { return }
self.playShuffleType.accept(AudioManager.sharedInstance.playerShuffleType)
}.disposed(by: rx.disposeBag)
input.notiPlayPause.subscribe { noti in
input.notiPlayPause.subscribe { [weak self] noti in
guard let self = self else { return }
self.isPlaying.accept(true)
}.disposed(by: rx.disposeBag)
input.notiPlayResume.subscribe { noti in
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,
// isLike: isLike,
shuffle: shuffle,
audioTrack: audioTrack,
toShare: toShare,
@ -183,6 +213,34 @@ class PlayerViewModel: ViewModel, ViewModelType {
isPlaying: isPlaying)
}
func updateAudioTrack(audioTrack: AudioTrack) {
self.audioTrack.onNext(audioTrack)
if let index = AudioManager.sharedInstance.playlist?.firstIndex(where: { $0.id == audioTrack.id }) {
// 使 AudioTrack
AudioManager.sharedInstance.playlist?[index] = audioTrack
}
}
func requestLike(objectId: String) -> Observable<Void> {
return self.provider.like(objectId: objectId, collectType: LikeType.track.rawValue)
.trackActivity(loading)
.trackError(error)
}
func requestCancelLike(objectId: String) -> Observable<Void> {
return self.provider.cancelLike(objectId: objectId, collectType: LikeType.track.rawValue)
.trackActivity(loading)
.trackError(error)
}
}
extension PlayerViewModel {

@ -24,13 +24,12 @@ class MusicStyleViewController: ViewController {
let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup)
// doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(viewModel?.headerHeight ?? 100))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
doubleColumnSection.boundarySupplementaryItems = [header]
doubleColumnSection.interGroupSpacing = 24
return doubleColumnSection
@ -88,13 +87,13 @@ class MusicStyleViewController: ViewController {
view.addSubview(collectionView)
collectionView.rx.refresh.subscribe { refreshType in
collectionView.rx.refresh.subscribe { [weak self] refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
self?.headerRefreshTrigger.onNext(())
case .loadMore:
self.footerRefreshTrigger.onNext(())
self?.footerRefreshTrigger.onNext(())
default: break
}

@ -46,7 +46,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
input.searchText.debounce(.milliseconds(500))
.drive { searchText in
self.fetchSearchData(keyword: searchText ?? "")
.subscribe { searchResults in
.subscribe { [weak self] searchResults in
let audioTrackArray = searchResults.songs?.map({ audioTrack in
return SearchResultsItem.single(item: audioTrack)
@ -56,11 +56,9 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
return SearchResultsItem.journal(item: journal)
})
self.audioTrackItems.accept(audioTrackArray ?? [])
self.journalItems.accept(journalArray ?? [])
self?.audioTrackItems.accept(audioTrackArray ?? [])
self?.journalItems.accept(journalArray ?? [])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
@ -69,7 +67,9 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
input.searchType.subscribe { searchType in
input.searchType.subscribe { [weak self] searchType in
guard let self = self else { return }
self.searchType.accept(searchType.element ?? .audio)
switch searchType.element {
@ -85,7 +85,9 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
let cuttentItems = Observable.merge(audioTrackItems.asObservable(), journalItems.asObservable())
cuttentItems.subscribe { searchResultsItems in
cuttentItems.subscribe { [weak self] searchResultsItems in
guard let self = self else { return }
switch self.searchType.value {
case .audio:
self.items.accept([SearchResultsSection.single(title: "", items: self.audioTrackItems.value)])

@ -22,13 +22,12 @@ class SearchViewController: ViewController, UIScrollViewDelegate {
let verticalGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(88))
let verticalGroup = NSCollectionLayoutGroup.horizontal(layoutSize: verticalGroupSize, subitem: item, count: 2)
verticalGroup.interItemSpacing = .fixed(15)
verticalGroup.contentInsets = .init(top: 15, leading: 0, bottom: 0, trailing: 0)
//
let section = NSCollectionLayoutSection(group: verticalGroup)
section.interGroupSpacing = 15
section.contentInsets = .init(top: 0, leading: 18, bottom: 0, trailing: 18)
section.contentInsets = .init(top: 15, leading: 18, bottom: 15, trailing: 18)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(104 + BaseDimensions.statusBarHeight))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
@ -123,12 +122,17 @@ class SearchViewController: ViewController, UIScrollViewDelegate {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.snp.makeConstraints { make in
collectionView.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(view).offset(-BaseDimensions.navBarHeight)
if AudioManager.sharedInstance.playlist?.count ?? 0 > 0 {
make.bottom.equalTo(view).offset(-66 - BaseDimensions.tabBarHeight)
} else {
make.bottom.equalTo(view).offset(-BaseDimensions.tabBarHeight)
}
}
}
}

@ -32,8 +32,8 @@ class SearchViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { _ in
self.fetchSearchCategoryData().subscribe { searchCategoryArray in
self.items.accept([SearchCategorySection.init(items: searchCategoryArray)])
self.fetchSearchCategoryData().subscribe { [weak self] searchCategoryArray in
self?.items.accept([SearchCategorySection.init(items: searchCategoryArray)])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
@ -45,7 +45,9 @@ class SearchViewModel: ViewModel, ViewModelType {
input.selection.drive { indexPath in
input.selection.drive { [weak self] indexPath in
guard let self = self else { return }
guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
self.itemSelected.onNext(sectionItem)
}.disposed(by: rx.disposeBag)

@ -70,10 +70,10 @@ class EditDateViewModel: ViewModel, ViewModelType {
input.viewWillAppear.subscribe { (_) in
input.viewWillAppear.subscribe { [weak self] _ in
guard let self = self else { return }
self.updateDays(forMonth: 1, year: self.selectedYear.value)
}.disposed(by: rx.disposeBag)
@ -236,14 +236,12 @@ class EditDateViewController: ViewController {
cancelButton.rx.tap.subscribe { _ in
self.navigator.dismiss(sender: self)
cancelButton.rx.tap.subscribe { [weak self] _ in
self?.navigator.dismiss(sender: self)
}.disposed(by: rx.disposeBag)
saveButton.rx.tap.subscribe { _ in
print("日期\(viewModel.selectedYear.value)\(viewModel.selectedMonth.value)\(viewModel.selectedDay.value)")
self.navigator.dismiss(sender: self)
saveButton.rx.tap.subscribe { [weak self] _ in
self?.navigator.dismiss(sender: self)
}.disposed(by: rx.disposeBag)
}

@ -87,16 +87,14 @@ class EditInfoViewController: TableViewController {
}.disposed(by: rx.disposeBag)
output.user.subscribe { user in
output.user.subscribe { [weak self] user in
guard let user = user.element else { return }
self.headerView.avatarView.kf.setImage(with: URL.init(string: user.avatar ?? ""))
self?.headerView.avatarView.kf.setImage(with: URL.init(string: user.avatar ?? ""))
}.disposed(by: rx.disposeBag)
output.avatar.subscribe { url in
self.headerView.avatarView.kf.setImage(with: URL.init(string: url))
output.avatar.subscribe { [weak self] url in
self?.headerView.avatarView.kf.setImage(with: URL.init(string: url))
}.disposed(by: rx.disposeBag)

@ -43,8 +43,10 @@ class EditInfoViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
self.requestUserInfoData().subscribe { user in
input.viewWillAppear.subscribe { _ in
self.requestUserInfoData().subscribe { [weak self] user in
guard let self = self else { return }
self.user.onNext(user)
self.updateItems(with: user)
@ -110,9 +112,9 @@ class EditInfoViewModel: ViewModel, ViewModelType {
editAvatar.subscribe { imageData in
self.editAvatar(imageData: imageData)
.subscribe { url in
.subscribe { [weak self] url in
self.avatar.onNext(url)
self?.avatar.onNext(url)
} onError: { error in
print("error: \(error)")
}.disposed(by: self.rx.disposeBag)

@ -140,8 +140,8 @@ class EditNameController: ViewController {
.bind(to: self.editNameTextFieldView.textFieldView.rx.text)
.disposed(by: rx.disposeBag)
output.dismiss.subscribe { _ in
self.navigator.pop(sender: self)
output.dismiss.subscribe { [weak self] _ in
self?.navigator.pop(sender: self)
}.disposed(by: rx.disposeBag)

@ -132,12 +132,12 @@ class EditSexViewController: ViewController, UIScrollViewDelegate {
output.popView.subscribe { _ in
self.navigator.pop(sender: self)
output.popView.subscribe { [weak self] _ in
self?.navigator.pop(sender: self)
}.disposed(by: rx.disposeBag)
dismissButton.rx.tap.subscribe { _ in
self.navigator.dismiss(sender: self)
dismissButton.rx.tap.subscribe { [weak self] _ in
self?.navigator.dismiss(sender: self)
}.disposed(by: rx.disposeBag)

@ -140,8 +140,8 @@ class EditSignatureViewController: ViewController {
.bind(to: self.editNameTextFieldView.textFieldView.rx.text)
.disposed(by: rx.disposeBag)
output.dismiss.subscribe { _ in
self.navigator.pop(sender: self)
output.dismiss.subscribe { [weak self] _ in
self?.navigator.pop(sender: self)
}.disposed(by: rx.disposeBag)

@ -41,8 +41,8 @@ class PhotoConfirmViewController: UIViewController {
}.disposed(by: rx.disposeBag)
cancelControl.rx.controlEvent(.touchUpInside)
.subscribe { _ in
self.dismiss(animated: true)
.subscribe { [weak self] _ in
self?.dismiss(animated: true)
}.disposed(by: rx.disposeBag)
}

@ -74,7 +74,7 @@ protocol IndieMusicAPI {
///
func subCommentList(parentId: String, page: Int, size: Int) -> Single<[Comment]>
///
func commentLike(commentId: String) -> Single<Void>
func commentLike(commentId: String) -> Single<CommentThumbState>
///
func sendComment(content: String, journalId: String?, parentId: String?, journalImage: String?) -> Single<Comment>
@ -90,5 +90,10 @@ protocol IndieMusicAPI {
///
func randomAudioTrack(limit: Int) -> Single<[AudioTrack]>
///
func myThumbupList(page: Int, size: Int) -> Single<[MineThumbup]>
///
func myCommentList(page: Int, size: Int) -> Single<[Comment]>
}

@ -56,6 +56,9 @@ enum APIConfig {
case randomAudioTrack(Int)
case myThumbupList(Int, Int)
case myCommentReplyList(Int, Int)
}
extension APIConfig: TargetType {
@ -136,12 +139,16 @@ extension APIConfig: TargetType {
case .randomAudioTrack(let limit):
return "luoo-music/song/random/\(limit)"
case .myThumbupList(let page, let size):
return "luoo-user/my/myThumbupList/\(page)/\(size)"
case .myCommentReplyList(let page, let size):
return "luoo-user/my/myCommentReplyList/\(page)/\(size)"
}
}
var method: Moya.Method {
switch self {
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack:
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack, .myThumbupList, .myCommentReplyList:
return .get
case .sendsms, .login, .autoLogin, .editAvatar, .like, .checkVersion, .logout, .sendComment:
return .post
@ -155,7 +162,7 @@ extension APIConfig: TargetType {
var parameterEncoding: ParameterEncoding {
switch self {
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .sendsms, .imageCheckCode, .login, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .like, .cancelLike, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack:
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .sendsms, .imageCheckCode, .login, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .like, .cancelLike, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack, .myThumbupList, .myCommentReplyList:
return URLEncoding.default
case .autoLogin, .editUserInfo, .editAvatar, .checkVersion, .logout, .commentLike, .sendComment:
@ -167,7 +174,7 @@ extension APIConfig: TargetType {
var task: Task {
var parameters: [String: Any] = [:]
switch self {
case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .hotCommentList, .latestCommentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack:
case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .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):
@ -196,7 +203,7 @@ extension APIConfig: TargetType {
var headers : [String : String]? {
switch self {
case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo, .logout, .editAvatar, .hotCommentList, .latestCommentList, .subCommentList, .commentLike, .filterMenu, .journalRecommend, .serach, .randomAudioTrack, .sendComment:
case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo, .logout, .editAvatar, .hotCommentList, .latestCommentList, .subCommentList, .commentLike, .filterMenu, .journalRecommend, .serach, .randomAudioTrack, .sendComment, .myThumbupList, .myCommentReplyList:
return ["Authorization": AuthManager.shared.token?.basicToken ?? ""]
default:
return nil

@ -254,8 +254,8 @@ extension RestApi {
return requestObject(.subCommentList(parentId, page, size), with: "data.rows", type: [Comment].self)
}
func commentLike(commentId: String) -> Single<Void> {
return requestWithoutMapping(.commentLike(commentId, ["":""])).map{ _ in }
func commentLike(commentId: String) -> Single<CommentThumbState> {
return requestObject(.commentLike(commentId, ["": ""]), with: "data", type: CommentThumbState.self)
}
@ -274,7 +274,7 @@ extension RestApi {
let dic = ["commentVo": commentVo]
return requestObject(.sendComment(dic), with: "data", type: Comment.self)
return requestObject(.sendComment(commentVo), with: "data", type: Comment.self)
}
func filterMenu() -> Single<FilterMenu> {
@ -298,4 +298,13 @@ extension RestApi {
return requestObject(.randomAudioTrack(limit), with: "data", type: [AudioTrack].self)
}
func myThumbupList(page: Int, size: Int) -> Single<[MineThumbup]> {
return requestObject(.myThumbupList(page, size), with: "data.rows", type: [MineThumbup].self)
}
func myCommentList(page: Int, size: Int) -> Single<[Comment]> {
return requestObject(.myCommentReplyList(page, size), with: "data.rows", type: [Comment].self)
}
}

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "message_comment_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,10 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="24" fill="url(#paint0_linear_1350_3276)" style=""/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34 24C34 29.5228 29.5228 34 24 34H16C16 34 17 32 16.5 30.5C16.3395 30.0185 16.0244 29.4571 15.6705 28.8267C14.922 27.4932 14 25.8505 14 24C14 18.4772 18.4772 14 24 14C29.5228 14 34 18.4772 34 24ZM25 28.75H20V27.25H25V28.75ZM20 25.75H27V24.25H20V25.75Z" fill="white" style="fill:white;fill-opacity:1;"/>
<defs>
<linearGradient id="paint0_linear_1350_3276" x1="-2.29105e-06" y1="-4" x2="40" y2="53.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#B1B8E8" style="stop-color:#B1B8E8;stop-color:color(display-p3 0.6954 0.7207 0.9083);stop-opacity:1;"/>
<stop offset="1" stop-color="#6677F8" style="stop-color:#6677F8;stop-color:color(display-p3 0.4005 0.4682 0.9708);stop-opacity:1;"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 950 B

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "message_follow_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,10 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="24" fill="url(#paint0_linear_4082_4034)" style=""/>
<path d="M19.641 15C16.5256 15 14 17.4625 14 20.5C14 26 20.6667 31 24.2564 32.1631C27.8462 31 34.5128 26 34.5128 20.5C34.5128 17.4625 31.9872 15 28.8718 15C26.3077 15 25.2773 16.5865 24.2564 18C23.2355 16.5865 22.2051 15 19.641 15Z" fill="white" style="fill:white;fill-opacity:1;"/>
<defs>
<linearGradient id="paint0_linear_4082_4034" x1="28" y1="44.5" x2="12.5" y2="-3.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#F2703E" style="stop-color:#F2703E;stop-color:color(display-p3 0.9500 0.4400 0.2415);stop-opacity:1;"/>
<stop offset="1" stop-color="#F8B9A0" style="stop-color:#F8B9A0;stop-color:color(display-p3 0.9710 0.7235 0.6259);stop-opacity:1;"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 874 B

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "message_like_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,11 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="24" fill="url(#paint0_linear_4082_4018)" style=""/>
<path d="M25.8 21.3V17.7C25.8 16.2088 24.5912 15 23.1 15L19.5 23.1V33H29.958C30.8555 33.0102 31.6235 32.3575 31.758 31.47L33 23.37C33.0792 22.8479 32.9252 22.3173 32.5787 21.9188C32.2322 21.5203 31.7281 21.294 31.2 21.3H25.8Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M18.5 23.0003H16.097C15.0427 22.9816 14.1414 23.855 14 24.8999V31.1999C14.1414 32.2449 15.0427 33.0186 16.097 32.9999H18.5V23.0003Z" fill="white" style="fill:white;fill-opacity:1;"/>
<defs>
<linearGradient id="paint0_linear_4082_4018" x1="28" y1="44.5" x2="12.5" y2="-3.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#897E75" style="stop-color:#897E75;stop-color:color(display-p3 0.5375 0.4947 0.4591);stop-opacity:1;"/>
<stop offset="1" stop-color="#AEAAA3" style="stop-color:#AEAAA3;stop-color:color(display-p3 0.6805 0.6673 0.6407);stop-opacity:1;"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,7 @@
/*
ChinaLocalizable.strings
IndieMusic
Created by WenLei on 2024/2/12.
*/

@ -0,0 +1,7 @@
/*
ChinaLocalizable.strings
IndieMusic
Created by WenLei on 2024/2/12.
*/
Loading…
Cancel
Save