diff --git a/submodules/AccountContext/Sources/SharedMediaPlayer.swift b/submodules/AccountContext/Sources/SharedMediaPlayer.swift index 584461a252..d95a4f6e46 100644 --- a/submodules/AccountContext/Sources/SharedMediaPlayer.swift +++ b/submodules/AccountContext/Sources/SharedMediaPlayer.swift @@ -58,14 +58,14 @@ public struct SharedMediaPlaybackAlbumArt: Equatable { } public enum SharedMediaPlaybackDisplayData: Equatable { - case music(title: String?, performer: String?, albumArt: SharedMediaPlaybackAlbumArt?, long: Bool) + case music(title: String?, performer: String?, albumArt: SharedMediaPlaybackAlbumArt?, long: Bool, caption: NSAttributedString?) case voice(author: Peer?, peer: Peer?) case instantVideo(author: Peer?, peer: Peer?, timestamp: Int32) public static func ==(lhs: SharedMediaPlaybackDisplayData, rhs: SharedMediaPlaybackDisplayData) -> Bool { switch lhs { - case let .music(lhsTitle, lhsPerformer, lhsAlbumArt, lhsDuration): - if case let .music(rhsTitle, rhsPerformer, rhsAlbumArt, rhsDuration) = rhs, lhsTitle == rhsTitle, lhsPerformer == rhsPerformer, lhsAlbumArt == rhsAlbumArt, lhsDuration == rhsDuration { + case let .music(lhsTitle, lhsPerformer, lhsAlbumArt, lhsDuration, lhsCaption): + if case let .music(rhsTitle, rhsPerformer, rhsAlbumArt, rhsDuration, rhsCaption) = rhs, lhsTitle == rhsTitle, lhsPerformer == rhsPerformer, lhsAlbumArt == rhsAlbumArt, lhsDuration == rhsDuration, lhsCaption?.string == rhsCaption?.string { return true } else { return false diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index bf9f321c04..7bf735a815 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2077,6 +2077,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U self._entitiesView = externalEntitiesView } else { self._entitiesView = DrawingEntitiesView(context: self.context, size: controller.size) + //self._entitiesView = DrawingEntitiesView(context: self.context, size: controller.originalSize) } self._drawingView?.entitiesView = self._entitiesView self._entitiesView?.drawingView = self._drawingView @@ -2942,7 +2943,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U super.displayNodeDidLoad() let dropInteraction = UIDropInteraction(delegate: self) - self.view.addInteraction(dropInteraction) + self.drawingView.addInteraction(dropInteraction) } public func generateResultData() -> TGPaintingData? { diff --git a/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift b/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift index a984bdb152..ec14e033a1 100644 --- a/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift +++ b/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift @@ -94,7 +94,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem { albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } - return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false) + return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false, caption: nil) } case let .Video(_, _, flags): if flags.contains(.instantRoundVideo) { @@ -107,7 +107,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem { } } - return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: "", albumArt: nil, long: false) + return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: "", albumArt: nil, long: false, caption: nil) } return nil } diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m index cead73940e..ba9aeee3a7 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m @@ -235,17 +235,7 @@ { void(^block)(void) = ^ { - CGAffineTransform transform = CGAffineTransformMakeRotation(-1 * TGRotationForInterfaceOrientation(orientation)); - CGFloat scale = 1.0; - if (self.frame.size.width != 0.0) { - scale = self.frame.size.height / self.frame.size.width; - } - if (_innerInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { - transform = CGAffineTransformScale(transform, scale, scale); - } else if (_innerInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { - transform = CGAffineTransformScale(transform, scale, scale); - } - _wrapperView.transform = transform; + [self updateWrapperTransform]; [self layoutSubviews]; }; @@ -257,6 +247,20 @@ block(); } +- (void)updateWrapperTransform { + CGAffineTransform transform = CGAffineTransformMakeRotation(-1 * TGRotationForInterfaceOrientation(_innerInterfaceOrientation)); + CGFloat scale = 1.0; + if (self.frame.size.width != 0.0) { + scale = self.frame.size.height / self.frame.size.width; + } + if (_innerInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { + transform = CGAffineTransformScale(transform, scale, scale); + } else if (_innerInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { + transform = CGAffineTransformScale(transform, scale, scale); + } + _wrapperView.transform = transform; +} + - (void)layoutSubviews { [super layoutSubviews]; @@ -264,6 +268,8 @@ _wrapperView.bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); _wrapperView.center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); + [self updateWrapperTransform]; + TGCameraPreviewView *previewView = _previewView; if (previewView.superview == _wrapperView) previewView.frame = self.bounds; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 74f4a95599..92d6206d3d 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -595,6 +595,10 @@ hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; } } + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && _intent == TGMediaAssetsControllerSendFileIntent) { + hasOnScreenNavigation = false; + } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" diff --git a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m index 2b1c3c8771..2399255d01 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m @@ -385,8 +385,8 @@ __weak TGMediaAvatarMenuMixin *weakSelf = self; UIViewController *(^presentBlock)(TGMediaAssetsController *) = nil; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) - { +// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) +// { presentBlock = ^UIViewController * (TGMediaAssetsController *controller) { __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; @@ -413,48 +413,51 @@ return controller; }; - } - else - { - presentBlock = ^UIViewController * (TGMediaAssetsController *controller) - { - __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; - if (strongSelf == nil) - return nil; - - controller.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet; - controller.modalPresentationStyle = UIModalPresentationFormSheet; - - TGOverlayFormsheetWindow *formSheetWindow = [[TGOverlayFormsheetWindow alloc] initWithContext:strongSelf->_context parentController:strongSelf->_parentController contentController:controller]; - [formSheetWindow showAnimated:true]; - - __weak TGNavigationController *weakNavController = controller; - __weak TGOverlayFormsheetWindow *weakFormSheetWindow = formSheetWindow; - controller.dismissalBlock = ^ - { - __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - __strong TGOverlayFormsheetWindow *strongFormSheetWindow = weakFormSheetWindow; - if (strongFormSheetWindow == nil) - return; - - __strong TGNavigationController *strongNavController = weakNavController; - if (strongNavController != nil) - { - if (strongNavController.presentingViewController != nil) - [strongNavController.presentingViewController dismissViewControllerAnimated:true completion:nil]; - else - [strongFormSheetWindow dismissAnimated:true]; - } - - if (strongSelf.didDismiss != nil) - strongSelf.didDismiss(); - }; - return nil; - }; - } +// } +// else +// { +// presentBlock = ^UIViewController * (TGMediaAssetsController *controller) +// { +// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; +// if (strongSelf == nil) +// return nil; +// +// controller.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet; +// controller.modalPresentationStyle = UIModalPresentationFormSheet; +// +// id windowManager = nil; +// windowManager = [strongSelf->_context makeOverlayWindowManager]; +// +// TGOverlayFormsheetWindow *formSheetWindow = [[TGOverlayFormsheetWindow alloc] initWithManager:windowManager parentController:strongSelf->_parentController contentController:controller]; +// [formSheetWindow showAnimated:true]; +// +// __weak TGNavigationController *weakNavController = controller; +// __weak TGOverlayFormsheetWindow *weakFormSheetWindow = formSheetWindow; +// controller.dismissalBlock = ^ +// { +// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; +// if (strongSelf == nil) +// return; +// +// __strong TGOverlayFormsheetWindow *strongFormSheetWindow = weakFormSheetWindow; +// if (strongFormSheetWindow == nil) +// return; +// +// __strong TGNavigationController *strongNavController = weakNavController; +// if (strongNavController != nil) +// { +// if (strongNavController.presentingViewController != nil) +// [strongNavController.presentingViewController dismissViewControllerAnimated:true completion:nil]; +// else +// [strongFormSheetWindow dismissAnimated:true]; +// } +// +// if (strongSelf.didDismiss != nil) +// strongSelf.didDismiss(); +// }; +// return nil; +// }; +// } void (^showMediaPicker)(TGMediaAssetGroup *) = ^(TGMediaAssetGroup *group) { diff --git a/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.h b/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.h index 87ee7477b1..b1d6f46a7a 100644 --- a/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.h +++ b/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.h @@ -6,7 +6,7 @@ @interface TGOverlayFormsheetWindow : UIWindow -- (instancetype)initWithContext:(id)context parentController:(TGViewController *)parentController contentController:(UIViewController *)contentController; +- (instancetype)initWithManager:(id)manager parentController:(TGViewController *)parentController contentController:(UIViewController *)contentController; - (void)showAnimated:(bool)animated; - (void)dismissAnimated:(bool)animated; diff --git a/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.m b/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.m index 632b704233..7650714b0c 100644 --- a/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.m +++ b/submodules/LegacyComponents/Sources/TGOverlayFormsheetWindow.m @@ -14,18 +14,19 @@ SMetaDisposable *_sizeClassDisposable; UIUserInterfaceSizeClass _sizeClass; - id _context; + id _manager; + bool _managedIsHidden; } @end @implementation TGOverlayFormsheetWindow -- (instancetype)initWithContext:(id)context parentController:(TGViewController *)parentController contentController:(UIViewController *)contentController +- (instancetype)initWithManager:(id)manager parentController:(TGViewController *)parentController contentController:(UIViewController *)contentController { - self = [super initWithFrame:[context fullscreenBounds]]; + self = [super initWithFrame:[[manager context] fullscreenBounds]]; if (self != nil) { - _context = context; + _manager = manager; self.windowLevel = parentController.view.window.windowLevel + 0.0001f; self.backgroundColor = [UIColor clearColor]; @@ -34,8 +35,7 @@ _contentController = contentController; - if (iosMajorVersion() < 9) - [self createControllerIfNeeded]; + [_manager bindController:_contentController]; } return self; } @@ -45,88 +45,38 @@ [_sizeClassDisposable dispose]; } -- (void)createControllerIfNeeded -{ - if (self.rootViewController != nil) - return; - - TGOverlayFormsheetController *controller = [[TGOverlayFormsheetController alloc] initWithContext:_context contentController:_contentController]; - controller.formSheetWindow = self; - self.rootViewController = controller; - - _contentController = nil; +- (BOOL)isHidden { + return _managedIsHidden; } -- (void)updateSizeClass:(UIUserInterfaceSizeClass)sizeClass animated:(bool)animated -{ - if (_sizeClass == sizeClass) - return; - - _sizeClass = sizeClass; - - if (sizeClass == UIUserInterfaceSizeClassCompact) - { - if ([self contentController].parentViewController != _parentController) - { - [[self contentController] removeFromParentViewController]; - [[self.contentController view] removeFromSuperview]; - - [_parentController presentViewController:[self contentController] animated:animated completion:nil]; - self.hidden = true; +- (void)setHidden:(BOOL)hidden { + if ([_manager managesWindow]) { + if (![super isHidden]) { + [super setHidden:true]; } - } - else - { - if ([self controller] == nil || [self contentController].parentViewController != [self controller]) - { - [self createControllerIfNeeded]; - - [self.contentController.presentingViewController dismissViewControllerAnimated:false completion:^ - { - [[self controller] setContentController:[self contentController]]; - self.hidden = false; - }]; + + if (_managedIsHidden != hidden) { + _managedIsHidden = hidden; + [_manager setHidden:hidden window:self]; + } + } else { + [super setHidden:hidden]; + + if (!hidden) { + [[[LegacyComponentsGlobals provider] applicationWindows].firstObject endEditing:true]; } } } - (void)showAnimated:(bool)animated { - if (iosMajorVersion() >= 9) + if ([self contentController].parentViewController != _parentController) { - UIUserInterfaceSizeClass sizeClass = [_context currentHorizontalSizeClass]; - - if (sizeClass == UIUserInterfaceSizeClassCompact) - { - [self updateSizeClass:sizeClass animated:true]; - } - else - { - [self createControllerIfNeeded]; - - self.hidden = false; - - if (animated) - [[self controller] animateInWithCompletion:nil]; - } + [[self contentController] removeFromParentViewController]; + [[self.contentController view] removeFromSuperview]; - __weak TGOverlayFormsheetWindow *weakSelf = self; - _sizeClassDisposable = [[SMetaDisposable alloc] init]; - [_sizeClassDisposable setDisposable:[[_context sizeClassSignal] startWithNext:^(NSNumber *next) - { - __strong TGOverlayFormsheetWindow *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf updateSizeClass:next.integerValue animated:false]; - }]]; - } - else - { - self.hidden = false; - - if (animated) - [[self controller] animateInWithCompletion:nil]; + [_parentController presentViewController:[self contentController] animated:animated completion:nil]; + //self.hidden = true; } } @@ -134,7 +84,8 @@ { TGViewController *parentController = _parentController; [parentController.associatedWindowStack removeObject:self]; - self.hidden = true; + [_manager setHidden:true window:self]; + //self.hidden = true; } - (void)dismissAnimated:(bool)animated diff --git a/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m b/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m index 6d168caa5b..ee938b1c84 100644 --- a/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m +++ b/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m @@ -190,7 +190,10 @@ controller.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet; controller.modalPresentationStyle = UIModalPresentationFormSheet; - TGOverlayFormsheetWindow *formSheetWindow = [[TGOverlayFormsheetWindow alloc] initWithContext:context parentController:strongParentController contentController:controller]; + id windowManager = nil; + windowManager = [context makeOverlayWindowManager]; + + TGOverlayFormsheetWindow *formSheetWindow = [[TGOverlayFormsheetWindow alloc] initWithManager:windowManager parentController:strongParentController contentController:controller]; [formSheetWindow showAnimated:true]; __weak TGNavigationController *weakNavController = controller; diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index 38ec952af0..fcdf028f53 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -41,7 +41,7 @@ private class MediaHeaderItemNode: ASDisplayNode { var subtitleString: NSAttributedString? if let playbackItem = playbackItem, let displayData = playbackItem.displayData { switch displayData { - case let .music(title, performer, _, long): + case let .music(title, performer, _, long, _): rateButtonHidden = !long let titleText: String = title ?? strings.MediaPlayer_UnknownTrack let subtitleText: String = performer ?? strings.MediaPlayer_UnknownArtist diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9b460cf5ef..92db2f7930 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -6803,7 +6803,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(index.id) { let highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId) controllerInteraction.highlightedState = highlightedState - strongSelf.updateItemNodesHighlightedStates(animated: false) + strongSelf.updateItemNodesHighlightedStates(animated: initial) strongSelf.scrolledToMessageIdValue = ScrolledToMessageId(id: index.id, allowedReplacementDirection: []) strongSelf.messageContextDisposable.set((Signal.complete() |> delay(0.7, queue: Queue.mainQueue())).start(completed: { diff --git a/submodules/TelegramUI/Sources/MediaManager.swift b/submodules/TelegramUI/Sources/MediaManager.swift index d44ce03b6e..725e33f801 100644 --- a/submodules/TelegramUI/Sources/MediaManager.swift +++ b/submodules/TelegramUI/Sources/MediaManager.swift @@ -248,7 +248,7 @@ public final class MediaManagerImpl: NSObject, MediaManager { var artwork: SharedMediaPlaybackAlbumArt? switch displayData { - case let .music(title, performer, artworkValue, _): + case let .music(title, performer, artworkValue, _, _): artwork = artworkValue let titleText: String = title ?? presentationData.strings.MediaPlayer_UnknownTrack diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 7376f7efbe..cc3314ab97 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index 9e21e6e6b4..ca88b0dd34 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -402,7 +402,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { let entities = generateTextEntities(additionalText, enabledTypes: [.mention]) let attributedAdditionalText = stringWithAppliedEntities(additionalText, entities: entities, baseColor: presentationData.theme.list.itemPrimaryTextColor, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont, underlineLinks: false, message: nil) - self.additionalTextNode.maximumNumberOfLines = 3 + self.additionalTextNode.maximumNumberOfLines = 10 self.additionalTextNode.attributedText = attributedAdditionalText } else { self.additionalTextNode.attributedText = nil diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index bca43ee055..e159d41075 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -1,11 +1,13 @@ import Foundation import UIKit +import Display import SwiftSignalKit import Postbox import TelegramCore import TelegramUIPreferences import AccountContext import MusicAlbumArtResources +import TextFormat private enum PeerMessagesMediaPlaylistLoadAnchor { case messageId(MessageId) @@ -95,6 +97,15 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { var displayData: SharedMediaPlaybackDisplayData? { if let file = extractFileMedia(self.message) { + let text = self.message.text + var entities: [MessageTextEntity] = [] + if let result = addLocallyGeneratedEntities(text, enabledTypes: [.timecode], entities: [], mediaDuration: file.duration.flatMap(Double.init)) { + entities = result + } + + let textFont = Font.regular(14.0) + let caption = stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: .white, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: self.message) + for attribute in file.attributes { switch attribute { case let .Audio(isVoice, duration, title, performer, _): @@ -114,7 +125,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } - return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0) + return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption) } case let .Video(_, _, flags): if flags.contains(.instantRoundVideo) { @@ -127,7 +138,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { } } - return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: self.message.effectiveAuthor?.debugDisplayTitle ?? "", albumArt: nil, long: false) + return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: self.message.effectiveAuthor?.debugDisplayTitle ?? "", albumArt: nil, long: false, caption: caption) } return nil }