mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
ce9f8fcb7c
commit
f17ead143a
@ -12845,6 +12845,8 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Chat.ToastStarsSent.Title_1" = "Star sent!";
|
"Chat.ToastStarsSent.Title_1" = "Star sent!";
|
||||||
"Chat.ToastStarsSent.Title_any" = "Stars sent!";
|
"Chat.ToastStarsSent.Title_any" = "Stars sent!";
|
||||||
|
"Chat.ToastStarsSent.TitleChannel_1" = "Star sent as {name}!";
|
||||||
|
"Chat.ToastStarsSent.TitleChannel_any" = "Stars sent as {name}!";
|
||||||
|
|
||||||
"Chat.ToastStarsSent.AnonymousTitle_1" = "Star sent anonymously!";
|
"Chat.ToastStarsSent.AnonymousTitle_1" = "Star sent anonymously!";
|
||||||
"Chat.ToastStarsSent.AnonymousTitle_any" = "Stars sent anonymously!";
|
"Chat.ToastStarsSent.AnonymousTitle_any" = "Stars sent anonymously!";
|
||||||
@ -13841,3 +13843,5 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Stars.Transfer.Terms" = "By purchasing you agree to the [Terms of Service]().";
|
"Stars.Transfer.Terms" = "By purchasing you agree to the [Terms of Service]().";
|
||||||
"Stars.Transfer.Terms_URL" = "https://telegram.org/tos/stars";
|
"Stars.Transfer.Terms_URL" = "https://telegram.org/tos/stars";
|
||||||
|
|
||||||
|
"StarsTransaction.Commission" = "Commission";
|
||||||
|
@ -238,8 +238,21 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
case .Local:
|
case .Local:
|
||||||
break
|
break
|
||||||
case .Remote, .Paused:
|
case .Remote, .Paused:
|
||||||
if let image = cloudFetchIcon {
|
var isHLS = false
|
||||||
statusState = .customIcon(image)
|
if let message = self.currentMessage {
|
||||||
|
for media in message.media {
|
||||||
|
if let file = media as? TelegramMediaFile {
|
||||||
|
isHLS = NativeVideoContent.isHLSVideo(file: file)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isHLS {
|
||||||
|
statusState = .none
|
||||||
|
} else {
|
||||||
|
if let image = cloudFetchIcon {
|
||||||
|
statusState = .customIcon(image)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.statusNode.transitionToState(statusState, completion: {})
|
self.statusNode.transitionToState(statusState, completion: {})
|
||||||
@ -954,6 +967,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
|
|
||||||
var messageText = NSMutableAttributedString(string: "")
|
var messageText = NSMutableAttributedString(string: "")
|
||||||
var hasCaption = false
|
var hasCaption = false
|
||||||
|
var mediaDuration: Double?
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if media is TelegramMediaPaidContent {
|
if media is TelegramMediaPaidContent {
|
||||||
hasCaption = true
|
hasCaption = true
|
||||||
@ -961,6 +975,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
hasCaption = true
|
hasCaption = true
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
hasCaption = file.mimeType.hasPrefix("image/") || file.mimeType.hasPrefix("video/")
|
hasCaption = file.mimeType.hasPrefix("image/") || file.mimeType.hasPrefix("video/")
|
||||||
|
mediaDuration = file.duration
|
||||||
} else if media is TelegramMediaInvoice {
|
} else if media is TelegramMediaInvoice {
|
||||||
hasCaption = true
|
hasCaption = true
|
||||||
}
|
}
|
||||||
@ -974,6 +989,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var text = message.text
|
var text = message.text
|
||||||
|
if let result = addLocallyGeneratedEntities(text, enabledTypes: [.timecode], entities: entities, mediaDuration: mediaDuration) {
|
||||||
|
entities = result
|
||||||
|
}
|
||||||
if let translateToLanguage, !text.isEmpty {
|
if let translateToLanguage, !text.isEmpty {
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
|
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
|
||||||
|
@ -388,15 +388,14 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
let targetCloneView = scrubberTransition.makeView()
|
let targetCloneView = scrubberTransition.makeView()
|
||||||
self.addSubview(targetCloneView)
|
self.addSubview(targetCloneView)
|
||||||
targetCloneView.frame = fromRect
|
targetCloneView.frame = fromRect
|
||||||
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 0.0), .immediate)
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 0.0, direction: .in), .immediate)
|
||||||
targetCloneView.alpha = 1.0
|
targetCloneView.alpha = 1.0
|
||||||
|
|
||||||
transition.updateFrame(view: targetCloneView, frame: CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - fromRect.height - 3.0), size: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height)))
|
transition.updateFrame(view: targetCloneView, frame: CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - fromRect.height - 3.0), size: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height)))
|
||||||
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 1.0), transition)
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 1.0, direction: .in), transition)
|
||||||
let scrubberTransitionView = scrubberTransition.view
|
let scrubberTransitionView = scrubberTransition.view
|
||||||
scrubberTransitionView.isHidden = true
|
scrubberTransitionView.isHidden = true
|
||||||
ContainedViewLayoutTransition.animated(duration: 0.08, curve: .easeInOut).updateAlpha(layer: targetCloneView.layer, alpha: 0.0, completion: { [weak scrubberTransitionView, weak targetCloneView] _ in
|
ContainedViewLayoutTransition.animated(duration: 0.08, curve: .easeInOut).updateAlpha(layer: targetCloneView.layer, alpha: 0.0, completion: { [weak targetCloneView] _ in
|
||||||
scrubberTransitionView?.isHidden = false
|
|
||||||
targetCloneView?.removeFromSuperview()
|
targetCloneView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -428,11 +427,11 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
let targetCloneView = scrubberTransition.makeView()
|
let targetCloneView = scrubberTransition.makeView()
|
||||||
self.addSubview(targetCloneView)
|
self.addSubview(targetCloneView)
|
||||||
targetCloneView.frame = CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - toRect.height), size: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height))
|
targetCloneView.frame = CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - toRect.height), size: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height))
|
||||||
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 0.0), .immediate)
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 0.0, direction: .out), .immediate)
|
||||||
targetCloneView.alpha = 0.0
|
targetCloneView.alpha = 0.0
|
||||||
|
|
||||||
transition.updateFrame(view: targetCloneView, frame: toRect)
|
transition.updateFrame(view: targetCloneView, frame: toRect)
|
||||||
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 1.0), transition)
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.Scrubber.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 1.0, direction: .out), transition)
|
||||||
let scrubberTransitionView = scrubberTransition.view
|
let scrubberTransitionView = scrubberTransition.view
|
||||||
scrubberTransitionView.isHidden = true
|
scrubberTransitionView.isHidden = true
|
||||||
transition.updateAlpha(layer: targetCloneView.layer, alpha: 1.0, completion: { [weak scrubberTransitionView] _ in
|
transition.updateAlpha(layer: targetCloneView.layer, alpha: 1.0, completion: { [weak scrubberTransitionView] _ in
|
||||||
|
@ -6,18 +6,26 @@ import Display
|
|||||||
public final class GalleryItemScrubberTransition {
|
public final class GalleryItemScrubberTransition {
|
||||||
public final class Scrubber {
|
public final class Scrubber {
|
||||||
public struct TransitionState: Equatable {
|
public struct TransitionState: Equatable {
|
||||||
|
public enum Direction {
|
||||||
|
case `in`
|
||||||
|
case out
|
||||||
|
}
|
||||||
|
|
||||||
public var sourceSize: CGSize
|
public var sourceSize: CGSize
|
||||||
public var destinationSize: CGSize
|
public var destinationSize: CGSize
|
||||||
public var progress: CGFloat
|
public var progress: CGFloat
|
||||||
|
public var direction: Direction
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
sourceSize: CGSize,
|
sourceSize: CGSize,
|
||||||
destinationSize: CGSize,
|
destinationSize: CGSize,
|
||||||
progress: CGFloat
|
progress: CGFloat,
|
||||||
|
direction: Direction
|
||||||
) {
|
) {
|
||||||
self.sourceSize = sourceSize
|
self.sourceSize = sourceSize
|
||||||
self.destinationSize = destinationSize
|
self.destinationSize = destinationSize
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
|
self.direction = direction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,6 +1006,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
|||||||
guard let overlayController = self.overlayController else {
|
guard let overlayController = self.overlayController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
overlayController.removePictureInPictureContent(content: self)
|
overlayController.removePictureInPictureContent(content: self)
|
||||||
self.node.canAttachContent = false
|
self.node.canAttachContent = false
|
||||||
if self.didExpand {
|
if self.didExpand {
|
||||||
@ -1413,7 +1414,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
if playerStatusValue.duration >= 60.0 * 10.0 {
|
if playerStatusValue.duration >= 60.0 * 10.0 {
|
||||||
var publicLinkPrefix: ShareControllerSubject.PublicLinkPrefix?
|
var publicLinkPrefix: ShareControllerSubject.PublicLinkPrefix?
|
||||||
if case let .message(message, _) = self.item?.contentInfo, message.id.namespace == Namespaces.Message.Cloud, let peer = message.peers[message.id.peerId] as? TelegramChannel, let username = peer.username {
|
if case let .message(message, _) = self.item?.contentInfo, message.id.namespace == Namespaces.Message.Cloud, let peer = message.peers[message.id.peerId] as? TelegramChannel, let username = peer.username ?? peer.usernames.first?.username {
|
||||||
let visibleString = "t.me/\(username)/\(message.id.id)"
|
let visibleString = "t.me/\(username)/\(message.id.id)"
|
||||||
publicLinkPrefix = ShareControllerSubject.PublicLinkPrefix(
|
publicLinkPrefix = ShareControllerSubject.PublicLinkPrefix(
|
||||||
visibleString: visibleString,
|
visibleString: visibleString,
|
||||||
@ -1878,23 +1879,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
self.mediaPlaybackStateDisposable.set((throttledSignal
|
self.mediaPlaybackStateDisposable.set((throttledSignal
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
guard let strongSelf = self, let videoNode = strongSelf.videoNode, videoNode.ownsContentNode else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let status = status {
|
if let status {
|
||||||
let shouldStorePlaybacksState: Bool
|
self.maybeStorePlaybackStatus(status: status)
|
||||||
shouldStorePlaybacksState = status.duration >= 20.0
|
|
||||||
|
|
||||||
if shouldStorePlaybacksState {
|
|
||||||
var timestamp: Double?
|
|
||||||
if status.timestamp > 5.0 && status.timestamp < status.duration - 5.0 {
|
|
||||||
timestamp = status.timestamp
|
|
||||||
}
|
|
||||||
item.storeMediaPlaybackState(message.id, timestamp, status.baseRate)
|
|
||||||
} else {
|
|
||||||
item.storeMediaPlaybackState(message.id, nil, status.baseRate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -2007,6 +1997,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
case let .buffering(_, whilePlaying, _, display):
|
case let .buffering(_, whilePlaying, _, display):
|
||||||
displayProgress = display
|
displayProgress = display
|
||||||
initialBuffering = !whilePlaying
|
initialBuffering = !whilePlaying
|
||||||
|
if item.content is HLSVideoContent && display {
|
||||||
|
initialBuffering = true
|
||||||
|
}
|
||||||
isPaused = !whilePlaying
|
isPaused = !whilePlaying
|
||||||
var isStreaming = false
|
var isStreaming = false
|
||||||
if let fetchStatus = strongSelf.fetchStatus {
|
if let fetchStatus = strongSelf.fetchStatus {
|
||||||
@ -2101,7 +2094,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if hasStarted || strongSelf.didPause {
|
if hasStarted || strongSelf.didPause {
|
||||||
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
||||||
} else if let fetchStatus = fetchStatus, !strongSelf.requiresDownload {
|
} else if let fetchStatus = fetchStatus, !strongSelf.requiresDownload {
|
||||||
strongSelf.footerContentNode.content = .fetch(status: fetchStatus, seekable: seekable)
|
if item.content is HLSVideoContent {
|
||||||
|
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
||||||
|
} else {
|
||||||
|
strongSelf.footerContentNode.content = .fetch(status: fetchStatus, seekable: seekable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf.footerContentNode.content = .playback(paused: false, seekable: seekable)
|
strongSelf.footerContentNode.content = .playback(paused: false, seekable: seekable)
|
||||||
@ -2369,6 +2366,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
videoNode.seek(0.0)
|
videoNode.seek(0.0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if let status = self.playerStatusValue {
|
||||||
|
self.maybeStorePlaybackStatus(status: status)
|
||||||
|
}
|
||||||
videoNode.continuePlayingWithoutSound()
|
videoNode.continuePlayingWithoutSound()
|
||||||
}
|
}
|
||||||
self.updateDisplayPlaceholder()
|
self.updateDisplayPlaceholder()
|
||||||
@ -2447,6 +2447,30 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func maybeStorePlaybackStatus(status: MediaPlayerStatus) {
|
||||||
|
guard let item = self.item else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let contentInfo = item.contentInfo, case let .message(message, _) = contentInfo else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let shouldStorePlaybacksState: Bool
|
||||||
|
shouldStorePlaybacksState = status.duration >= 20.0
|
||||||
|
|
||||||
|
if shouldStorePlaybacksState {
|
||||||
|
var timestamp: Double?
|
||||||
|
if status.timestamp > 5.0 && status.timestamp < status.duration - 5.0 {
|
||||||
|
timestamp = status.timestamp
|
||||||
|
} else {
|
||||||
|
timestamp = 0.0
|
||||||
|
}
|
||||||
|
item.storeMediaPlaybackState(message.id, timestamp, status.baseRate)
|
||||||
|
} else {
|
||||||
|
item.storeMediaPlaybackState(message.id, nil, status.baseRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd {
|
private var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd {
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
if !item.isSecret, let content = item.content as? NativeVideoContent, content.duration <= 30 {
|
if !item.isSecret, let content = item.content as? NativeVideoContent, content.duration <= 30 {
|
||||||
@ -2634,6 +2658,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func animateOut(to node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) {
|
override func animateOut(to node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) {
|
||||||
|
if let status = self.playerStatusValue {
|
||||||
|
self.maybeStorePlaybackStatus(status: status)
|
||||||
|
}
|
||||||
|
|
||||||
self.isAnimatingOut = true
|
self.isAnimatingOut = true
|
||||||
|
|
||||||
guard let videoNode = self.videoNode else {
|
guard let videoNode = self.videoNode else {
|
||||||
@ -2785,6 +2813,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
|
toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
|
||||||
|
|
||||||
if videoNode.hasAttachedContext {
|
if videoNode.hasAttachedContext {
|
||||||
|
if let status = self.playerStatusValue {
|
||||||
|
self.maybeStorePlaybackStatus(status: status)
|
||||||
|
}
|
||||||
|
|
||||||
if self.isPaused || !self.keepSoundOnDismiss {
|
if self.isPaused || !self.keepSoundOnDismiss {
|
||||||
if let item = self.item, item.content is HLSVideoContent {
|
if let item = self.item, item.content is HLSVideoContent {
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,13 +205,14 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
|
|||||||
private var isSoundEnabled: Bool
|
private var isSoundEnabled: Bool
|
||||||
private var isMuted: Bool
|
private var isMuted: Bool
|
||||||
private var isAmbientMode: Bool
|
private var isAmbientMode: Bool
|
||||||
|
|
||||||
private var seekId: Int = 0
|
private var seekId: Int = 0
|
||||||
private var seekTimestamp: Double = 0.0
|
private var seekTimestamp: Double = 0.0
|
||||||
private var pendingSeekTimestamp: Double?
|
private var pendingSeekTimestamp: Double?
|
||||||
private var pendingContinuePlaybackAfterSeekToTimestamp: Double?
|
private var pendingContinuePlaybackAfterSeekToTimestamp: Double?
|
||||||
private var shouldNotifySeeked: Bool = false
|
private var shouldNotifySeeked: Bool = false
|
||||||
private var stoppedAtEnd: Bool = false
|
private var stoppedAtEnd: Bool = false
|
||||||
|
private var bufferingStartTime: Double?
|
||||||
|
|
||||||
private var renderSynchronizerRate: Double = 0.0
|
private var renderSynchronizerRate: Double = 0.0
|
||||||
private var videoIsRequestingMediaData: Bool = false
|
private var videoIsRequestingMediaData: Bool = false
|
||||||
@ -704,9 +705,21 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
|
|||||||
self.triggerRequestMediaData()
|
self.triggerRequestMediaData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isBuffering {
|
||||||
|
if self.bufferingStartTime == nil {
|
||||||
|
self.bufferingStartTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.bufferingStartTime = nil
|
||||||
|
}
|
||||||
|
|
||||||
let playbackStatus: MediaPlayerPlaybackStatus
|
let playbackStatus: MediaPlayerPlaybackStatus
|
||||||
if isBuffering {
|
if isBuffering {
|
||||||
playbackStatus = .buffering(initial: false, whilePlaying: self.isPlaying, progress: 0.0, display: true)
|
var displayBuffering = false
|
||||||
|
if let bufferingStartTime = self.bufferingStartTime, (CFAbsoluteTimeGetCurrent() - bufferingStartTime) >= 0.3 {
|
||||||
|
displayBuffering = true
|
||||||
|
}
|
||||||
|
playbackStatus = .buffering(initial: false, whilePlaying: self.isPlaying, progress: 0.0, display: displayBuffering)
|
||||||
} else if self.isPlaying {
|
} else if self.isPlaying {
|
||||||
playbackStatus = .playing
|
playbackStatus = .playing
|
||||||
} else {
|
} else {
|
||||||
@ -918,6 +931,7 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.pendingSeekTimestamp = nil
|
self.pendingSeekTimestamp = nil
|
||||||
|
self.stoppedAtEnd = false
|
||||||
self.updateInternalState()
|
self.updateInternalState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,11 @@ public final class FFMpegMediaDataReaderV2: MediaDataReader {
|
|||||||
|
|
||||||
if (codecName == "h264" || codecName == "hevc") {
|
if (codecName == "h264" || codecName == "hevc") {
|
||||||
passthroughDecoder = false
|
passthroughDecoder = false
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
useHardwareAcceleration = false
|
||||||
|
#else
|
||||||
useHardwareAcceleration = true
|
useHardwareAcceleration = true
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (codecName == "av1" || codecName == "av01") {
|
if (codecName == "av1" || codecName == "av01") {
|
||||||
passthroughDecoder = false
|
passthroughDecoder = false
|
||||||
|
@ -489,7 +489,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
|||||||
chapterNodesContainer.isUserInteractionEnabled = false
|
chapterNodesContainer.isUserInteractionEnabled = false
|
||||||
chapterNodesContainerImpl = chapterNodesContainer
|
chapterNodesContainerImpl = chapterNodesContainer
|
||||||
|
|
||||||
var chapters = chapters
|
var chapters = chapters.sorted(by: { $0.start < $1.start })
|
||||||
if let firstChapter = chapters.first, firstChapter.start > 0.0 {
|
if let firstChapter = chapters.first, firstChapter.start > 0.0 {
|
||||||
chapters.insert(MediaPlayerScrubbingChapter(title: "", start: 0.0), at: 0)
|
chapters.insert(MediaPlayerScrubbingChapter(title: "", start: 0.0), at: 0)
|
||||||
}
|
}
|
||||||
@ -915,7 +915,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
let endPosition: CGFloat = max(startPosition, floor(backgroundFrame.width * CGFloat(chapter.start / duration)) - lineWidth / 2.0)
|
let endPosition: CGFloat = max(startPosition, floor(backgroundFrame.width * CGFloat(chapter.start / duration)) - lineWidth / 2.0)
|
||||||
let width = endPosition - startPosition
|
let width = endPosition - startPosition
|
||||||
if width < lineWidth * 0.5 {
|
if width < lineWidth * 0.5 && i != node.chapterNodes.count - 1 {
|
||||||
previousChapterNode.frame = CGRect()
|
previousChapterNode.frame = CGRect()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -2055,9 +2055,16 @@ public final class ShareController: ViewController {
|
|||||||
shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages))
|
shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages))
|
||||||
}
|
}
|
||||||
case let .media(mediaReference, mediaParameters):
|
case let .media(mediaReference, mediaParameters):
|
||||||
|
var forwardSourceMessageId: EngineMessage.Id?
|
||||||
|
if case let .message(message, _) = mediaReference, let sourceMessageId = message.id, (sourceMessageId.peerId.namespace == Namespaces.Peer.CloudUser || sourceMessageId.peerId.namespace == Namespaces.Peer.CloudGroup || sourceMessageId.peerId.namespace == Namespaces.Peer.CloudChannel) {
|
||||||
|
forwardSourceMessageId = sourceMessageId
|
||||||
|
}
|
||||||
|
|
||||||
var sendTextAsCaption = false
|
var sendTextAsCaption = false
|
||||||
if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile {
|
if forwardSourceMessageId == nil {
|
||||||
sendTextAsCaption = true
|
if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile {
|
||||||
|
sendTextAsCaption = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
@ -2133,8 +2140,8 @@ public final class ShareController: ViewController {
|
|||||||
if let startAtTimestamp = mediaParameters?.startAtTimestamp, let startAtTimestampNode = strongSelf.controllerNode.startAtTimestampNode, startAtTimestampNode.value {
|
if let startAtTimestamp = mediaParameters?.startAtTimestamp, let startAtTimestampNode = strongSelf.controllerNode.startAtTimestampNode, startAtTimestampNode.value {
|
||||||
attributes.append(ForwardVideoTimestampAttribute(timestamp: startAtTimestamp))
|
attributes.append(ForwardVideoTimestampAttribute(timestamp: startAtTimestamp))
|
||||||
}
|
}
|
||||||
if case let .message(message, _) = mediaReference, let sourceMessageId = message.id, (sourceMessageId.peerId.namespace == Namespaces.Peer.CloudUser || sourceMessageId.peerId.namespace == Namespaces.Peer.CloudGroup || sourceMessageId.peerId.namespace == Namespaces.Peer.CloudChannel) {
|
if let forwardSourceMessageId {
|
||||||
messages.append(.forward(source: sourceMessageId, threadId: threadId, grouping: .auto, attributes: attributes, correlationId: nil))
|
messages.append(.forward(source: forwardSourceMessageId, threadId: threadId, grouping: .auto, attributes: attributes, correlationId: nil))
|
||||||
} else {
|
} else {
|
||||||
messages.append(.message(text: sendTextAsCaption ? text : "", attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
messages.append(.message(text: sendTextAsCaption ? text : "", attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
}
|
}
|
||||||
|
@ -2048,7 +2048,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if displayInlineScrubber, let videoTimestamp, let file = media as? TelegramMediaFile, let duration = file.duration, duration > 1.0 {
|
if displayInlineScrubber, videoTimestamp != nil, let file = media as? TelegramMediaFile, let duration = file.duration, duration > 1.0 {
|
||||||
let timestampContainerView: UIView
|
let timestampContainerView: UIView
|
||||||
if let current = strongSelf.timestampContainerView {
|
if let current = strongSelf.timestampContainerView {
|
||||||
timestampContainerView = current
|
timestampContainerView = current
|
||||||
@ -2097,12 +2097,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
let videoTimestampBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: imageFrame.height - 3.0), size: CGSize(width: imageFrame.width, height: 3.0))
|
let videoTimestampBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: imageFrame.height - 3.0), size: CGSize(width: imageFrame.width, height: 3.0))
|
||||||
videoTimestampBackgroundLayer.frame = videoTimestampBackgroundFrame
|
videoTimestampBackgroundLayer.frame = videoTimestampBackgroundFrame
|
||||||
|
|
||||||
var fraction = Double(videoTimestamp) / duration
|
strongSelf.updatePlaybackPosition()
|
||||||
fraction = max(0.0, min(1.0, fraction))
|
|
||||||
|
|
||||||
let foregroundWidth = round(fraction * videoTimestampBackgroundFrame.width)
|
|
||||||
let videoTimestampForegroundFrame = CGRect(origin: CGPoint(x: videoTimestampBackgroundFrame.minX, y: videoTimestampBackgroundFrame.minY), size: CGSize(width: foregroundWidth, height: videoTimestampBackgroundFrame.height))
|
|
||||||
videoTimestampForegroundLayer.frame = videoTimestampForegroundFrame
|
|
||||||
} else {
|
} else {
|
||||||
if let timestampContainerView = strongSelf.timestampContainerView {
|
if let timestampContainerView = strongSelf.timestampContainerView {
|
||||||
strongSelf.timestampContainerView = nil
|
strongSelf.timestampContainerView = nil
|
||||||
@ -2977,6 +2972,52 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
secretTimer.invalidate()
|
secretTimer.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updatePlaybackPosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updatePlaybackPosition() {
|
||||||
|
guard let message = self.message else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let videoTimestampBackgroundLayer = self.videoTimestampBackgroundLayer, let videoTimestampForegroundLayer = self.videoTimestampForegroundLayer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let file = self.media as? TelegramMediaFile, let duration = file.duration else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoTimestamp: Double?
|
||||||
|
var storedVideoTimestamp: Double?
|
||||||
|
for attribute in message.attributes {
|
||||||
|
if let attribute = attribute as? ForwardVideoTimestampAttribute {
|
||||||
|
videoTimestamp = Double(attribute.timestamp)
|
||||||
|
} else if let attribute = attribute as? DerivedDataMessageAttribute {
|
||||||
|
if let value = attribute.data["mps"]?.get(MediaPlaybackStoredState.self) {
|
||||||
|
storedVideoTimestamp = value.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let storedVideoTimestamp {
|
||||||
|
videoTimestamp = storedVideoTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
if let playerStatus = self.playerStatus {
|
||||||
|
videoTimestamp = playerStatus.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let videoTimestamp else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let videoTimestampBackgroundFrame = videoTimestampBackgroundLayer.frame
|
||||||
|
|
||||||
|
var fraction = videoTimestamp / duration
|
||||||
|
fraction = max(0.0, min(1.0, fraction))
|
||||||
|
|
||||||
|
let foregroundWidth = floorToScreenPixels(fraction * videoTimestampBackgroundFrame.width)
|
||||||
|
let videoTimestampForegroundFrame = CGRect(origin: CGPoint(x: videoTimestampBackgroundFrame.minX, y: videoTimestampBackgroundFrame.minY), size: CGSize(width: foregroundWidth, height: videoTimestampBackgroundFrame.height))
|
||||||
|
videoTimestampForegroundLayer.frame = videoTimestampForegroundFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reveal() {
|
public func reveal() {
|
||||||
@ -3059,6 +3100,16 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let timestampContainerView = self.timestampContainerView {
|
||||||
|
if isHidden {
|
||||||
|
timestampContainerView.isHidden = true
|
||||||
|
} else {
|
||||||
|
if timestampContainerView.isHidden {
|
||||||
|
timestampContainerView.isHidden = false
|
||||||
|
timestampContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func transitionNode(adjustRect: Bool) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
public func transitionNode(adjustRect: Bool) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
|
@ -1011,6 +1011,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
private let closeButton = ComponentView<Empty>()
|
private let closeButton = ComponentView<Empty>()
|
||||||
|
|
||||||
private let title = ComponentView<Empty>()
|
private let title = ComponentView<Empty>()
|
||||||
|
private let subtitle = ComponentView<Empty>()
|
||||||
private let descriptionText = ComponentView<Empty>()
|
private let descriptionText = ComponentView<Empty>()
|
||||||
|
|
||||||
private let badgeStars = BadgeStarsView()
|
private let badgeStars = BadgeStarsView()
|
||||||
@ -1790,6 +1791,18 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
let title = self.title
|
let title = self.title
|
||||||
let descriptionText = self.descriptionText
|
let descriptionText = self.descriptionText
|
||||||
let actionButton = self.actionButton
|
let actionButton = self.actionButton
|
||||||
|
|
||||||
|
let titleSubtitleSpacing: CGFloat = 1.0
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let subtitleSize = self.subtitle.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: "from \(currentMyPeer.compactDisplayTitle)", font: Font.regular(12.0), textColor: environment.theme.list.itemSecondaryTextColor))
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
let titleSize = title.update(
|
let titleSize = title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
@ -1799,13 +1812,24 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
|
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
|
||||||
)
|
)
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
|
|
||||||
|
let titleSubtitleHeight = titleSize.height + titleSubtitleSpacing + subtitleSize.height
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: floor((56.0 - titleSubtitleHeight) * 0.5)), size: titleSize)
|
||||||
if let titleView = title.view {
|
if let titleView = title.view {
|
||||||
if titleView.superview == nil {
|
if titleView.superview == nil {
|
||||||
self.navigationBarContainer.addSubview(titleView)
|
self.navigationBarContainer.addSubview(titleView)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: titleView, frame: titleFrame)
|
transition.setFrame(view: titleView, frame: titleFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) * 0.5), y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)
|
||||||
|
if let subtitleView = subtitle.view {
|
||||||
|
if subtitleView.superview == nil {
|
||||||
|
self.navigationBarContainer.addSubview(subtitleView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: subtitleView, frame: subtitleFrame)
|
||||||
|
}
|
||||||
|
|
||||||
contentHeight += 56.0
|
contentHeight += 56.0
|
||||||
contentHeight += 8.0
|
contentHeight += 8.0
|
||||||
@ -1995,17 +2019,37 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
guard let self, let component = self.component, let peer = topPeer.peer else {
|
guard let self, let component = self.component, let peer = topPeer.peer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let peerInfoController = component.context.sharedContext.makePeerInfoController(
|
guard let controller = self.environment?.controller() else {
|
||||||
context: component.context,
|
return
|
||||||
updatedPresentationData: nil,
|
|
||||||
peer: peer._asPeer(),
|
|
||||||
mode: .generic,
|
|
||||||
avatarInitiallyExpanded: false,
|
|
||||||
fromChat: false,
|
|
||||||
requestsContext: nil
|
|
||||||
) {
|
|
||||||
self.environment?.controller()?.push(peerInfoController)
|
|
||||||
}
|
}
|
||||||
|
guard let navigationController = controller.navigationController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var viewControllers = navigationController.viewControllers
|
||||||
|
guard let index = viewControllers.firstIndex(where: { $0 === controller }) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let context = component.context
|
||||||
|
|
||||||
|
if case .user = peer {
|
||||||
|
if let peerInfoController = context.sharedContext.makePeerInfoController(
|
||||||
|
context: context,
|
||||||
|
updatedPresentationData: nil,
|
||||||
|
peer: peer._asPeer(),
|
||||||
|
mode: .generic,
|
||||||
|
avatarInitiallyExpanded: false,
|
||||||
|
fromChat: false,
|
||||||
|
requestsContext: nil
|
||||||
|
) {
|
||||||
|
viewControllers.insert(peerInfoController, at: index)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
||||||
|
viewControllers.insert(chatController, at: index)
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: true)
|
||||||
|
controller.dismiss()
|
||||||
},
|
},
|
||||||
isEnabled: topPeer.peer != nil && topPeer.peer?.id != component.context.account.peerId,
|
isEnabled: topPeer.peer != nil && topPeer.peer?.id != component.context.account.peerId,
|
||||||
animateAlpha: false
|
animateAlpha: false
|
||||||
|
@ -576,7 +576,8 @@ public final class GifPagerContentComponent: Component {
|
|||||||
private let standaloneShimmerEffect: StandaloneShimmerEffect
|
private let standaloneShimmerEffect: StandaloneShimmerEffect
|
||||||
|
|
||||||
private let backgroundView: BlurredBackgroundView
|
private let backgroundView: BlurredBackgroundView
|
||||||
private var vibrancyEffectView: UIVisualEffectView?
|
private let backgroundTintView: UIView
|
||||||
|
private var vibrancyEffectView: UIView?
|
||||||
private let mirrorContentScrollView: UIView
|
private let mirrorContentScrollView: UIView
|
||||||
private let scrollView: ContentScrollView
|
private let scrollView: ContentScrollView
|
||||||
private let scrollClippingView: UIView
|
private let scrollClippingView: UIView
|
||||||
@ -598,6 +599,7 @@ public final class GifPagerContentComponent: Component {
|
|||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.backgroundView = BlurredBackgroundView(color: nil)
|
self.backgroundView = BlurredBackgroundView(color: nil)
|
||||||
|
self.backgroundTintView = UIView()
|
||||||
|
|
||||||
self.shimmerHostView = PortalSourceView()
|
self.shimmerHostView = PortalSourceView()
|
||||||
self.standaloneShimmerEffect = StandaloneShimmerEffect()
|
self.standaloneShimmerEffect = StandaloneShimmerEffect()
|
||||||
@ -620,6 +622,7 @@ public final class GifPagerContentComponent: Component {
|
|||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.backgroundView.addSubview(self.backgroundTintView)
|
||||||
self.addSubview(self.backgroundView)
|
self.addSubview(self.backgroundView)
|
||||||
|
|
||||||
self.shimmerHostView.alpha = 0.0
|
self.shimmerHostView.alpha = 0.0
|
||||||
@ -983,15 +986,15 @@ public final class GifPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if self.vibrancyEffectView == nil {
|
if self.vibrancyEffectView == nil {
|
||||||
let style: UIBlurEffect.Style
|
let vibrancyEffectView = UIView()
|
||||||
style = .extraLight
|
vibrancyEffectView.backgroundColor = .white
|
||||||
let blurEffect = UIBlurEffect(style: style)
|
if let filter = CALayer.luminanceToAlpha() {
|
||||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
|
vibrancyEffectView.layer.filters = [filter]
|
||||||
let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect)
|
}
|
||||||
self.vibrancyEffectView = vibrancyEffectView
|
self.vibrancyEffectView = vibrancyEffectView
|
||||||
self.backgroundView.addSubview(vibrancyEffectView)
|
self.backgroundTintView.mask = vibrancyEffectView
|
||||||
vibrancyEffectView.contentView.addSubview(self.mirrorContentScrollView)
|
vibrancyEffectView.addSubview(self.mirrorContentScrollView)
|
||||||
vibrancyEffectView.contentView.addSubview(self.mirrorSearchHeaderContainer)
|
vibrancyEffectView.addSubview(self.mirrorSearchHeaderContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1000,7 +1003,11 @@ public final class GifPagerContentComponent: Component {
|
|||||||
if hideBackground {
|
if hideBackground {
|
||||||
backgroundColor = backgroundColor.withAlphaComponent(0.01)
|
backgroundColor = backgroundColor.withAlphaComponent(0.01)
|
||||||
}
|
}
|
||||||
self.backgroundView.updateColor(color: backgroundColor, enableBlur: true, forceKeepBlur: false, transition: transition.containedViewLayoutTransition)
|
|
||||||
|
self.backgroundTintView.backgroundColor = backgroundColor
|
||||||
|
transition.setFrame(view: self.backgroundTintView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||||
|
|
||||||
|
self.backgroundView.updateColor(color: .clear, enableBlur: true, forceKeepBlur: true, transition: transition.containedViewLayoutTransition)
|
||||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||||
self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)
|
self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
|
@ -1089,7 +1089,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
if let starrefCommissionPermille = transaction.starrefCommissionPermille, transaction.starrefPeerId != nil {
|
if let starrefCommissionPermille = transaction.starrefCommissionPermille, transaction.starrefPeerId != nil {
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "commission",
|
id: "commission",
|
||||||
title: "Commission",
|
title: strings.StarsTransaction_Commission,
|
||||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(formatPermille(starrefCommissionPermille))%", font: tableFont, textColor: tableTextColor))
|
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(formatPermille(starrefCommissionPermille))%", font: tableFont, textColor: tableTextColor))
|
||||||
)),
|
)),
|
||||||
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
|
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
|
||||||
|
@ -408,12 +408,9 @@ extension ChatControllerImpl {
|
|||||||
guard let starsContext = self.context.starsContext else {
|
guard let starsContext = self.context.starsContext else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let peerId = self.chatLocation.peerId else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let _ = (combineLatest(
|
let _ = (combineLatest(
|
||||||
starsContext.state,
|
starsContext.state,
|
||||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ReactionSettings(id: peerId))
|
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ReactionSettings(id: message.id.peerId))
|
||||||
)
|
)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state, reactionSettings in
|
|> deliverOnMainQueue).start(next: { [weak self] state, reactionSettings in
|
||||||
@ -446,7 +443,7 @@ extension ChatControllerImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let purchaseScreen = strongSelf.context.sharedContext.makeStarsPurchaseScreen(context: strongSelf.context, starsContext: starsContext, options: options, purpose: .reactions(peerId: peerId, requiredStars: 1), completion: { result in
|
let purchaseScreen = strongSelf.context.sharedContext.makeStarsPurchaseScreen(context: strongSelf.context, starsContext: starsContext, options: options, purpose: .reactions(peerId: message.id.peerId, requiredStars: 1), completion: { result in
|
||||||
let _ = result
|
let _ = result
|
||||||
//TODO:release
|
//TODO:release
|
||||||
})
|
})
|
||||||
|
@ -493,46 +493,62 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func displayOrUpdateSendStarsUndo(messageId: EngineMessage.Id, count: Int, privacy: TelegramPaidReactionPrivacy) {
|
func displayOrUpdateSendStarsUndo(messageId: EngineMessage.Id, count: Int, privacy: TelegramPaidReactionPrivacy) {
|
||||||
if self.currentSendStarsUndoMessageId != messageId {
|
var privacyPeer: Signal<EnginePeer?, NoError> = .single(nil)
|
||||||
if let current = self.currentSendStarsUndoController {
|
if case let .peer(id) = privacy {
|
||||||
self.currentSendStarsUndoController = nil
|
privacyPeer = self.context.engine.data.get(
|
||||||
current.dismiss()
|
TelegramEngine.EngineData.Item.Peer.Peer(id: id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let _ = (privacyPeer
|
||||||
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] privacyPeer in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if self.currentSendStarsUndoMessageId != messageId {
|
||||||
if let _ = self.currentSendStarsUndoController {
|
if let current = self.currentSendStarsUndoController {
|
||||||
self.currentSendStarsUndoCount += count
|
self.currentSendStarsUndoController = nil
|
||||||
} else {
|
current.dismiss()
|
||||||
self.currentSendStarsUndoCount = count
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let title: String
|
if let _ = self.currentSendStarsUndoController {
|
||||||
if case .anonymous = privacy {
|
self.currentSendStarsUndoCount += count
|
||||||
title = self.presentationData.strings.Chat_ToastStarsSent_AnonymousTitle(Int32(self.currentSendStarsUndoCount))
|
} else {
|
||||||
} else {
|
self.currentSendStarsUndoCount = count
|
||||||
title = self.presentationData.strings.Chat_ToastStarsSent_Title(Int32(self.currentSendStarsUndoCount))
|
}
|
||||||
}
|
|
||||||
|
let title: String
|
||||||
let textItems = AnimatedTextComponent.extractAnimatedTextString(string: self.presentationData.strings.Chat_ToastStarsSent_Text("", ""), id: "text", mapping: [
|
if case .anonymous = privacy {
|
||||||
0: .number(self.currentSendStarsUndoCount, minDigits: 1),
|
title = self.presentationData.strings.Chat_ToastStarsSent_AnonymousTitle(Int32(self.currentSendStarsUndoCount))
|
||||||
1: .text(self.presentationData.strings.Chat_ToastStarsSent_TextStarAmount(Int32(self.currentSendStarsUndoCount)))
|
} else if case .peer = privacy, let privacyPeer {
|
||||||
])
|
let rawTitle = self.presentationData.strings.Chat_ToastStarsSent_TitleChannel(Int32(self.currentSendStarsUndoCount))
|
||||||
|
title = rawTitle.replacingOccurrences(of: "{name}", with: privacyPeer.compactDisplayTitle)
|
||||||
self.currentSendStarsUndoMessageId = messageId
|
} else {
|
||||||
if let current = self.currentSendStarsUndoController {
|
title = self.presentationData.strings.Chat_ToastStarsSent_Title(Int32(self.currentSendStarsUndoCount))
|
||||||
current.content = .starsSent(context: self.context, title: title, text: textItems)
|
}
|
||||||
} else {
|
|
||||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
let textItems = AnimatedTextComponent.extractAnimatedTextString(string: self.presentationData.strings.Chat_ToastStarsSent_Text("", ""), id: "text", mapping: [
|
||||||
guard let self else {
|
0: .number(self.currentSendStarsUndoCount, minDigits: 1),
|
||||||
|
1: .text(self.presentationData.strings.Chat_ToastStarsSent_TextStarAmount(Int32(self.currentSendStarsUndoCount)))
|
||||||
|
])
|
||||||
|
|
||||||
|
self.currentSendStarsUndoMessageId = messageId
|
||||||
|
if let current = self.currentSendStarsUndoController {
|
||||||
|
current.content = .starsSent(context: self.context, title: title, text: textItems)
|
||||||
|
} else {
|
||||||
|
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||||
|
guard let self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if case .undo = action {
|
||||||
|
self.context.engine.messages.cancelPendingSendStarsReaction(id: messageId)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
if case .undo = action {
|
self.currentSendStarsUndoController = controller
|
||||||
self.context.engine.messages.cancelPendingSendStarsReaction(id: messageId)
|
self.present(controller, in: .current)
|
||||||
}
|
}
|
||||||
return false
|
})
|
||||||
})
|
|
||||||
self.currentSendStarsUndoController = controller
|
|
||||||
self.present(controller, in: .current)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user