mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Various improvements
This commit is contained in:
parent
77cc01f0d9
commit
9dde762a1c
8 changed files with 574 additions and 442 deletions
|
|
@ -311,7 +311,15 @@ private enum AttachmentFileEntry: ItemListNodeEntry {
|
|||
}
|
||||
}
|
||||
|
||||
private func attachmentFileControllerEntries(presentationData: PresentationData, mode: AttachmentFileControllerMode, state: AttachmentFileControllerState, savedMusic: [Message]?, recentDocuments: [Message]?, hasScan: Bool, empty: Bool) -> [AttachmentFileEntry] {
|
||||
private func attachmentFileControllerEntries(
|
||||
presentationData: PresentationData,
|
||||
mode: AttachmentFileControllerMode,
|
||||
state: AttachmentFileControllerState,
|
||||
savedMusic: [Message]?,
|
||||
recentDocuments: [Message]?,
|
||||
hasScan: Bool,
|
||||
empty: Bool
|
||||
) -> [AttachmentFileEntry] {
|
||||
guard !empty else {
|
||||
return []
|
||||
}
|
||||
|
|
@ -332,7 +340,7 @@ private func attachmentFileControllerEntries(presentationData: PresentationData,
|
|||
listTitle = presentationData.strings.Attachment_SharedAudio
|
||||
}
|
||||
|
||||
if case .audio = mode {
|
||||
if case let .audio(audioMode) = mode, audioMode != .savedMusic {
|
||||
if let savedMusic, savedMusic.count > 0 {
|
||||
entries.append(.savedHeader(presentationData.theme, presentationData.strings.MediaEditor_Audio_SavedMusic.uppercased()))
|
||||
|
||||
|
|
@ -521,8 +529,14 @@ private func messageSelectionState(state: AttachmentFileControllerState, message
|
|||
}
|
||||
|
||||
public enum AttachmentFileControllerMode {
|
||||
public enum AudioMode {
|
||||
case chat
|
||||
case story
|
||||
case savedMusic
|
||||
}
|
||||
|
||||
case recent
|
||||
case audio(story: Bool)
|
||||
case audio(AudioMode)
|
||||
|
||||
var isAudio: Bool {
|
||||
if case .audio = self {
|
||||
|
|
@ -548,10 +562,10 @@ public func makeAttachmentFileControllerImpl(
|
|||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
mode: AttachmentFileControllerMode = .recent,
|
||||
source: AttachmentFileControllerSource = .generic,
|
||||
bannedSendMedia: (Int32, Bool)?,
|
||||
presentGallery: @escaping () -> Void,
|
||||
presentFiles: @escaping () -> Void,
|
||||
presentDocumentScanner: (() -> Void)?,
|
||||
bannedSendMedia: (Int32, Bool)? = nil,
|
||||
presentGallery: @escaping () -> Void = {},
|
||||
presentFiles: @escaping () -> Void = {},
|
||||
presentDocumentScanner: (() -> Void)? = nil,
|
||||
send: @escaping ([AnyMediaReference], Bool, Int32?, NSAttributedString?) -> Void
|
||||
) -> AttachmentFileController {
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
|
@ -573,9 +587,11 @@ public func makeAttachmentFileControllerImpl(
|
|||
|
||||
var isAudio = false
|
||||
var isAttach = true
|
||||
if case let .audio(isStory) = mode {
|
||||
if case let .audio(mode) = mode {
|
||||
isAudio = true
|
||||
isAttach = !isStory
|
||||
if case .story = mode {
|
||||
isAttach = false
|
||||
}
|
||||
}
|
||||
|
||||
var didPreviewAudio = false
|
||||
|
|
@ -1032,6 +1048,11 @@ public func makeAttachmentFileControllerImpl(
|
|||
context.sharedContext.mediaManager.setPlaylist(nil, type: .music, control: .playback(.pause))
|
||||
}
|
||||
}
|
||||
|
||||
if case let .audio(audioMode) = mode, audioMode != .chat {
|
||||
controller.hasBottomEdgeEffect = false
|
||||
}
|
||||
|
||||
updateIsSearchingImpl = { [weak controller] isSearching in
|
||||
controller?.isSearching = isSearching
|
||||
}
|
||||
|
|
@ -1074,14 +1095,13 @@ public func storyAudioPickerController(
|
|||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, style: .glass, chatLocation: nil, buttons: [.audio], initialButton: .audio, fromMenu: false, hasTextInput: false)
|
||||
controller.requestController = { _, present in
|
||||
let filePickerController = makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, mode: .audio(story: true), bannedSendMedia: nil, presentGallery: {}, presentFiles: {
|
||||
let filePickerController = makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, mode: .audio(.story), bannedSendMedia: nil, presentGallery: {}, presentFiles: {
|
||||
selectFromFiles()
|
||||
dismissImpl?()
|
||||
}, presentDocumentScanner: nil, send: { files, _, _, _ in
|
||||
completion(files.first!)
|
||||
dismissImpl?()
|
||||
}) as! AttachmentFileControllerImpl
|
||||
filePickerController.hasBottomEdgeEffect = false
|
||||
present(filePickerController, filePickerController.mediaPickerContext)
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3028,7 +3028,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
playlistLocation: playlistLocation,
|
||||
parentNavigationController: self.controller?.navigationController as? NavigationController
|
||||
)
|
||||
self.controller?.present(musicController, in: .window(.root))
|
||||
musicController.navigationPresentation = .flatModal
|
||||
self.controller?.push(musicController)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Navigation/Add.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Navigation/Add.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "plus_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Navigation/Add.imageset/plus_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Navigation/Add.imageset/plus_30.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -8,6 +8,7 @@ import TelegramUIPreferences
|
|||
import AccountContext
|
||||
import ShareController
|
||||
import UndoUI
|
||||
import AttachmentFileController
|
||||
|
||||
final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayerController {
|
||||
private let context: AccountContext
|
||||
|
|
@ -62,88 +63,116 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
|
|||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = OverlayAudioPlayerControllerNode(context: self.context, chatLocation: self.chatLocation, type: self.type, initialMessageId: self.initialMessageId, initialOrder: self.initialOrder, playlistLocation: self.playlistLocation, requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
}, requestShare: { [weak self] subject in
|
||||
if let strongSelf = self {
|
||||
var canShowInChat = false
|
||||
if case .messages = subject {
|
||||
canShowInChat = true
|
||||
}
|
||||
let shareController = ShareController(context: strongSelf.context, subject: subject, showInChat: canShowInChat ? { message in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: message.id.peerId, messageId: message.id)
|
||||
strongSelf.dismiss()
|
||||
self.displayNode = OverlayAudioPlayerControllerNode(
|
||||
context: self.context,
|
||||
chatLocation: self.chatLocation,
|
||||
type: self.type,
|
||||
initialMessageId: self.initialMessageId,
|
||||
initialOrder: self.initialOrder,
|
||||
playlistLocation: self.playlistLocation,
|
||||
requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
},
|
||||
requestShare: { [weak self] subject in
|
||||
if let strongSelf = self {
|
||||
var canShowInChat = false
|
||||
if case .messages = subject {
|
||||
canShowInChat = true
|
||||
}
|
||||
} : nil, externalShare: true)
|
||||
shareController.completed = { [weak self] peerIds in
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
EngineDataList(
|
||||
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
||||
let shareController = ShareController(context: strongSelf.context, subject: subject, showInChat: canShowInChat ? { message in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: message.id.peerId, messageId: message.id)
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
} : nil, externalShare: true)
|
||||
shareController.completed = { [weak self] peerIds in
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
EngineDataList(
|
||||
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
||||
)
|
||||
)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in
|
||||
if let strongSelf = self {
|
||||
let peers = peerList.compactMap { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let text: String
|
||||
var savedMessages = false
|
||||
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in
|
||||
if let strongSelf = self {
|
||||
let peers = peerList.compactMap { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let text: String
|
||||
var savedMessages = false
|
||||
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One
|
||||
savedMessages = true
|
||||
} else {
|
||||
text = ""
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
|
||||
if savedMessages, let self, action == .info {
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
guard let navigationController = self.parentNavigationController else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true))
|
||||
})
|
||||
}
|
||||
return false
|
||||
}), in: .current)
|
||||
}
|
||||
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
|
||||
if savedMessages, let self, action == .info {
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
guard let navigationController = self.parentNavigationController else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true))
|
||||
})
|
||||
}
|
||||
return false
|
||||
}), in: .current)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.controllerNode.view.endEditing(true)
|
||||
strongSelf.present(shareController, in: .window(.root))
|
||||
}
|
||||
strongSelf.controllerNode.view.endEditing(true)
|
||||
strongSelf.present(shareController, in: .window(.root))
|
||||
},
|
||||
requestSearchByArtist: { [weak self] artist in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.openSearch(filter: .music, query: artist)
|
||||
self.dismiss()
|
||||
},
|
||||
requestAdd: { [weak self] in
|
||||
guard let self, let navigationController = self.parentNavigationController else {
|
||||
return
|
||||
}
|
||||
let controller = makeAttachmentFileControllerImpl(
|
||||
context: self.context,
|
||||
mode: .audio(.savedMusic),
|
||||
send: { [weak self] mediaReferences, _, _, _ in
|
||||
guard let self, let reference = mediaReferences.first?.concrete(TelegramMediaFile.self) else {
|
||||
return
|
||||
}
|
||||
self.controllerNode.addToSavedMusic(file: reference)
|
||||
}
|
||||
) as! AttachmentFileControllerImpl
|
||||
controller.navigationPresentation = .modal
|
||||
navigationController.pushViewController(controller)
|
||||
},
|
||||
getParentController: { [weak self] in
|
||||
return self
|
||||
}
|
||||
}, requestSearchByArtist: { [weak self] artist in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.openSearch(filter: .music, query: artist)
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
},
|
||||
getParentController: { [weak self] in
|
||||
return self
|
||||
})
|
||||
)
|
||||
|
||||
self.ready.set(self.controllerNode.ready.get())
|
||||
|
||||
|
|
@ -165,7 +194,11 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
|
|||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
if let _ = self?.navigationController {
|
||||
self?.dismiss(animated: false, completion: nil)
|
||||
} else {
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Foundation
|
|||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
|
@ -14,6 +15,8 @@ import ChatControllerInteraction
|
|||
import ContextUI
|
||||
import UndoUI
|
||||
import ChatHistoryEntry
|
||||
import MultilineTextComponent
|
||||
import GlassControls
|
||||
|
||||
final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestureRecognizerDelegate {
|
||||
let ready = Promise<Bool>()
|
||||
|
|
@ -27,6 +30,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
private let requestDismiss: () -> Void
|
||||
private let requestShare: (ShareControllerSubject) -> Void
|
||||
private let requestSearchByArtist: (String) -> Void
|
||||
private let requestAdd: () -> Void
|
||||
private let playlistLocation: SharedMediaPlaylistLocation?
|
||||
private let isGlobalSearch: Bool
|
||||
|
||||
|
|
@ -35,11 +39,21 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
private var currentIsReversed: Bool
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
private let containerContainingNode: ASDisplayNode
|
||||
private let contentNode: ASDisplayNode
|
||||
private let controlsNode: OverlayPlayerControlsNode
|
||||
private let controlsNode: OverlayAudioPlayerControlsNode
|
||||
private let historyBackgroundNode: ASDisplayNode
|
||||
private let historyBackgroundContentNode: ASDisplayNode
|
||||
private let historyFrameNode: SparseNode
|
||||
private let historyFrameLeftOverlayNode: ASDisplayNode
|
||||
private let historyFrameRightOverlayNode: ASDisplayNode
|
||||
private let historyFrameTopOverlayNode: ASDisplayNode
|
||||
private let historyFrameTopMaskNode: ASImageNode
|
||||
private let headerButtons = ComponentView<Empty>()
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
private var floatingHeaderOffset: CGFloat?
|
||||
private var historyContentOffset: CGFloat = 0.0
|
||||
private var historyNode: ChatHistoryListNodeImpl
|
||||
private var replacementHistoryNode: ChatHistoryListNodeImpl?
|
||||
private var replacementHistoryNodeFloatingOffset: CGFloat?
|
||||
|
|
@ -53,9 +67,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
|
||||
private let getParentController: () -> ViewController?
|
||||
|
||||
private var savedIdsDisposable: Disposable?
|
||||
private var dataDisposable: Disposable?
|
||||
private var savedIdsPromise = Promise<Set<Int64>?>()
|
||||
private var savedIds: Set<Int64>?
|
||||
private var peer: EnginePeer?
|
||||
|
||||
private var copyProtectionEnabled = false
|
||||
|
||||
|
|
@ -69,6 +84,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
requestDismiss: @escaping () -> Void,
|
||||
requestShare: @escaping (ShareControllerSubject) -> Void,
|
||||
requestSearchByArtist: @escaping (String) -> Void,
|
||||
requestAdd: @escaping () -> Void,
|
||||
getParentController: @escaping () -> ViewController?
|
||||
) {
|
||||
self.context = context
|
||||
|
|
@ -78,6 +94,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.requestDismiss = requestDismiss
|
||||
self.requestShare = requestShare
|
||||
self.requestSearchByArtist = requestSearchByArtist
|
||||
self.requestAdd = requestAdd
|
||||
self.playlistLocation = playlistLocation
|
||||
self.getParentController = getParentController
|
||||
|
||||
|
|
@ -238,9 +255,12 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
|
||||
self.containerContainingNode = ASDisplayNode()
|
||||
self.containerContainingNode.clipsToBounds = true
|
||||
|
||||
self.contentNode = ASDisplayNode()
|
||||
|
||||
self.controlsNode = OverlayPlayerControlsNode(account: context.account, engine: context.engine, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState, chatLocation: self.chatLocation, source: self.source)
|
||||
self.controlsNode = OverlayAudioPlayerControlsNode(account: context.account, engine: context.engine, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState, chatLocation: self.chatLocation, source: self.source)
|
||||
self.controlsNode.getParentController = getParentController
|
||||
|
||||
self.historyBackgroundNode = ASDisplayNode()
|
||||
|
|
@ -248,10 +268,28 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
|
||||
self.historyBackgroundContentNode = ASDisplayNode()
|
||||
self.historyBackgroundContentNode.isLayerBacked = true
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.itemModalBlocksBackgroundColor
|
||||
|
||||
self.historyBackgroundNode.addSubnode(self.historyBackgroundContentNode)
|
||||
|
||||
self.historyFrameNode = SparseNode()
|
||||
self.historyFrameLeftOverlayNode = ASDisplayNode()
|
||||
self.historyFrameLeftOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
|
||||
self.historyFrameRightOverlayNode = ASDisplayNode()
|
||||
self.historyFrameRightOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
|
||||
self.historyFrameTopOverlayNode = ASDisplayNode()
|
||||
self.historyFrameTopOverlayNode.cornerRadius = 38.0
|
||||
self.historyFrameTopOverlayNode.clipsToBounds = true
|
||||
self.historyFrameTopOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
self.historyFrameTopOverlayNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.historyFrameTopMaskNode = ASImageNode()
|
||||
self.historyFrameTopMaskNode.displaysAsynchronously = false
|
||||
self.historyFrameTopMaskNode.image = PresentationResourcesItemList.cornersImage(self.presentationData.theme.withModalBlocksBackground(), top: true, bottom: false, glass: true)
|
||||
self.historyFrameTopMaskNode.isUserInteractionEnabled = false
|
||||
|
||||
let tagMask: MessageTags
|
||||
switch type {
|
||||
case .music:
|
||||
|
|
@ -264,7 +302,23 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
|
||||
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
|
||||
self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil })
|
||||
self.historyNode = ChatHistoryListNodeImpl(
|
||||
context: context,
|
||||
updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData),
|
||||
systemStyle: .glass,
|
||||
chatLocation: chatLocation,
|
||||
chatLocationContextHolder: chatLocationContextHolder,
|
||||
adMessagesContext: nil,
|
||||
tag: .tag(tagMask),
|
||||
source: self.source,
|
||||
subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false),
|
||||
controllerInteraction: self.controllerInteraction,
|
||||
selectedMessages: .single(nil),
|
||||
mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch),
|
||||
isChatPreview: false,
|
||||
messageTransitionNode: { return nil
|
||||
}
|
||||
)
|
||||
self.historyNode.clipsToBounds = true
|
||||
|
||||
super.init()
|
||||
|
|
@ -275,6 +329,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.historyNode.preloadPages = true
|
||||
self.historyNode.stackFromBottom = true
|
||||
self.historyNode.areContentAnimationsEnabled = true
|
||||
self.historyNode.contentPositionChanged = { [weak self] offset in
|
||||
self?.updateHistoryContentOffset(offset, transition: .immediate)
|
||||
}
|
||||
self.historyNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
||||
|
|
@ -359,11 +416,18 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
}
|
||||
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.contentNode)
|
||||
self.addSubnode(self.containerContainingNode)
|
||||
self.containerContainingNode.addSubnode(self.contentNode)
|
||||
self.contentNode.addSubnode(self.historyBackgroundNode)
|
||||
self.contentNode.addSubnode(self.historyNode)
|
||||
self.contentNode.addSubnode(self.historyFrameNode)
|
||||
self.contentNode.addSubnode(self.controlsNode)
|
||||
|
||||
self.historyFrameNode.addSubnode(self.historyFrameLeftOverlayNode)
|
||||
self.historyFrameNode.addSubnode(self.historyFrameRightOverlayNode)
|
||||
self.historyFrameNode.addSubnode(self.historyFrameTopOverlayNode)
|
||||
self.historyFrameNode.addSubnode(self.historyFrameTopMaskNode)
|
||||
|
||||
self.historyNode.beganInteractiveDragging = { [weak self] _ in
|
||||
self?.controlsNode.collapse()
|
||||
}
|
||||
|
|
@ -415,11 +479,19 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
copyProtectionEnabled = .single(false)
|
||||
}
|
||||
|
||||
self.savedIdsDisposable = combineLatest(
|
||||
let peer: Signal<EnginePeer?, NoError>
|
||||
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, _) = playlistLocation {
|
||||
peer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: savedMusicContext.peerId))
|
||||
} else {
|
||||
peer = .single(nil)
|
||||
}
|
||||
|
||||
self.dataDisposable = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
context.engine.peers.savedMusicIds(),
|
||||
copyProtectionEnabled
|
||||
).start(next: { [weak self] savedIds, copyProtectionEnabled in
|
||||
copyProtectionEnabled,
|
||||
peer
|
||||
).start(next: { [weak self] savedIds, copyProtectionEnabled, peer in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -428,9 +500,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.savedIdsPromise.set(.single(savedIds))
|
||||
self.copyProtectionEnabled = copyProtectionEnabled
|
||||
self.controlsNode.forceCopyProtected.set(copyProtectionEnabled)
|
||||
self.peer = peer
|
||||
|
||||
let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : .animated(duration: 0.5, curve: .spring)
|
||||
self.updateFloatingHeaderOffset(offset: self.floatingHeaderOffset ?? 0.0, transition: transition)
|
||||
if let validLayout = self.validLayout {
|
||||
self.containerLayoutUpdated(validLayout, transition: transition)
|
||||
}
|
||||
|
|
@ -457,7 +529,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.replacementHistoryNodeReadyDisposable.dispose()
|
||||
self.savedIdsDisposable?.dispose()
|
||||
self.dataDisposable?.dispose()
|
||||
self.saveMediaDisposable?.dispose()
|
||||
}
|
||||
|
||||
|
|
@ -539,7 +611,12 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.itemModalBlocksBackgroundColor
|
||||
self.historyFrameLeftOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
self.historyFrameRightOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
self.historyFrameTopOverlayNode.backgroundColor = self.presentationData.theme.list.modalBlocksBackgroundColor
|
||||
self.historyFrameTopMaskNode.image = PresentationResourcesItemList.cornersImage(self.presentationData.theme.withModalBlocksBackground(), top: true, bottom: false, glass: true)
|
||||
|
||||
self.controlsNode.updatePresentationData(self.presentationData)
|
||||
}
|
||||
|
||||
|
|
@ -685,32 +762,70 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
return self.savedIds?.contains(fileReference.media.fileId.id)
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
insets.bottom = layout.intrinsicInsets.bottom
|
||||
|
||||
if layout.size.width > layout.size.height && self.controlsNode.isExpanded {
|
||||
self.controlsNode.isExpanded = false
|
||||
private func updateHistoryContentOffset(_ offset: ListViewVisibleContentOffset, transition: ContainedViewLayoutTransition) {
|
||||
switch offset {
|
||||
case let .known(value):
|
||||
self.historyContentOffset = value
|
||||
case .none:
|
||||
self.historyContentOffset = 0.0
|
||||
case .unknown:
|
||||
break
|
||||
}
|
||||
|
||||
let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5)
|
||||
self.updateContainerContainingNodeTransform(transition: transition)
|
||||
}
|
||||
|
||||
private func updateContainerContainingNodeTransform(transition: ContainedViewLayoutTransition) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved)
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
transition.updateTransform(layer: self.containerContainingNode.layer, transform: CATransform3DIdentity)
|
||||
transition.updateCornerRadius(node: self.containerContainingNode, cornerRadius: 0.0)
|
||||
return
|
||||
}
|
||||
|
||||
let listTopInset = layoutTopInset + controlsHeight
|
||||
let expandDistance: CGFloat = 78.0
|
||||
let topOffsetFraction = max(0.0, min(1.0, self.historyContentOffset / expandDistance))
|
||||
|
||||
let listNodeSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset)
|
||||
let minScale: CGFloat = (layout.size.width - 6.0 * 2.0) / layout.size.width
|
||||
let minScaledTranslation: CGFloat = (layout.size.height - layout.size.height * minScale) * 0.5 - 6.0
|
||||
|
||||
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
||||
let scale = minScale * (1.0 - topOffsetFraction) + topOffsetFraction
|
||||
let scaledTranslation = minScaledTranslation * (1.0 - topOffsetFraction)
|
||||
|
||||
var containerTransform = CATransform3DIdentity
|
||||
containerTransform = CATransform3DTranslate(containerTransform, 0.0, scaledTranslation, 0.0)
|
||||
containerTransform = CATransform3DScale(containerTransform, scale, scale, scale)
|
||||
|
||||
transition.updateTransform(layer: self.containerContainingNode.layer, transform: containerTransform)
|
||||
transition.updateCornerRadius(node: self.containerContainingNode, cornerRadius: layout.deviceMetrics.screenCornerRadius)
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrameAsPositionAndBounds(node: self.containerContainingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let controlsHeight = self.controlsNode.updateLayout(width: layout.size.width, leftInset: 0.0, rightInset: 0.0, bottomInset: layout.intrinsicInsets.bottom, maxHeight: layout.size.height, savedMusic: self.isSaved, transition: transition)
|
||||
|
||||
let controlsFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - controlsHeight), size: CGSize(width: layout.size.width, height: controlsHeight))
|
||||
transition.updateFrame(node: self.controlsNode, frame: controlsFrame)
|
||||
|
||||
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
|
||||
var insets = UIEdgeInsets()
|
||||
insets.left = 16.0
|
||||
insets.right = 16.0
|
||||
insets.bottom = 0.0
|
||||
|
||||
let headerHeight: CGFloat = 78.0
|
||||
let listTopInset = layoutTopInset + headerHeight
|
||||
let listNodeSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - controlsHeight)
|
||||
|
||||
insets.top = max(0.0, listNodeSize.height - floor(62.0 * 3.5))
|
||||
|
||||
var itemOffsetInsets = insets
|
||||
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
||||
|
|
@ -718,7 +833,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
itemOffsetInsets.bottom = 0.0
|
||||
insets = itemOffsetInsets
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.historyNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize))
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
|
|
@ -728,6 +842,101 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil))
|
||||
replacementHistoryNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
}
|
||||
|
||||
|
||||
let leftControlItems: [GlassControlGroupComponent.Item] = [
|
||||
GlassControlGroupComponent.Item(
|
||||
id: AnyHashable("close"),
|
||||
content: .icon("Navigation/Close"),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestDismiss()
|
||||
}
|
||||
)
|
||||
]
|
||||
var rightControlItems: [GlassControlGroupComponent.Item] = []
|
||||
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, _) = playlistLocation, savedMusicContext.peerId == self.context.account.peerId {
|
||||
rightControlItems.append(
|
||||
GlassControlGroupComponent.Item(
|
||||
id: AnyHashable("add"),
|
||||
content: .icon("Navigation/Add"),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestAdd()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
rightControlItems.append(
|
||||
GlassControlGroupComponent.Item(
|
||||
id: AnyHashable("search"),
|
||||
content: .icon("Navigation/Search"),
|
||||
action: {
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
let headerInset: CGFloat = 16.0
|
||||
let headerButtonsSize = self.headerButtons.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: AnyComponent(
|
||||
GlassControlPanelComponent(
|
||||
theme: self.presentationData.theme,
|
||||
leftItem: GlassControlPanelComponent.Item(
|
||||
items: leftControlItems,
|
||||
background: .panel
|
||||
),
|
||||
centralItem: nil,
|
||||
rightItem: GlassControlPanelComponent.Item(
|
||||
items: rightControlItems,
|
||||
background: .panel
|
||||
),
|
||||
centerAlignmentIfPossible: true,
|
||||
isDark: self.presentationData.theme.overallDarkAppearance
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width - headerInset * 2.0, height: 44.0)
|
||||
)
|
||||
if let headerButtonsView = self.headerButtons.view {
|
||||
if headerButtonsView.superview == nil {
|
||||
self.historyFrameNode.view.addSubview(headerButtonsView)
|
||||
}
|
||||
headerButtonsView.frame = CGRect(origin: CGPoint(x: headerInset, y: headerInset), size: headerButtonsSize)
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let titleString: String
|
||||
if let peer = self.peer {
|
||||
if peer.id == self.context.account.peerId {
|
||||
titleString = "Your Playlist"
|
||||
} else {
|
||||
titleString = "\(peer.compactDisplayTitle)'s Playlist"
|
||||
}
|
||||
} else {
|
||||
titleString = "Playlist"
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: titleString, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width - 70.0 * 2.0, height: headerHeight)
|
||||
)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.historyFrameNode.view.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: 29.0), size: titleSize)
|
||||
}
|
||||
|
||||
self.updateHistoryContentOffset(self.historyNode.visibleContentOffset(), transition: transition)
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
|
|
@ -747,23 +956,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.controlsNode.bounds.contains(self.view.convert(point, to: self.controlsNode.view)) {
|
||||
let controlsHitTest = self.controlsNode.view.hitTest(self.view.convert(point, to: self.controlsNode.view), with: event)
|
||||
if controlsHitTest == nil {
|
||||
if self.controlsNode.frame.maxY > self.historyNode.frame.minY {
|
||||
return self.historyNode.view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = super.hitTest(point, with: event)
|
||||
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
}
|
||||
if point.y < self.controlsNode.frame.minY {
|
||||
return self.dimNode.view
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -839,41 +1035,35 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
}
|
||||
|
||||
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard let validLayout = self.validLayout else {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
self.floatingHeaderOffset = offset
|
||||
|
||||
let layoutTopInset: CGFloat = max(validLayout.statusBarHeight ?? 0.0, validLayout.safeInsets.top)
|
||||
let sideInset: CGFloat = 16.0
|
||||
let headerHeight: CGFloat = 78.0
|
||||
|
||||
let maxHeight = validLayout.size.height - layoutTopInset - floor(56.0 * 0.5)
|
||||
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
|
||||
|
||||
let rawControlsOffset = offset + layoutTopInset
|
||||
let backgroundOffset = max(layoutTopInset, rawControlsOffset)
|
||||
|
||||
let controlsHeight = self.controlsNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, hasSectionHeader: true, savedMusic: self.isSaved, transition: transition)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: backgroundOffset + headerHeight), size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
|
||||
let listTopInset = layoutTopInset + controlsHeight
|
||||
let frameFrame = CGRect(origin: CGPoint(x: 0.0, y: backgroundOffset), size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
let topOverlayFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: headerHeight))
|
||||
let leftOverlayFrame = CGRect(origin: CGPoint(x: 0.0, y: headerHeight), size: CGSize(width: sideInset, height: layout.size.height))
|
||||
let rightOverlayFrame = CGRect(origin: CGPoint(x: layout.size.width - sideInset, y: headerHeight), size: CGSize(width: sideInset, height: layout.size.height))
|
||||
|
||||
let rawControlsOffset = offset + listTopInset - controlsHeight
|
||||
let controlsOffset = max(layoutTopInset, rawControlsOffset)
|
||||
let isOverscrolling = rawControlsOffset <= layoutTopInset
|
||||
let controlsFrame = CGRect(origin: CGPoint(x: 0.0, y: controlsOffset), size: CGSize(width: validLayout.size.width, height: controlsHeight))
|
||||
|
||||
let previousFrame = self.controlsNode.frame
|
||||
|
||||
if !controlsFrame.equalTo(previousFrame) {
|
||||
self.controlsNode.frame = controlsFrame
|
||||
|
||||
let positionDelta = CGPoint(x: controlsFrame.minX - previousFrame.minX, y: controlsFrame.minY - previousFrame.minY)
|
||||
|
||||
transition.animateOffsetAdditive(node: self.controlsNode, offset: positionDelta.y)
|
||||
self.historyFrameNode.frame = frameFrame
|
||||
self.historyFrameTopOverlayNode.frame = topOverlayFrame
|
||||
self.historyFrameLeftOverlayNode.frame = leftOverlayFrame
|
||||
self.historyFrameRightOverlayNode.frame = rightOverlayFrame
|
||||
if let image = self.historyFrameTopMaskNode.image {
|
||||
self.historyFrameTopMaskNode.frame = CGRect(origin: CGPoint(x: sideInset, y: topOverlayFrame.maxY), size: CGSize(width: layout.size.width - sideInset * 2.0, height: image.size.height))
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: self.controlsNode.separatorNode, alpha: isOverscrolling ? 1.0 : 0.0)
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: controlsFrame.maxY), size: CGSize(width: validLayout.size.width, height: validLayout.size.height))
|
||||
|
||||
let previousBackgroundFrame = self.historyBackgroundNode.frame
|
||||
|
||||
if !backgroundFrame.equalTo(previousBackgroundFrame) {
|
||||
self.historyBackgroundNode.frame = backgroundFrame
|
||||
self.historyBackgroundContentNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
|
||||
|
|
@ -896,7 +1086,22 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
}
|
||||
|
||||
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil })
|
||||
let historyNode = ChatHistoryListNodeImpl(
|
||||
context: self.context,
|
||||
updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData),
|
||||
systemStyle: .glass,
|
||||
chatLocation: self.chatLocation,
|
||||
chatLocationContextHolder: chatLocationContextHolder,
|
||||
adMessagesContext: nil,
|
||||
tag: .tag(tagMask),
|
||||
source: self.source,
|
||||
subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false),
|
||||
controllerInteraction: self.controllerInteraction,
|
||||
selectedMessages: .single(nil),
|
||||
mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch),
|
||||
isChatPreview: false,
|
||||
messageTransitionNode: { return nil
|
||||
})
|
||||
historyNode.clipsToBounds = true
|
||||
historyNode.preloadPages = true
|
||||
historyNode.stackFromBottom = true
|
||||
|
|
@ -909,19 +1114,14 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
insets.bottom = layout.intrinsicInsets.bottom
|
||||
|
||||
let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5)
|
||||
|
||||
let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved)
|
||||
|
||||
let listTopInset = layoutTopInset + controlsHeight
|
||||
|
||||
insets.left = 16.0
|
||||
insets.right = 16.0
|
||||
insets.bottom = 0.0
|
||||
|
||||
let listTopInset = layoutTopInset
|
||||
let listNodeSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset)
|
||||
|
||||
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
||||
insets.top = max(0.0, listNodeSize.height - floor(62.0 * 3.5))
|
||||
|
||||
historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize)
|
||||
|
||||
|
|
@ -944,19 +1144,14 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.contentNode.insertSubnode(replacementHistoryNode, belowSubnode: self.historyNode)
|
||||
self.historyNode = replacementHistoryNode
|
||||
self.setupReordering()
|
||||
self.updateHistoryContentOffset(replacementHistoryNode.visibleContentOffset(), transition: .immediate)
|
||||
|
||||
if let validLayout = self.validLayout, let offset = self.replacementHistoryNodeFloatingOffset, let previousOffset = self.floatingHeaderOffset {
|
||||
let offsetDelta = offset - previousOffset
|
||||
|
||||
let layoutTopInset: CGFloat = max(validLayout.statusBarHeight ?? 0.0, validLayout.safeInsets.top)
|
||||
|
||||
let maxHeight = validLayout.size.height - layoutTopInset - floor(56.0 * 0.5)
|
||||
|
||||
let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved)
|
||||
|
||||
let listTopInset = layoutTopInset + controlsHeight
|
||||
|
||||
let controlsBottomOffset = max(layoutTopInset, offset + listTopInset)
|
||||
let controlsBottomOffset = max(layoutTopInset, offset)
|
||||
|
||||
let previousBackgroundNode = ASDisplayNode()
|
||||
previousBackgroundNode.isLayerBacked = true
|
||||
|
|
@ -984,6 +1179,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
||||
}
|
||||
}
|
||||
self.historyNode.contentPositionChanged = { [weak self] offset in
|
||||
self?.updateHistoryContentOffset(offset, transition: .immediate)
|
||||
}
|
||||
|
||||
self.historyNode.endedInteractiveDragging = { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
|
|
@ -1007,19 +1205,14 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
insets.bottom = layout.intrinsicInsets.bottom
|
||||
|
||||
let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5)
|
||||
|
||||
let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved)
|
||||
|
||||
let listTopInset = layoutTopInset + controlsHeight
|
||||
insets.left = 16.0
|
||||
insets.right = 16.0
|
||||
insets.bottom = 0.0
|
||||
|
||||
let listTopInset = layoutTopInset
|
||||
let listNodeSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset)
|
||||
|
||||
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
||||
insets.top = max(0.0, listNodeSize.height - floor(62.0 * 3.5))
|
||||
|
||||
var itemOffsetInsets = insets
|
||||
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
||||
|
|
@ -1035,6 +1228,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
|
||||
self.historyNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
|
||||
self.updateHistoryContentOffset(self.historyNode.visibleContentOffset(), transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1170,16 +1365,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
)
|
||||
}
|
||||
|
||||
// items.append(
|
||||
// .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_Forward, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
// f(.default)
|
||||
//
|
||||
// if let _ = self {
|
||||
//
|
||||
// }
|
||||
// }))
|
||||
// )
|
||||
|
||||
var canDelete = false
|
||||
if case .custom = self.source {
|
||||
if self.savedIds?.contains(file.fileId.id) == true {
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ private func normalizeValue(_ value: CGFloat) -> CGFloat {
|
|||
private func generateBackground(theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 20.0, height: 10.0 + 8.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: -4.0), blur: 20.0, color: UIColor(white: 0.0, alpha: 0.3).cgColor)
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: -4.0), blur: 30.0, color: UIColor(white: 0.0, alpha: 0.2).cgColor)
|
||||
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: CGSize(width: 20.0, height: 20.0)))
|
||||
context.fill(CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: CGSize(width: 20.0, height: 20.0)))
|
||||
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 10 + 8)
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +111,8 @@ private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat {
|
|||
return size.width
|
||||
}
|
||||
|
||||
private let titleFont = Font.semibold(19.0)
|
||||
private let descriptionFont = Font.regular(17.0)
|
||||
private let titleFont = Font.semibold(17.0)
|
||||
private let descriptionFont = Font.regular(15.0)
|
||||
|
||||
private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?, Bool, NSAttributedString?) {
|
||||
var titleString: NSAttributedString?
|
||||
|
|
@ -135,13 +135,13 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, pres
|
|||
}
|
||||
|
||||
titleString = NSAttributedString(string: titleText, font: titleFont, textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
descriptionString = NSAttributedString(string: subtitleText, font: descriptionFont, textColor: hasArtist ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemSecondaryTextColor)
|
||||
descriptionString = NSAttributedString(string: subtitleText, font: descriptionFont, textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
}
|
||||
|
||||
return (titleString, descriptionString, hasArtist, captionString)
|
||||
}
|
||||
|
||||
final class OverlayPlayerControlsNode: ASDisplayNode {
|
||||
final class OverlayAudioPlayerControlsNode: ASDisplayNode {
|
||||
private let accountManager: AccountManager<TelegramAccountManagerTypes>
|
||||
private let account: Account
|
||||
private let engine: TelegramEngine
|
||||
|
|
@ -162,7 +162,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
private let artistButton: HighlightTrackingButtonNode
|
||||
|
||||
private var profileAudio: ComponentView<Empty>?
|
||||
private var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
private let scrubberNode: MediaPlayerScrubbingNode
|
||||
private let leftDurationLabel: MediaPlayerTimeTextNode
|
||||
|
|
@ -190,10 +189,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
private let rateButton: AudioRateButton
|
||||
|
||||
let separatorNode: ASDisplayNode
|
||||
|
||||
private let sectionBackground: ASDisplayNode
|
||||
private let sectionTitle: ComponentView<Empty>
|
||||
|
||||
|
||||
var isExpanded = false
|
||||
var updateIsExpanded: (() -> Void)?
|
||||
|
||||
|
|
@ -219,9 +215,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
private var statusDisposable: Disposable?
|
||||
private var chapterDisposable: Disposable?
|
||||
|
||||
private var peerName: String?
|
||||
private var peerDisposable: Disposable?
|
||||
|
||||
private var previousCaption: NSAttributedString?
|
||||
private var chaptersPromise = ValuePromise<[MediaPlayerScrubbingChapter]>([])
|
||||
private var currentChapter: MediaPlayerScrubbingChapter?
|
||||
|
|
@ -236,7 +229,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
private var currentDuration: Double = 0.0
|
||||
private var currentPosition: Double = 0.0
|
||||
|
||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, hasSectionHeader: Bool, savedMusic: Bool?)?
|
||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, maxHeight: CGFloat, savedMusic: Bool?)?
|
||||
|
||||
init(account: Account, engine: TelegramEngine, accountManager: AccountManager<TelegramAccountManagerTypes>, presentationData: PresentationData, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError>, chatLocation: ChatLocation, source: ChatHistoryListSource) {
|
||||
self.accountManager = accountManager
|
||||
|
|
@ -271,13 +264,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.artistButton = HighlightTrackingButtonNode()
|
||||
|
||||
self.shareNode = HighlightableButtonNode()
|
||||
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemAccentColor), for: [])
|
||||
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemPrimaryTextColor), for: [])
|
||||
|
||||
self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 3.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: presentationData.theme.list.controlSecondaryColor, foregroundColor: presentationData.theme.list.itemAccentColor, bufferingColor: presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), chapters: []))
|
||||
self.leftDurationLabel = MediaPlayerTimeTextNode(textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 7.0, lineCap: .round, scrubberHandle: .none, backgroundColor: presentationData.theme.list.controlSecondaryColor, foregroundColor: presentationData.theme.list.itemPrimaryTextColor, bufferingColor: presentationData.theme.list.itemPrimaryTextColor.withAlphaComponent(0.4), chapters: []))
|
||||
self.leftDurationLabel = MediaPlayerTimeTextNode(textColor: presentationData.theme.list.itemSecondaryTextColor, textFont: Font.medium(12.0))
|
||||
self.leftDurationLabel.displaysAsynchronously = false
|
||||
self.leftDurationLabel.keepPreviousValueOnEmptyState = true
|
||||
self.rightDurationLabel = MediaPlayerTimeTextNode(textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
self.rightDurationLabel = MediaPlayerTimeTextNode(textColor: presentationData.theme.list.itemSecondaryTextColor, textFont: Font.medium(12.0))
|
||||
self.rightDurationLabel.displaysAsynchronously = false
|
||||
self.rightDurationLabel.mode = .reversed
|
||||
self.rightDurationLabel.alignment = .right
|
||||
|
|
@ -316,16 +309,11 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.separatorNode.isLayerBacked = true
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemPlainSeparatorColor
|
||||
|
||||
self.sectionBackground = ASDisplayNode()
|
||||
self.sectionBackground.backgroundColor = presentationData.theme.chatList.sectionHeaderFillColor
|
||||
|
||||
self.sectionTitle = ComponentView<Empty>()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.collapseNode)
|
||||
//self.addSubnode(self.collapseNode)
|
||||
|
||||
self.addSubnode(self.albumArtNode)
|
||||
//self.addSubnode(self.titleNode)
|
||||
|
|
@ -346,8 +334,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.addSubnode(self.playPauseButton)
|
||||
self.playPauseButton.addSubnode(self.playPauseIconNode)
|
||||
|
||||
self.addSubnode(self.sectionBackground)
|
||||
self.addSubnode(self.separatorNode)
|
||||
//self.addSubnode(self.separatorNode)
|
||||
|
||||
let accountId = account.id
|
||||
let delayedStatus = status
|
||||
|
|
@ -375,33 +362,31 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.leftDurationLabel.status = mappedStatus
|
||||
self.rightDurationLabel.status = mappedStatus
|
||||
|
||||
self.scrubbingDisposable = (self.scrubberNode.scrubbingPosition
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let leftDurationLabelPushed: Bool
|
||||
let rightDurationLabelPushed: Bool
|
||||
let infoNodePushed: Bool
|
||||
if let value = value {
|
||||
leftDurationLabelPushed = value < 0.16
|
||||
rightDurationLabelPushed = value > (strongSelf.rateButton.isHidden ? 0.84 : 0.74)
|
||||
infoNodePushed = value >= 0.16 && value <= 0.84
|
||||
} else {
|
||||
leftDurationLabelPushed = false
|
||||
rightDurationLabelPushed = false
|
||||
infoNodePushed = false
|
||||
}
|
||||
if leftDurationLabelPushed != strongSelf.leftDurationLabelPushed || rightDurationLabelPushed != strongSelf.rightDurationLabelPushed || infoNodePushed != strongSelf.infoNodePushed {
|
||||
strongSelf.leftDurationLabelPushed = leftDurationLabelPushed
|
||||
strongSelf.rightDurationLabelPushed = rightDurationLabelPushed
|
||||
strongSelf.infoNodePushed = infoNodePushed
|
||||
|
||||
if let layout = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .animated(duration: 0.35, curve: .spring))
|
||||
}
|
||||
}
|
||||
})
|
||||
// self.scrubbingDisposable = (self.scrubberNode.scrubbingPosition
|
||||
// |> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// let leftDurationLabelPushed: Bool
|
||||
// let rightDurationLabelPushed: Bool
|
||||
// let infoNodePushed: Bool
|
||||
// if let value = value {
|
||||
// leftDurationLabelPushed = value < 0.16
|
||||
// rightDurationLabelPushed = value > (strongSelf.rateButton.isHidden ? 0.84 : 0.74)
|
||||
// infoNodePushed = value >= 0.16 && value <= 0.84
|
||||
// } else {
|
||||
// leftDurationLabelPushed = false
|
||||
// rightDurationLabelPushed = false
|
||||
// infoNodePushed = false
|
||||
// }
|
||||
// if leftDurationLabelPushed != strongSelf.leftDurationLabelPushed || rightDurationLabelPushed != strongSelf.rightDurationLabelPushed || infoNodePushed != strongSelf.infoNodePushed {
|
||||
// strongSelf.leftDurationLabelPushed = leftDurationLabelPushed
|
||||
// strongSelf.rightDurationLabelPushed = rightDurationLabelPushed
|
||||
// strongSelf.infoNodePushed = infoNodePushed
|
||||
//
|
||||
// strongSelf.requestLayout(transition: .animated(duration: 0.35, curve: .spring))
|
||||
// }
|
||||
// })
|
||||
|
||||
self.statusDisposable = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
|
|
@ -478,9 +463,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
let duration = value.status.duration
|
||||
if duration != strongSelf.currentDuration && !duration.isZero {
|
||||
strongSelf.currentDuration = duration
|
||||
if let layout = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate)
|
||||
}
|
||||
strongSelf.requestLayout(transition: .immediate)
|
||||
}
|
||||
|
||||
strongSelf.rateButton.isHidden = rateButtonIsHidden
|
||||
|
|
@ -527,20 +510,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
strongSelf.requestLayout?(.animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
})
|
||||
|
||||
if case .custom = self.source, case let .peer(peerId) = self.chatLocation, peerId != account.peerId {
|
||||
self.peerDisposable = (engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
self.peerName = peer.compactDisplayTitle
|
||||
if let layout = self.validLayout {
|
||||
let _ = self.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
self.chapterDisposable = combineLatest(queue: Queue.mainQueue(), mappedStatus, self.chaptersPromise.get())
|
||||
.startStrict(next: { [weak self] status, chapters in
|
||||
if let strongSelf = self, status.duration > 1.0, chapters.count > 0 {
|
||||
|
|
@ -585,10 +555,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
strongSelf.infoNode.attributedText = NSAttributedString(string: chapter.title, font: Font.regular(13.0), textColor: strongSelf.presentationData.theme.list.itemSecondaryTextColor)
|
||||
|
||||
if let layout = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate)
|
||||
}
|
||||
strongSelf.requestLayout(transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -632,7 +599,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.statusDisposable?.dispose()
|
||||
self.chapterDisposable?.dispose()
|
||||
self.scrubbingDisposable?.dispose()
|
||||
self.peerDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
|
@ -740,8 +706,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
|
||||
self.backgroundNode.image = generateBackground(theme: presentationData.theme)
|
||||
self.collapseNode.setImage(generateCollapseIcon(theme: presentationData.theme), for: [])
|
||||
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemAccentColor), for: [])
|
||||
self.scrubberNode.updateColors(backgroundColor: presentationData.theme.list.controlSecondaryColor, foregroundColor: presentationData.theme.list.itemAccentColor)
|
||||
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemPrimaryTextColor), for: [])
|
||||
self.scrubberNode.updateColors(backgroundColor: presentationData.theme.list.controlSecondaryColor, foregroundColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.leftDurationLabel.textColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
self.rightDurationLabel.textColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
self.backwardButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Previous"), color: presentationData.theme.list.itemPrimaryTextColor)
|
||||
|
|
@ -759,22 +725,23 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.updateRateButton(rate)
|
||||
}
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemPlainSeparatorColor
|
||||
self.sectionBackground.backgroundColor = presentationData.theme.chatList.sectionHeaderFillColor
|
||||
|
||||
self.updateLabels(transition: .immediate)
|
||||
}
|
||||
|
||||
private func updateLabels(transition: ContainedViewLayoutTransition) {
|
||||
guard let (width, leftInset, rightInset, maxHeight, _, _) = self.validLayout else {
|
||||
guard let (width, leftInset, rightInset, bottomInset, maxHeight, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: false, savedMusic: nil)
|
||||
let panelHeight = OverlayAudioPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, maxHeight: maxHeight, isExpanded: self.isExpanded, savedMusic: nil)
|
||||
|
||||
let sideInset: CGFloat = 20.0
|
||||
|
||||
let infoLabelsLeftInset: CGFloat = 60.0
|
||||
let infoLabelsLeftInset: CGFloat = 53.0
|
||||
let infoLabelsRightInset: CGFloat = 32.0
|
||||
|
||||
let infoVerticalOrigin: CGFloat = panelHeight - OverlayPlayerControlsNode.basePanelHeight + 36.0
|
||||
let infoVerticalOrigin: CGFloat = panelHeight - OverlayAudioPlayerControlsNode.basePanelHeight + 21.0
|
||||
|
||||
let (titleString, descriptionString, hasArtist, caption) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
|
||||
|
||||
|
|
@ -782,7 +749,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.previousCaption = caption
|
||||
let chapters = caption.flatMap { parseMediaPlayerChapters($0) } ?? []
|
||||
self.chaptersPromise.set(chapters)
|
||||
self.scrubberNode.updateContent(.standard(lineHeight: 3.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: self.presentationData.theme.list.controlSecondaryColor, foregroundColor: self.presentationData.theme.list.itemAccentColor, bufferingColor: self.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), chapters: chapters))
|
||||
self.scrubberNode.updateContent(.standard(lineHeight: 7.0, lineCap: .round, scrubberHandle: .none, backgroundColor: self.presentationData.theme.list.controlSecondaryColor, foregroundColor: self.presentationData.theme.list.itemPrimaryTextColor, bufferingColor: self.presentationData.theme.list.itemPrimaryTextColor.withAlphaComponent(0.4), chapters: chapters))
|
||||
}
|
||||
|
||||
self.artistButton.isUserInteractionEnabled = hasArtist
|
||||
|
|
@ -811,7 +778,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - titleLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 1.0), size: titleLayout.size))
|
||||
let _ = titleApply()
|
||||
|
||||
let descriptionFrame = CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 25.0), size: descriptionLayout.size)
|
||||
let descriptionFrame = CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 24.0), size: descriptionLayout.size)
|
||||
transition.updateFrame(node: self.descriptionNode, frame: descriptionFrame)
|
||||
let _ = descriptionApply()
|
||||
|
||||
|
|
@ -874,51 +841,54 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.rateButton.setContent(.image(optionsRateImage(rate: rate.stringValue.uppercased(), color: self.presentationData.theme.list.itemSecondaryTextColor)))
|
||||
}
|
||||
|
||||
static let basePanelHeight: CGFloat = 220.0
|
||||
static let sectionHeaderHeight: CGFloat = 28.0
|
||||
static let basePanelHeight: CGFloat = 164.0
|
||||
|
||||
static func heightForLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, isExpanded: Bool, hasSectionHeader: Bool, savedMusic: Bool?) -> CGFloat {
|
||||
var panelHeight: CGFloat = OverlayPlayerControlsNode.basePanelHeight
|
||||
static func heightForLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, maxHeight: CGFloat, isExpanded: Bool, savedMusic: Bool?) -> CGFloat {
|
||||
var panelHeight: CGFloat = OverlayAudioPlayerControlsNode.basePanelHeight
|
||||
if isExpanded {
|
||||
let sideInset: CGFloat = 20.0
|
||||
panelHeight += width - leftInset - rightInset - sideInset * 2.0 + 24.0
|
||||
}
|
||||
var height = min(panelHeight, maxHeight)
|
||||
if hasSectionHeader {
|
||||
height += sectionHeaderHeight
|
||||
}
|
||||
if let savedMusic {
|
||||
height += savedMusic ? 38.0 : 70.0
|
||||
if let _ = savedMusic {
|
||||
height += 70.0
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0)
|
||||
height += buttonInsets.bottom
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, hasSectionHeader: Bool, savedMusic: Bool?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let previousSavedMusic = self.validLayout?.savedMusic
|
||||
self.validLayout = (width, leftInset, rightInset, maxHeight, hasSectionHeader, savedMusic)
|
||||
func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let _ = self.updateLayout(width: layout.width, leftInset: layout.leftInset, rightInset: layout.rightInset, bottomInset: layout.bottomInset, maxHeight: layout.maxHeight, savedMusic: layout.savedMusic, transition: transition)
|
||||
}
|
||||
|
||||
let finalPanelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: hasSectionHeader, savedMusic: savedMusic)
|
||||
let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: false, savedMusic: nil)
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, maxHeight: CGFloat, savedMusic: Bool?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = (width, leftInset, rightInset, bottomInset, maxHeight, savedMusic)
|
||||
|
||||
let finalPanelHeight = OverlayAudioPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, maxHeight: maxHeight, isExpanded: self.isExpanded, savedMusic: savedMusic)
|
||||
let panelHeight = OverlayAudioPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, maxHeight: maxHeight, isExpanded: self.isExpanded, savedMusic: nil)
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
transition.updateFrame(node: self.collapseNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: CGSize(width: width, height: 30.0)))
|
||||
|
||||
let sideInset: CGFloat = 20.0
|
||||
let sideButtonsInset: CGFloat = sideInset + 36.0
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideButtonsInset: CGFloat = sideInset + 60.0
|
||||
|
||||
|
||||
let infoVerticalOrigin: CGFloat = panelHeight - OverlayPlayerControlsNode.basePanelHeight + 36.0
|
||||
let infoVerticalOrigin: CGFloat = panelHeight - OverlayAudioPlayerControlsNode.basePanelHeight + 21.0
|
||||
|
||||
self.updateLabels(transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.shareNode, frame: CGRect(origin: CGPoint(x: width - rightInset - sideInset - 32.0, y: infoVerticalOrigin + 2.0), size: CGSize(width: 42.0, height: 42.0)))
|
||||
transition.updateFrame(node: self.shareNode, frame: CGRect(origin: CGPoint(x: width - rightInset - sideInset - 34.0, y: infoVerticalOrigin), size: CGSize(width: 42.0, height: 42.0)))
|
||||
|
||||
let albumArtSize = CGSize(width: 48.0, height: 48.0)
|
||||
let albumArtSize = CGSize(width: 46.0, height: 46.0)
|
||||
let makeAlbumArtLayout = self.albumArtNode.asyncLayout()
|
||||
let applyAlbumArt = makeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 10.0), imageSize: albumArtSize, boundingSize: albumArtSize, intrinsicInsets: UIEdgeInsets()))
|
||||
applyAlbumArt()
|
||||
let albumArtFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: infoVerticalOrigin - 1.0), size: albumArtSize)
|
||||
let albumArtFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: infoVerticalOrigin - 3.0), size: albumArtSize)
|
||||
let previousAlbumArtNodeFrame = self.albumArtNode.frame
|
||||
transition.updateFrame(node: self.albumArtNode, frame: albumArtFrame)
|
||||
|
||||
|
|
@ -930,9 +900,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
} else {
|
||||
animateIn = true
|
||||
largeAlbumArtNode = TransformImageNode()
|
||||
if self.isNodeLoaded {
|
||||
largeAlbumArtNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.albumArtTap(_:))))
|
||||
}
|
||||
self.largeAlbumArtNode = largeAlbumArtNode
|
||||
self.addSubnode(largeAlbumArtNode)
|
||||
if self.currentAlbumArtInitialized {
|
||||
|
|
@ -940,7 +907,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
let albumArtHeight = max(1.0, panelHeight - OverlayPlayerControlsNode.basePanelHeight - 24.0)
|
||||
let albumArtHeight = max(1.0, panelHeight - OverlayAudioPlayerControlsNode.basePanelHeight - 24.0)
|
||||
|
||||
let largeAlbumArtSize = CGSize(width: albumArtHeight, height: albumArtHeight)
|
||||
let makeLargeAlbumArtLayout = largeAlbumArtNode.asyncLayout()
|
||||
|
|
@ -988,15 +955,16 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
let scrubberVerticalOrigin: CGFloat = infoVerticalOrigin + 64.0
|
||||
let scrubberVerticalOrigin: CGFloat = infoVerticalOrigin + 58.0
|
||||
let scrubberInset: CGFloat = 9.0
|
||||
|
||||
transition.updateFrame(node: self.scrubberNode, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: scrubberVerticalOrigin - 8.0), size: CGSize(width: width - sideInset * 2.0 - leftInset - rightInset, height: 10.0 + 8.0 * 2.0)))
|
||||
transition.updateFrame(node: self.scrubberNode, frame: CGRect(origin: CGPoint(x: sideInset + scrubberInset, y: scrubberVerticalOrigin - 8.0), size: CGSize(width: width - (sideInset + scrubberInset) * 2.0, height: 10.0 + 8.0 * 2.0)))
|
||||
|
||||
let leftLabelVerticalOffset: CGFloat = self.leftDurationLabelPushed ? 6.0 : 0.0
|
||||
transition.updateFrame(node: self.leftDurationLabel, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: scrubberVerticalOrigin + 14.0 + leftLabelVerticalOffset), size: CGSize(width: 100.0, height: 20.0)))
|
||||
transition.updateFrame(node: self.leftDurationLabel, frame: CGRect(origin: CGPoint(x: leftInset + sideInset + scrubberInset, y: scrubberVerticalOrigin + 19.0 + leftLabelVerticalOffset), size: CGSize(width: 100.0, height: 20.0)))
|
||||
|
||||
let rightLabelVerticalOffset: CGFloat = self.rightDurationLabelPushed ? 6.0 : 0.0
|
||||
transition.updateFrame(node: self.rightDurationLabel, frame: CGRect(origin: CGPoint(x: width - sideInset - rightInset - 100.0, y: scrubberVerticalOrigin + 14.0 + rightLabelVerticalOffset), size: CGSize(width: 100.0, height: 20.0)))
|
||||
transition.updateFrame(node: self.rightDurationLabel, frame: CGRect(origin: CGPoint(x: width - sideInset - rightInset - 100.0 - scrubberInset, y: scrubberVerticalOrigin + 19.0 + rightLabelVerticalOffset), size: CGSize(width: 100.0, height: 20.0)))
|
||||
|
||||
let infoLabelVerticalOffset: CGFloat = self.infoNodePushed ? 6.0 : 0.0
|
||||
|
||||
|
|
@ -1012,10 +980,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
|
||||
let buttonSize = CGSize(width: 64.0, height: 64.0)
|
||||
let buttonsWidth = min(width - leftInset - rightInset - sideButtonsInset * 2.0, 320.0)
|
||||
let buttonsRect = CGRect(origin: CGPoint(x: floor((width - buttonsWidth) / 2.0), y: scrubberVerticalOrigin + 36.0), size: CGSize(width: buttonsWidth, height: buttonSize.height))
|
||||
let buttonsRect = CGRect(origin: CGPoint(x: floor((width - buttonsWidth) / 2.0), y: scrubberVerticalOrigin + 26.0), size: CGSize(width: buttonsWidth, height: buttonSize.height))
|
||||
|
||||
transition.updateFrame(node: self.orderButton, frame: CGRect(origin: CGPoint(x: leftInset + sideInset - 22.0, y: buttonsRect.minY), size: buttonSize))
|
||||
transition.updateFrame(node: self.loopingButton, frame: CGRect(origin: CGPoint(x: width - rightInset - sideInset - buttonSize.width + 22.0, y: buttonsRect.minY), size: buttonSize))
|
||||
transition.updateFrame(node: self.orderButton, frame: CGRect(origin: CGPoint(x: leftInset + sideInset - 12.0, y: buttonsRect.minY), size: buttonSize))
|
||||
transition.updateFrame(node: self.loopingButton, frame: CGRect(origin: CGPoint(x: width - rightInset - sideInset - buttonSize.width + 12.0, y: buttonsRect.minY), size: buttonSize))
|
||||
|
||||
transition.updateFrame(node: self.backwardButton, frame: CGRect(origin: buttonsRect.origin, size: buttonSize))
|
||||
transition.updateFrame(node: self.forwardButton, frame: CGRect(origin: CGPoint(x: buttonsRect.maxX - buttonSize.width, y: buttonsRect.minY), size: buttonSize))
|
||||
|
|
@ -1023,64 +991,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
let playPauseFrame = CGRect(origin: CGPoint(x: buttonsRect.minX + floor((buttonsRect.width - buttonSize.width) / 2.0), y: buttonsRect.minY), size: buttonSize)
|
||||
transition.updateFrame(node: self.playPauseButton, frame: playPauseFrame)
|
||||
transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: -6.0, y: -6.0), size: CGSize(width: 76.0, height: 76.0)))
|
||||
|
||||
var sectionHeaderTransition = transition
|
||||
if self.sectionTitle.view?.superview == nil {
|
||||
sectionHeaderTransition = .immediate
|
||||
}
|
||||
|
||||
sectionHeaderTransition.updateFrame(node: self.sectionBackground, frame: CGRect(origin: CGPoint(x: 0.0, y: finalPanelHeight - OverlayPlayerControlsNode.sectionHeaderHeight), size: CGSize(width: width, height: OverlayPlayerControlsNode.sectionHeaderHeight)))
|
||||
|
||||
self.separatorNode.isHidden = hasSectionHeader
|
||||
|
||||
if hasSectionHeader {
|
||||
let sideInset: CGFloat = 16.0
|
||||
var sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_ThisChat
|
||||
if let peerName = self.peerName {
|
||||
sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusic(peerName.uppercased()).string
|
||||
} else if case .custom = self.source {
|
||||
sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusicYou
|
||||
}
|
||||
let sectionTitleSize = self.sectionTitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: sectionTitle, font: Font.regular(13.0), textColor: self.presentationData.theme.chatList.sectionHeaderTextColor)),
|
||||
truncationType: .middle
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: width - leftInset - rightInset - sideInset * 2.0, height: OverlayPlayerControlsNode.sectionHeaderHeight)
|
||||
)
|
||||
if let sectionTitleView = self.sectionTitle.view {
|
||||
if sectionTitleView.superview == nil {
|
||||
self.view.addSubview(sectionTitleView)
|
||||
}
|
||||
sectionTitleView.bounds = CGRect(origin: .zero, size: sectionTitleSize)
|
||||
sectionHeaderTransition.updateFrame(view: sectionTitleView, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: finalPanelHeight - OverlayPlayerControlsNode.sectionHeaderHeight + 6.0 + UIScreenPixel), size: sectionTitleSize))
|
||||
}
|
||||
} else if let sectionTitleView = self.sectionTitle.view, sectionTitleView.superview != nil {
|
||||
sectionTitleView.removeFromSuperview()
|
||||
}
|
||||
|
||||
|
||||
if let savedMusic {
|
||||
var profileAudioTransition = transition
|
||||
var animateIn = false
|
||||
if previousSavedMusic != savedMusic, let profileAudio {
|
||||
self.profileAudio = nil
|
||||
if let profileAudioView = profileAudio.view {
|
||||
if transition.isAnimated {
|
||||
profileAudioView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.25)
|
||||
profileAudioView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
profileAudioView.removeFromSuperview()
|
||||
})
|
||||
animateIn = true
|
||||
} else {
|
||||
profileAudioView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.profileAudio == nil {
|
||||
profileAudioTransition = .immediate
|
||||
}
|
||||
|
|
@ -1089,90 +1002,58 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
self.profileAudio = componentView
|
||||
return componentView
|
||||
}()
|
||||
|
||||
let profileAudioComponent: AnyComponent<Empty>
|
||||
var profileAudioOffset: CGFloat = 0.0
|
||||
|
||||
let profileAudioButtonContent: AnyComponentWithIdentity<Empty>
|
||||
var buttonBackgroundColor = self.presentationData.theme.list.itemCheckColors.fillColor
|
||||
if savedMusic {
|
||||
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== self.presentationData.theme {
|
||||
self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: self.presentationData.theme.list.itemAccentColor)!, self.presentationData.theme)
|
||||
}
|
||||
let textFont = Font.regular(13.0)
|
||||
let textColor = self.presentationData.theme.list.itemSecondaryTextColor
|
||||
let linkColor = self.presentationData.theme.list.itemAccentColor
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
|
||||
let attributedString = parseMarkdownIntoAttributedString(self.presentationData.strings.MediaPlayer_SavedMusic_RemoveFromProfile, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = attributedString.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
||||
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
||||
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
||||
}
|
||||
profileAudioComponent = AnyComponent(MultilineTextComponent(
|
||||
text: .plain(attributedString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 5,
|
||||
lineSpacing: 0.2,
|
||||
highlightColor: linkColor.withAlphaComponent(0.1),
|
||||
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { [weak self] attributes, _ in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
if let file = self?.currentFileReference {
|
||||
self?.requestRemoveFromProfile?(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO:localize
|
||||
profileAudioButtonContent = AnyComponentWithIdentity(id: "removeFromProfile", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Remove from Profile", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)))
|
||||
))
|
||||
profileAudioOffset = 18.0
|
||||
buttonBackgroundColor = self.presentationData.theme.list.controlSecondaryColor
|
||||
} else {
|
||||
profileAudioComponent = AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: self.presentationData.theme.list.itemCheckColors.fillColor,
|
||||
foreground: self.presentationData.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: self.presentationData.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
BundleIconComponent(name: "Peer Info/SaveMusic", tintColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: self.presentationData.strings.MediaPlayer_SavedMusic_AddToProfile, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)))
|
||||
))
|
||||
], spacing: 8.0)
|
||||
)),
|
||||
action: { [weak self] in
|
||||
if let file = self?.currentFileReference {
|
||||
self?.requestSaveToProfile?(file)
|
||||
}
|
||||
}
|
||||
profileAudioButtonContent = AnyComponentWithIdentity(id: "addToProfile", component: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
BundleIconComponent(name: "Peer Info/SaveMusic", tintColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: self.presentationData.strings.MediaPlayer_SavedMusic_AddToProfile, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)))
|
||||
))
|
||||
], spacing: 8.0)
|
||||
))
|
||||
}
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let profileAudioSize = profileAudio.update(
|
||||
transition: .immediate,
|
||||
component: profileAudioComponent,
|
||||
transition: ComponentTransition(profileAudioTransition),
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: buttonBackgroundColor,
|
||||
foreground: self.presentationData.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: buttonBackgroundColor
|
||||
),
|
||||
content: profileAudioButtonContent,
|
||||
action: { [weak self] in
|
||||
guard let self, let file = self.currentFileReference else {
|
||||
return
|
||||
}
|
||||
if savedMusic {
|
||||
self.requestRemoveFromProfile?(file)
|
||||
} else {
|
||||
self.requestSaveToProfile?(file)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: width - leftInset - rightInset - 32.0, height: 50.0)
|
||||
containerSize: CGSize(width: width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
)
|
||||
let profileAudioOrigin: CGFloat = finalPanelHeight + profileAudioOffset - (hasSectionHeader ? OverlayPlayerControlsNode.sectionHeaderHeight : 0.0) - 42.0 - floorToScreenPixels(profileAudioSize.height / 2.0)
|
||||
let profileAudioOrigin: CGFloat = finalPanelHeight - profileAudioSize.height - buttonInsets.bottom
|
||||
let profileAudioFrame = CGRect(origin: CGPoint(x: floor((width - profileAudioSize.width) / 2.0), y: profileAudioOrigin), size: profileAudioSize)
|
||||
if let profileAudioView = profileAudio.view {
|
||||
if profileAudioView.superview == nil {
|
||||
self.view.addSubview(profileAudioView)
|
||||
|
||||
if animateIn {
|
||||
profileAudioView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
profileAudioView.layer.animateScale(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
profileAudioTransition.updateFrame(view: profileAudioView, frame: profileAudioFrame)
|
||||
}
|
||||
|
|
@ -1412,7 +1293,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
@objc func albumArtTap(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if !"".isEmpty, case .ended = recognizer.state {
|
||||
if let supernode = self.supernode {
|
||||
let bounds = supernode.bounds
|
||||
if bounds.width > bounds.height {
|
||||
|
|
@ -2753,7 +2753,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
}
|
||||
|
||||
public func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, audio: Bool, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, presentDocumentScanner: (() -> Void)?, send: @escaping ([AnyMediaReference], Bool, Int32?, NSAttributedString?) -> Void) -> AttachmentFileController {
|
||||
return makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, mode: audio ? .audio(story: false) : .recent, bannedSendMedia: bannedSendMedia, presentGallery: presentGallery, presentFiles: presentFiles, presentDocumentScanner: presentDocumentScanner, send: send)
|
||||
return makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, mode: audio ? .audio(.chat) : .recent, bannedSendMedia: bannedSendMedia, presentGallery: presentGallery, presentFiles: presentFiles, presentDocumentScanner: presentDocumentScanner, send: send)
|
||||
}
|
||||
|
||||
public func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, isScheduledMessages: Bool, isFile: Bool, hasTimer: Bool, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue