Various improvements

This commit is contained in:
Isaac 2025-02-18 14:43:23 +01:00
parent ce9f8fcb7c
commit f17ead143a
15 changed files with 307 additions and 106 deletions

View File

@ -12845,6 +12845,8 @@ Sorry for the inconvenience.";
"Chat.ToastStarsSent.Title_1" = "Star 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_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_URL" = "https://telegram.org/tos/stars";
"StarsTransaction.Commission" = "Commission";

View File

@ -238,8 +238,21 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
case .Local:
break
case .Remote, .Paused:
if let image = cloudFetchIcon {
statusState = .customIcon(image)
var isHLS = false
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: {})
@ -954,6 +967,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
var messageText = NSMutableAttributedString(string: "")
var hasCaption = false
var mediaDuration: Double?
for media in message.media {
if media is TelegramMediaPaidContent {
hasCaption = true
@ -961,6 +975,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
hasCaption = true
} else if let file = media as? TelegramMediaFile {
hasCaption = file.mimeType.hasPrefix("image/") || file.mimeType.hasPrefix("video/")
mediaDuration = file.duration
} else if media is TelegramMediaInvoice {
hasCaption = true
}
@ -974,6 +989,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
}
var text = message.text
if let result = addLocallyGeneratedEntities(text, enabledTypes: [.timecode], entities: entities, mediaDuration: mediaDuration) {
entities = result
}
if let translateToLanguage, !text.isEmpty {
for attribute in message.attributes {
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {

View File

@ -388,15 +388,14 @@ final class ChatVideoGalleryItemScrubberView: UIView {
let targetCloneView = scrubberTransition.makeView()
self.addSubview(targetCloneView)
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
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
scrubberTransitionView.isHidden = true
ContainedViewLayoutTransition.animated(duration: 0.08, curve: .easeInOut).updateAlpha(layer: targetCloneView.layer, alpha: 0.0, completion: { [weak scrubberTransitionView, weak targetCloneView] _ in
scrubberTransitionView?.isHidden = false
ContainedViewLayoutTransition.animated(duration: 0.08, curve: .easeInOut).updateAlpha(layer: targetCloneView.layer, alpha: 0.0, completion: { [weak targetCloneView] _ in
targetCloneView?.removeFromSuperview()
})
@ -428,11 +427,11 @@ final class ChatVideoGalleryItemScrubberView: UIView {
let targetCloneView = scrubberTransition.makeView()
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))
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
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
scrubberTransitionView.isHidden = true
transition.updateAlpha(layer: targetCloneView.layer, alpha: 1.0, completion: { [weak scrubberTransitionView] _ in

View File

@ -6,18 +6,26 @@ import Display
public final class GalleryItemScrubberTransition {
public final class Scrubber {
public struct TransitionState: Equatable {
public enum Direction {
case `in`
case out
}
public var sourceSize: CGSize
public var destinationSize: CGSize
public var progress: CGFloat
public var direction: Direction
public init(
sourceSize: CGSize,
destinationSize: CGSize,
progress: CGFloat
progress: CGFloat,
direction: Direction
) {
self.sourceSize = sourceSize
self.destinationSize = destinationSize
self.progress = progress
self.direction = direction
}
}

View File

@ -1006,6 +1006,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
guard let overlayController = self.overlayController else {
return
}
overlayController.removePictureInPictureContent(content: self)
self.node.canAttachContent = false
if self.didExpand {
@ -1413,7 +1414,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if playerStatusValue.duration >= 60.0 * 10.0 {
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)"
publicLinkPrefix = ShareControllerSubject.PublicLinkPrefix(
visibleString: visibleString,
@ -1878,23 +1879,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.mediaPlaybackStateDisposable.set((throttledSignal
|> deliverOnMainQueue).start(next: { [weak self] status in
guard let strongSelf = self, let videoNode = strongSelf.videoNode, videoNode.ownsContentNode else {
guard let self else {
return
}
if let status = status {
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
}
item.storeMediaPlaybackState(message.id, timestamp, status.baseRate)
} else {
item.storeMediaPlaybackState(message.id, nil, status.baseRate)
}
if let status {
self.maybeStorePlaybackStatus(status: status)
}
}))
}
@ -2007,6 +1997,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
case let .buffering(_, whilePlaying, _, display):
displayProgress = display
initialBuffering = !whilePlaying
if item.content is HLSVideoContent && display {
initialBuffering = true
}
isPaused = !whilePlaying
var isStreaming = false
if let fetchStatus = strongSelf.fetchStatus {
@ -2101,7 +2094,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if hasStarted || strongSelf.didPause {
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
} 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 {
strongSelf.footerContentNode.content = .playback(paused: false, seekable: seekable)
@ -2369,6 +2366,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
videoNode.seek(0.0)
}
} else {
if let status = self.playerStatusValue {
self.maybeStorePlaybackStatus(status: status)
}
videoNode.continuePlayingWithoutSound()
}
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 {
if let item = self.item {
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) {
if let status = self.playerStatusValue {
self.maybeStorePlaybackStatus(status: status)
}
self.isAnimatingOut = true
guard let videoNode = self.videoNode else {
@ -2785,6 +2813,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
if videoNode.hasAttachedContext {
if let status = self.playerStatusValue {
self.maybeStorePlaybackStatus(status: status)
}
if self.isPaused || !self.keepSoundOnDismiss {
if let item = self.item, item.content is HLSVideoContent {
} else {

View File

@ -205,13 +205,14 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
private var isSoundEnabled: Bool
private var isMuted: Bool
private var isAmbientMode: Bool
private var seekId: Int = 0
private var seekTimestamp: Double = 0.0
private var pendingSeekTimestamp: Double?
private var pendingContinuePlaybackAfterSeekToTimestamp: Double?
private var shouldNotifySeeked: Bool = false
private var stoppedAtEnd: Bool = false
private var bufferingStartTime: Double?
private var renderSynchronizerRate: Double = 0.0
private var videoIsRequestingMediaData: Bool = false
@ -704,9 +705,21 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
self.triggerRequestMediaData()
}
if isBuffering {
if self.bufferingStartTime == nil {
self.bufferingStartTime = CFAbsoluteTimeGetCurrent()
}
} else {
self.bufferingStartTime = nil
}
let playbackStatus: MediaPlayerPlaybackStatus
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 {
playbackStatus = .playing
} else {
@ -918,6 +931,7 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
}
self.pendingSeekTimestamp = nil
self.stoppedAtEnd = false
self.updateInternalState()
}
}

View File

@ -80,7 +80,11 @@ public final class FFMpegMediaDataReaderV2: MediaDataReader {
if (codecName == "h264" || codecName == "hevc") {
passthroughDecoder = false
#if targetEnvironment(simulator)
useHardwareAcceleration = false
#else
useHardwareAcceleration = true
#endif
}
if (codecName == "av1" || codecName == "av01") {
passthroughDecoder = false

View File

@ -489,7 +489,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
chapterNodesContainer.isUserInteractionEnabled = false
chapterNodesContainerImpl = chapterNodesContainer
var chapters = chapters
var chapters = chapters.sorted(by: { $0.start < $1.start })
if let firstChapter = chapters.first, firstChapter.start > 0.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 width = endPosition - startPosition
if width < lineWidth * 0.5 {
if width < lineWidth * 0.5 && i != node.chapterNodes.count - 1 {
previousChapterNode.frame = CGRect()
continue
}

View File

@ -2055,9 +2055,16 @@ public final class ShareController: ViewController {
shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages))
}
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
if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile {
sendTextAsCaption = true
if forwardSourceMessageId == nil {
if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile {
sendTextAsCaption = true
}
}
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 {
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) {
messages.append(.forward(source: sourceMessageId, threadId: threadId, grouping: .auto, attributes: attributes, correlationId: nil))
if let forwardSourceMessageId {
messages.append(.forward(source: forwardSourceMessageId, threadId: threadId, grouping: .auto, attributes: attributes, correlationId: nil))
} 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: []))
}

View File

@ -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
if let current = strongSelf.timestampContainerView {
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))
videoTimestampBackgroundLayer.frame = videoTimestampBackgroundFrame
var fraction = Double(videoTimestamp) / duration
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
strongSelf.updatePlaybackPosition()
} else {
if let timestampContainerView = strongSelf.timestampContainerView {
strongSelf.timestampContainerView = nil
@ -2977,6 +2972,52 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
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() {
@ -3059,6 +3100,16 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
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?))? {

View File

@ -1011,6 +1011,7 @@ private final class ChatSendStarsScreenComponent: Component {
private let closeButton = ComponentView<Empty>()
private let title = ComponentView<Empty>()
private let subtitle = ComponentView<Empty>()
private let descriptionText = ComponentView<Empty>()
private let badgeStars = BadgeStarsView()
@ -1790,6 +1791,18 @@ private final class ChatSendStarsScreenComponent: Component {
let title = self.title
let descriptionText = self.descriptionText
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(
transition: .immediate,
@ -1799,13 +1812,24 @@ private final class ChatSendStarsScreenComponent: Component {
environment: {},
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 titleView.superview == nil {
self.navigationBarContainer.addSubview(titleView)
}
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 += 8.0
@ -1995,17 +2019,37 @@ private final class ChatSendStarsScreenComponent: Component {
guard let self, let component = self.component, let peer = topPeer.peer else {
return
}
if let peerInfoController = component.context.sharedContext.makePeerInfoController(
context: component.context,
updatedPresentationData: nil,
peer: peer._asPeer(),
mode: .generic,
avatarInitiallyExpanded: false,
fromChat: false,
requestsContext: nil
) {
self.environment?.controller()?.push(peerInfoController)
guard let controller = self.environment?.controller() else {
return
}
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,
animateAlpha: false

View File

@ -576,7 +576,8 @@ public final class GifPagerContentComponent: Component {
private let standaloneShimmerEffect: StandaloneShimmerEffect
private let backgroundView: BlurredBackgroundView
private var vibrancyEffectView: UIVisualEffectView?
private let backgroundTintView: UIView
private var vibrancyEffectView: UIView?
private let mirrorContentScrollView: UIView
private let scrollView: ContentScrollView
private let scrollClippingView: UIView
@ -598,6 +599,7 @@ public final class GifPagerContentComponent: Component {
override init(frame: CGRect) {
self.backgroundView = BlurredBackgroundView(color: nil)
self.backgroundTintView = UIView()
self.shimmerHostView = PortalSourceView()
self.standaloneShimmerEffect = StandaloneShimmerEffect()
@ -620,6 +622,7 @@ public final class GifPagerContentComponent: Component {
super.init(frame: frame)
self.backgroundView.addSubview(self.backgroundTintView)
self.addSubview(self.backgroundView)
self.shimmerHostView.alpha = 0.0
@ -983,15 +986,15 @@ public final class GifPagerContentComponent: Component {
}
} else {
if self.vibrancyEffectView == nil {
let style: UIBlurEffect.Style
style = .extraLight
let blurEffect = UIBlurEffect(style: style)
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect)
let vibrancyEffectView = UIView()
vibrancyEffectView.backgroundColor = .white
if let filter = CALayer.luminanceToAlpha() {
vibrancyEffectView.layer.filters = [filter]
}
self.vibrancyEffectView = vibrancyEffectView
self.backgroundView.addSubview(vibrancyEffectView)
vibrancyEffectView.contentView.addSubview(self.mirrorContentScrollView)
vibrancyEffectView.contentView.addSubview(self.mirrorSearchHeaderContainer)
self.backgroundTintView.mask = vibrancyEffectView
vibrancyEffectView.addSubview(self.mirrorContentScrollView)
vibrancyEffectView.addSubview(self.mirrorSearchHeaderContainer)
}
}
@ -1000,7 +1003,11 @@ public final class GifPagerContentComponent: Component {
if hideBackground {
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)
self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)

View File

@ -1089,7 +1089,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let starrefCommissionPermille = transaction.starrefCommissionPermille, transaction.starrefPeerId != nil {
tableItems.append(.init(
id: "commission",
title: "Commission",
title: strings.StarsTransaction_Commission,
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)

View File

@ -408,12 +408,9 @@ extension ChatControllerImpl {
guard let starsContext = self.context.starsContext else {
return
}
guard let peerId = self.chatLocation.peerId else {
return
}
let _ = (combineLatest(
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)
|> deliverOnMainQueue).start(next: { [weak self] state, reactionSettings in
@ -446,7 +443,7 @@ extension ChatControllerImpl {
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
//TODO:release
})

View File

@ -493,46 +493,62 @@ extension ChatControllerImpl {
}
func displayOrUpdateSendStarsUndo(messageId: EngineMessage.Id, count: Int, privacy: TelegramPaidReactionPrivacy) {
if self.currentSendStarsUndoMessageId != messageId {
if let current = self.currentSendStarsUndoController {
self.currentSendStarsUndoController = nil
current.dismiss()
var privacyPeer: Signal<EnginePeer?, NoError> = .single(nil)
if case let .peer(id) = privacy {
privacyPeer = self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: id)
)
}
let _ = (privacyPeer
|> deliverOnMainQueue).startStandalone(next: { [weak self] privacyPeer in
guard let self else {
return
}
}
if let _ = self.currentSendStarsUndoController {
self.currentSendStarsUndoCount += count
} else {
self.currentSendStarsUndoCount = count
}
let title: String
if case .anonymous = privacy {
title = self.presentationData.strings.Chat_ToastStarsSent_AnonymousTitle(Int32(self.currentSendStarsUndoCount))
} else {
title = self.presentationData.strings.Chat_ToastStarsSent_Title(Int32(self.currentSendStarsUndoCount))
}
let textItems = AnimatedTextComponent.extractAnimatedTextString(string: self.presentationData.strings.Chat_ToastStarsSent_Text("", ""), id: "text", mapping: [
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 {
if self.currentSendStarsUndoMessageId != messageId {
if let current = self.currentSendStarsUndoController {
self.currentSendStarsUndoController = nil
current.dismiss()
}
}
if let _ = self.currentSendStarsUndoController {
self.currentSendStarsUndoCount += count
} else {
self.currentSendStarsUndoCount = count
}
let title: String
if case .anonymous = privacy {
title = self.presentationData.strings.Chat_ToastStarsSent_AnonymousTitle(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)
} else {
title = self.presentationData.strings.Chat_ToastStarsSent_Title(Int32(self.currentSendStarsUndoCount))
}
let textItems = AnimatedTextComponent.extractAnimatedTextString(string: self.presentationData.strings.Chat_ToastStarsSent_Text("", ""), id: "text", mapping: [
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
}
if case .undo = action {
self.context.engine.messages.cancelPendingSendStarsReaction(id: messageId)
}
return false
})
self.currentSendStarsUndoController = controller
self.present(controller, in: .current)
}
})
self.currentSendStarsUndoController = controller
self.present(controller, in: .current)
}
})
}
}