Various improvements

This commit is contained in:
Ilya Laktyushin
2023-01-04 11:18:20 +04:00
parent 076129348b
commit c10f020f0a
15 changed files with 203 additions and 154 deletions

View File

@@ -61,19 +61,21 @@ private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat {
private let titleFont = Font.semibold(18.0)
private let descriptionFont = Font.regular(18.0)
private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?, Bool) {
private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?, Bool, NSAttributedString?) {
var titleString: NSAttributedString?
var descriptionString: NSAttributedString?
var hasArtist = false
var captionString: NSAttributedString?
if let data = data {
let titleText: String
let subtitleText: String
switch data {
case let .music(title, performer, _, _):
case let .music(title, performer, _, _, caption):
titleText = title ?? presentationData.strings.MediaPlayer_UnknownTrack
subtitleText = performer ?? presentationData.strings.MediaPlayer_UnknownArtist
hasArtist = performer != nil
captionString = caption
case .voice, .instantVideo:
titleText = ""
subtitleText = ""
@@ -83,7 +85,7 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, pres
descriptionString = NSAttributedString(string: subtitleText, font: descriptionFont, textColor: hasArtist ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemSecondaryTextColor)
}
return (titleString, descriptionString, hasArtist)
return (titleString, descriptionString, hasArtist, captionString)
}
final class OverlayPlayerControlsNode: ASDisplayNode {
@@ -146,6 +148,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
private var currentAlbumArt: SharedMediaPlaybackAlbumArt?
private var currentFileReference: FileMediaReference?
private var statusDisposable: Disposable?
private var chapterDisposable: Disposable?
private var previousCaption: NSAttributedString?
private var chaptersPromise = ValuePromise<[MediaPlayerScrubbingChapter]>([])
private var currentChapter: MediaPlayerScrubbingChapter?
private let hapticFeedback = HapticFeedback()
private var scrubbingDisposable: Disposable?
private var leftDurationLabelPushed = false
@@ -376,7 +385,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
strongSelf.updateRateButton(baseRate)
}
if let displayData = displayData, case let .music(_, _, _, long) = displayData, long {
if let displayData = displayData, case let .music(_, _, _, long, _) = displayData, long {
strongSelf.scrubberNode.enableFineScrubbing = true
rateButtonIsHidden = false
} else {
@@ -430,6 +439,58 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
}
})
self.chapterDisposable = combineLatest(queue: Queue.mainQueue(), mappedStatus, self.chaptersPromise.get())
.start(next: { [weak self] status, chapters in
if let strongSelf = self, status.duration > 1.0, chapters.count > 0 {
let previousChapter = strongSelf.currentChapter
var currentChapter: MediaPlayerScrubbingChapter?
for chapter in chapters {
if chapter.start > status.timestamp {
break
} else {
currentChapter = chapter
}
}
if let chapter = currentChapter, chapter != previousChapter {
strongSelf.currentChapter = chapter
if strongSelf.scrubberNode.isScrubbing {
strongSelf.hapticFeedback.impact(.light)
}
if let previousChapter = previousChapter, !strongSelf.infoNode.alpha.isZero {
if let snapshotView = strongSelf.infoNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = strongSelf.infoNode.frame
strongSelf.infoNode.view.superview?.addSubview(snapshotView)
let offset: CGFloat = 30.0
let snapshotTargetPosition: CGPoint
let nodeStartPosition: CGPoint
if previousChapter.start < chapter.start {
snapshotTargetPosition = CGPoint(x: -offset, y: 0.0)
nodeStartPosition = CGPoint(x: offset, y: 0.0)
} else {
snapshotTargetPosition = CGPoint(x: offset, y: 0.0)
nodeStartPosition = CGPoint(x: -offset, y: 0.0)
}
snapshotView.layer.animatePosition(from: CGPoint(), to: snapshotTargetPosition, duration: 0.2, removeOnCompletion: false, additive: true)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
strongSelf.infoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
strongSelf.infoNode.layer.animatePosition(from: nodeStartPosition, to: CGPoint(), duration: 0.2, additive: true)
}
}
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, transition: .immediate)
}
}
}
})
self.scrubberNode.seek = { [weak self] value in
self?.control?(.seek(value))
}
@@ -463,6 +524,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
deinit {
self.statusDisposable?.dispose()
self.chapterDisposable?.dispose()
self.scrubbingDisposable?.dispose()
}
@@ -600,7 +662,15 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
let infoVerticalOrigin: CGFloat = panelHeight - OverlayPlayerControlsNode.basePanelHeight + 36.0
let (titleString, descriptionString, hasArtist) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
let (titleString, descriptionString, hasArtist, caption) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
if self.previousCaption?.string != caption?.string {
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.artistButton.isUserInteractionEnabled = hasArtist
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - sideInset * 2.0 - leftInset - rightInset - infoLabelsLeftInset - infoLabelsRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
@@ -619,7 +689,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
var albumArt: SharedMediaPlaybackAlbumArt?
if let displayData = self.displayData {
switch displayData {
case let .music(_, _, value, _):
case let .music(_, _, value, _, _):
albumArt = value
default:
break
@@ -913,7 +983,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
}
@objc func artistPressed() {
let (_, descriptionString, _) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
let (_, descriptionString, _, _) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
if let artist = descriptionString?.string {
self.requestSearchByArtist?(artist)
}