From 1f43a83c5cf8c12927b4ca10e2fc7d5cc1d4fd02 Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Wed, 30 Apr 2025 22:33:37 +0100 Subject: [PATCH 01/22] revert ffmpeg headers --- submodules/FFMpegBinding/Sources/FFMpegAVCodec.m | 2 +- submodules/FFMpegBinding/Sources/FFMpegAVCodecContext.m | 4 ++-- submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m | 4 ++-- submodules/FFMpegBinding/Sources/FFMpegAVFrame.m | 2 +- submodules/FFMpegBinding/Sources/FFMpegAVIOContext.m | 2 +- submodules/FFMpegBinding/Sources/FFMpegGlobals.m | 2 +- submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m | 8 ++++---- submodules/FFMpegBinding/Sources/FFMpegPacket.m | 4 ++-- submodules/FFMpegBinding/Sources/FFMpegRemuxer.m | 6 +++--- submodules/FFMpegBinding/Sources/FFMpegSWResample.m | 6 +++--- submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m | 6 +++--- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVCodec.m b/submodules/FFMpegBinding/Sources/FFMpegAVCodec.m index 739ab5efb3..f50633c2a8 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVCodec.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVCodec.m @@ -1,6 +1,6 @@ #import -#import +#import "libavcodec/avcodec.h" @interface FFMpegAVCodec () { AVCodec const *_impl; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVCodecContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVCodecContext.m index 0fae131670..ee2cde92c0 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVCodecContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVCodecContext.m @@ -3,8 +3,8 @@ #import #import -#import -#import +#import "libavformat/avformat.h" +#import "libavcodec/avcodec.h" static enum AVPixelFormat getPreferredPixelFormat(__unused AVCodecContext *ctx, __unused const enum AVPixelFormat *pix_fmts) { return AV_PIX_FMT_VIDEOTOOLBOX; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m index a98c295d6d..ebc0d4f963 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m @@ -4,8 +4,8 @@ #import #import -#import -#import +#import "libavcodec/avcodec.h" +#import "libavformat/avformat.h" int FFMpegCodecIdH264 = AV_CODEC_ID_H264; int FFMpegCodecIdHEVC = AV_CODEC_ID_HEVC; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m index 9c17b53c09..fda8224dc7 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m @@ -1,6 +1,6 @@ #import -#import +#import "libavformat/avformat.h" @interface FFMpegAVFrame () { AVFrame *_impl; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVIOContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVIOContext.m index ebad7d28c9..be3cecf4bd 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVIOContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVIOContext.m @@ -1,6 +1,6 @@ #import -#import +#import "libavformat/avformat.h" int FFMPEG_CONSTANT_AVERROR_EOF = AVERROR_EOF; diff --git a/submodules/FFMpegBinding/Sources/FFMpegGlobals.m b/submodules/FFMpegBinding/Sources/FFMpegGlobals.m index 459725e5ab..f0ff9091bb 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegGlobals.m +++ b/submodules/FFMpegBinding/Sources/FFMpegGlobals.m @@ -1,6 +1,6 @@ #import -#import +#import "libavformat/avformat.h" @implementation FFMpegGlobals diff --git a/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m b/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m index 22531987c8..23a0498bef 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m +++ b/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m @@ -1,10 +1,10 @@ #import #import -#include -#include -#include -#include +#include "libavutil/timestamp.h" +#include "libavformat/avformat.h">" +#include "libavcodec/avcodec.h">" +#include "libswresample/swresample.h" #define MOV_TIMESCALE 1000 diff --git a/submodules/FFMpegBinding/Sources/FFMpegPacket.m b/submodules/FFMpegBinding/Sources/FFMpegPacket.m index 4ef8b24dbe..7da5655c96 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegPacket.m +++ b/submodules/FFMpegBinding/Sources/FFMpegPacket.m @@ -2,8 +2,8 @@ #import -#import -#import +#import "libavcodec/avcodec.h" +#import "libavformat/avformat.h" @interface FFMpegPacket () { AVPacket *_impl; diff --git a/submodules/FFMpegBinding/Sources/FFMpegRemuxer.m b/submodules/FFMpegBinding/Sources/FFMpegRemuxer.m index 722a21f4fe..df3c1adfb8 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegRemuxer.m +++ b/submodules/FFMpegBinding/Sources/FFMpegRemuxer.m @@ -2,9 +2,9 @@ #import -#include -#include -#include +#include "libavutil/timestamp.h" +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" #define MOV_TIMESCALE 1000 diff --git a/submodules/FFMpegBinding/Sources/FFMpegSWResample.m b/submodules/FFMpegBinding/Sources/FFMpegSWResample.m index de38cab260..c6771b6db3 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegSWResample.m +++ b/submodules/FFMpegBinding/Sources/FFMpegSWResample.m @@ -2,9 +2,9 @@ #import -#import -#import -#import +#import "libavformat/avformat.h" +#import "libavcodec/avcodec.h" +#import "libswresample/swresample.h" @interface FFMpegSWResample () { int _sourceSampleRate; diff --git a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m index 4274fc6d6c..4746828e62 100755 --- a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m +++ b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m @@ -1,9 +1,9 @@ #import #import -#include -#include -#include +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" +#include "libavutil/imgutils.h" @interface FFMpegVideoWriter () From 79eb359587df6f159ff2b52ff715cc3294751180 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 5 May 2025 20:08:50 +0400 Subject: [PATCH 02/22] Various fixes --- .../Sources/BotCheckoutControllerNode.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index ae27df6f66..191f9b27c8 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -611,7 +611,7 @@ private final class ActionButtonPanelNode: ASDisplayNode { private(set) var isAccepted: Bool = false var isAcceptedUpdated: (() -> Void)? var openRecurrentTerms: (() -> Void)? - private var recurrentConfirmationNode: RecurrentConfirmationNode? + var recurrentConfirmationNode: RecurrentConfirmationNode? func update(presentationData: PresentationData, layout: ContainerViewLayout, invoice: BotPaymentInvoice?, botName: String?) -> (CGFloat, CGFloat) { let bottomPanelVerticalInset: CGFloat = 16.0 @@ -1211,7 +1211,8 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz payString = self.presentationData.strings.CheckoutInfo_Pay } - self.actionButton.isEnabled = isButtonEnabled + self.actionButton.isEnabled = true + self.actionButton.isImplicitlyDisabled = !isButtonEnabled if let currentPaymentMethod = self.currentPaymentMethod { switch currentPaymentMethod { @@ -1268,7 +1269,11 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } @objc func actionButtonPressed() { - self.pay() + if let recurrentConfirmationNode = self.actionButtonPanelNode.recurrentConfirmationNode, !self.actionButtonPanelNode.isAccepted { + recurrentConfirmationNode.layer.addShakeAnimation() + } else { + self.pay() + } } private func pay(savedCredentialsToken: TemporaryTwoStepPasswordToken? = nil, liabilityNoticeAccepted: Bool = false, receivedCredentials: BotPaymentCredentials? = nil) { From 17df575f541987891574fd58b068e839e52df12b Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 5 May 2025 22:20:06 +0200 Subject: [PATCH 03/22] Revert "Fix typo and crash" This reverts commit e060b91947a0b3bbb6109bccfd87f202503ceb6f. --- .../Sources/StoryItemContentComponent.swift | 2 +- .../Sources/StoryItemSetContainerComponent.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 513d067018..e50d333e51 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -466,7 +466,7 @@ final class StoryItemContentComponent: Component { return } - var useLegacyImplementation = true + var useLegacyImplementation = false if let data = component.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_video_legacystoryplayer"] as? Double { useLegacyImplementation = value != 0.0 } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index f88ddc8fd8..27b69c1316 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1875,7 +1875,7 @@ public final class StoryItemSetContainerComponent: Component { continue } var nextVisibleItem: VisibleItem? - if i != component.slice.allItems.count - 1 { + if i != component.slice.allItems.count { nextVisibleItem = self.visibleItems[component.slice.allItems[i + 1].id] } if let itemView = visibleItem.view.view as? StoryItemContentComponent.View { From 631f942a7f66d7670d03edbb7238c7310d48031e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 5 May 2025 22:20:22 +0200 Subject: [PATCH 04/22] Revert "Improve story video playback" This reverts commit d4443f9682bfa5ba72b1d311dd2862c07a8857e6. --- .../Sources/ChunkMediaPlayerV2.swift | 184 +++---- .../Components/LivestreamVideoViewV1.swift | 2 +- .../Sources/StoryContent.swift | 2 - .../Sources/StoryItemContentComponent.swift | 452 ++++-------------- .../StoryItemSetContainerComponent.swift | 15 +- .../Sources/HLSVideoJSNativeContentNode.swift | 2 +- .../Sources/NativeVideoContent.swift | 2 +- 7 files changed, 144 insertions(+), 515 deletions(-) diff --git a/submodules/MediaPlayer/Sources/ChunkMediaPlayerV2.swift b/submodules/MediaPlayer/Sources/ChunkMediaPlayerV2.swift index 9825eb7b4c..2026c7b8c7 100644 --- a/submodules/MediaPlayer/Sources/ChunkMediaPlayerV2.swift +++ b/submodules/MediaPlayer/Sources/ChunkMediaPlayerV2.swift @@ -33,101 +33,6 @@ private final class ChunkMediaPlayerExternalSourceImpl: ChunkMediaPlayerSourceIm } public final class ChunkMediaPlayerV2: ChunkMediaPlayer { - public final class AudioContext { - fileprivate let audioSessionManager: ManagedAudioSession - private var audioSessionDisposable: Disposable? - private(set) var hasAudioSession: Bool = false - private(set) var isAmbientMode: Bool = false - private(set) var isInitialized: Bool = false - - private var updatedListeners = Bag<() -> Void>() - - public init( - audioSessionManager: ManagedAudioSession - ) { - self.audioSessionManager = audioSessionManager - } - - deinit { - self.audioSessionDisposable?.dispose() - } - - func onUpdated(_ f: @escaping () -> Void) -> Disposable { - let index = self.updatedListeners.add(f) - return ActionDisposable { [weak self] in - Queue.mainQueue().async { - guard let self else { - return - } - self.updatedListeners.remove(index) - } - } - } - - func setIsAmbient(isAmbient: Bool) { - self.hasAudioSession = false - - for f in self.updatedListeners.copyItems() { - f() - } - - self.audioSessionDisposable?.dispose() - self.audioSessionDisposable = nil - } - - func update(type: ManagedAudioSessionType?) { - if let type { - if self.audioSessionDisposable == nil { - self.isInitialized = true - - self.audioSessionDisposable = self.audioSessionManager.push(params: ManagedAudioSessionClientParams( - audioSessionType: type, - activateImmediately: false, - manualActivate: { [weak self] control in - control.setupAndActivate(synchronous: false, { state in - Queue.mainQueue().async { - guard let self else { - return - } - self.hasAudioSession = true - for f in self.updatedListeners.copyItems() { - f() - } - } - }) - }, - deactivate: { [weak self] _ in - return Signal { subscriber in - guard let self else { - subscriber.putCompletion() - return EmptyDisposable - } - - self.hasAudioSession = false - for f in self.updatedListeners.copyItems() { - f() - } - subscriber.putCompletion() - - return EmptyDisposable - } - |> runOn(.mainQueue()) - }, - headsetConnectionStatusChanged: { _ in }, - availableOutputsChanged: { _, _ in } - )) - } - } else { - if let audioSessionDisposable = self.audioSessionDisposable { - self.audioSessionDisposable = nil - audioSessionDisposable.dispose() - } - - self.hasAudioSession = false - } - } - } - public enum SourceDescription { public final class ResourceDescription { public let postbox: Postbox @@ -261,10 +166,10 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { private let dataQueue: Queue private let mediaDataReaderParams: MediaDataReaderParams + private let audioSessionManager: ManagedAudioSession private let onSeeked: (() -> Void)? private weak var playerNode: MediaPlayerNode? - private let audioContext: AudioContext private let renderSynchronizer: AVSampleBufferRenderSynchronizer private var videoRenderer: AVSampleBufferDisplayLayer private var audioRenderer: AVSampleBufferAudioRenderer? @@ -293,20 +198,13 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { } public var actionAtEnd: MediaPlayerActionAtEnd = .stop - public weak var migrateToNextPlayerOnEnd: ChunkMediaPlayerV2? { - didSet { - if self.migrateToNextPlayerOnEnd !== oldValue { - self.updateInternalState() - } - } - } private var didSeekOnce: Bool = false private var isPlaying: Bool = false private var baseRate: Double = 1.0 private var isSoundEnabled: Bool private var isMuted: Bool - private var initialIsAmbient: Bool + private var isAmbientMode: Bool private var seekId: Int = 0 private var seekTimestamp: Double = 0.0 @@ -325,11 +223,12 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { private var partsStateDisposable: Disposable? private var updateTimer: Foundation.Timer? - private var audioContextUpdatedDisposable: Disposable? + private var audioSessionDisposable: Disposable? + private var hasAudioSession: Bool = false public init( params: MediaDataReaderParams, - audioContext: AudioContext, + audioSessionManager: ManagedAudioSession, source: SourceDescription, video: Bool, playAutomatically: Bool = false, @@ -348,7 +247,7 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { self.dataQueue = ChunkMediaPlayerV2.sharedDataQueue self.mediaDataReaderParams = params - self.audioContext = audioContext + self.audioSessionManager = audioSessionManager self.onSeeked = onSeeked self.playerNode = playerNode @@ -358,7 +257,7 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { self.isSoundEnabled = enableSound self.isMuted = soundMuted - self.initialIsAmbient = ambient + self.isAmbientMode = ambient self.baseRate = baseRate self.renderSynchronizer = AVSampleBufferRenderSynchronizer() @@ -397,19 +296,12 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { } else { self.renderSynchronizer.addRenderer(self.videoRenderer) } - - self.audioContextUpdatedDisposable = self.audioContext.onUpdated({ [weak self] in - guard let self else { - return - } - self.updateInternalState() - }) } deinit { self.partsStateDisposable?.dispose() self.updateTimer?.invalidate() - self.audioContextUpdatedDisposable?.dispose() + self.audioSessionDisposable?.dispose() if #available(iOS 17.0, *) { self.videoRenderer.sampleBufferRenderer.stopRequestingMediaData() @@ -429,19 +321,51 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { } private func updateInternalState() { - var audioSessionType: ManagedAudioSessionType? if self.isSoundEnabled && self.hasSound { - let isAmbient: Bool - if self.audioContext.isInitialized { - isAmbient = self.audioContext.isAmbientMode - } else { - isAmbient = self.initialIsAmbient + if self.audioSessionDisposable == nil { + self.audioSessionDisposable = self.audioSessionManager.push(params: ManagedAudioSessionClientParams( + audioSessionType: self.isAmbientMode ? .ambient : .play(mixWithOthers: false), + activateImmediately: false, + manualActivate: { [weak self] control in + control.setupAndActivate(synchronous: false, { state in + Queue.mainQueue().async { + guard let self else { + return + } + self.hasAudioSession = true + self.updateInternalState() + } + }) + }, + deactivate: { [weak self] _ in + return Signal { subscriber in + guard let self else { + subscriber.putCompletion() + return EmptyDisposable + } + + self.hasAudioSession = false + self.updateInternalState() + subscriber.putCompletion() + + return EmptyDisposable + } + |> runOn(.mainQueue()) + }, + headsetConnectionStatusChanged: { _ in }, + availableOutputsChanged: { _, _ in } + )) } - audioSessionType = isAmbient ? .ambient : .play(mixWithOthers: false) + } else { + if let audioSessionDisposable = self.audioSessionDisposable { + self.audioSessionDisposable = nil + audioSessionDisposable.dispose() + } + + self.hasAudioSession = false } - self.audioContext.update(type: audioSessionType) - if self.isSoundEnabled && self.hasSound && self.audioContext.hasAudioSession { + if self.isSoundEnabled && self.hasSound && self.hasAudioSession { if self.audioRenderer == nil { let audioRenderer = AVSampleBufferAudioRenderer() audioRenderer.isMuted = self.isMuted @@ -875,9 +799,13 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer { } public func continueWithOverridingAmbientMode(isAmbient: Bool) { - if self.audioContext.isAmbientMode != isAmbient { - self.initialIsAmbient = isAmbient - self.audioContext.setIsAmbient(isAmbient: isAmbient) + if self.isAmbientMode != isAmbient { + self.isAmbientMode = isAmbient + + self.hasAudioSession = false + self.updateInternalState() + self.audioSessionDisposable?.dispose() + self.audioSessionDisposable = nil let currentTimestamp: CMTime if let pendingSeekTimestamp = self.pendingSeekTimestamp { diff --git a/submodules/TelegramCallsUI/Sources/Components/LivestreamVideoViewV1.swift b/submodules/TelegramCallsUI/Sources/Components/LivestreamVideoViewV1.swift index 030ecff2b5..e9d61c78a6 100644 --- a/submodules/TelegramCallsUI/Sources/Components/LivestreamVideoViewV1.swift +++ b/submodules/TelegramCallsUI/Sources/Components/LivestreamVideoViewV1.swift @@ -70,7 +70,7 @@ final class LivestreamVideoViewV1: UIView { var onSeeked: (() -> Void)? self.player = ChunkMediaPlayerV2( params: ChunkMediaPlayerV2.MediaDataReaderParams(context: context), - audioContext: ChunkMediaPlayerV2.AudioContext(audioSessionManager: audioSessionManager), + audioSessionManager: audioSessionManager, source: .externalParts(self.chunkPlayerPartsState.get()), video: true, enableSound: true, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index 725a27a253..b4e4c11784 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -6,7 +6,6 @@ import SwiftSignalKit import TelegramCore import Postbox import TelegramPresentationData -import UniversalMediaPlayer public final class StoryContentItem: Equatable { public final class ExternalState { @@ -33,7 +32,6 @@ public final class StoryContentItem: Equatable { public final class SharedState { public var replyDrafts: [StoryId: NSAttributedString] = [:] public var baseRate: Double = 1.0 - public var audioContext: ChunkMediaPlayerV2.AudioContext? public init() { } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index e50d333e51..f7adeea7cd 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -15,275 +15,6 @@ import ButtonComponent import MultilineTextComponent import TelegramPresentationData -private protocol StoryVideoView: UIView { - var audioMode: StoryContentItem.AudioMode { get set } - var playbackCompleted: (() -> Void)? { get set } - var status: Signal { get } - - func play() - func pause() - func seek(timestamp: Double) - func setSoundMuted(soundMuted: Bool) - func continueWithOverridingAmbientMode(isAmbient: Bool) - func setBaseRate(baseRate: Double) - func update(size: CGSize, transition: ComponentTransition) -} - -private final class LegacyStoryVideoView: UIView, StoryVideoView { - private let videoNode: UniversalVideoNode - - var audioMode: StoryContentItem.AudioMode - var playbackCompleted: (() -> Void)? - - var status: Signal { - return self.videoNode.status - } - - init( - context: AccountContext, - file: FileMediaReference, - audioMode: StoryContentItem.AudioMode, - baseRate: Double, - isCaptureProtected: Bool - ) { - self.audioMode = audioMode - - var userLocation: MediaResourceUserLocation = .other - switch file { - case let .story(peer, _, _): - userLocation = .peer(peer.id) - default: - break - } - var hasSentFramesToDisplay: (() -> Void)? - self.videoNode = UniversalVideoNode( - context: context, - postbox: context.account.postbox, - audioSession: context.sharedContext.mediaManager.audioSession, - manager: context.sharedContext.mediaManager.universalVideoManager, - decoration: StoryVideoDecoration(), - content: NativeVideoContent( - id: .contextResult(0, "\(UInt64.random(in: 0 ... UInt64.max))"), - userLocation: userLocation, - fileReference: file, - imageReference: nil, - streamVideo: .story, - loopVideo: true, - enableSound: true, - soundMuted: audioMode == .off, - beginWithAmbientSound: audioMode == .ambient, - mixWithOthers: true, - useLargeThumbnail: false, - autoFetchFullSizeThumbnail: false, - tempFilePath: nil, - captureProtected: isCaptureProtected, - hintDimensions: file.media.dimensions?.cgSize, - storeAfterDownload: nil, - displayImage: false, - hasSentFramesToDisplay: { - hasSentFramesToDisplay?() - } - ), - priority: .gallery - ) - self.videoNode.isHidden = true - self.videoNode.setBaseRate(baseRate) - - super.init(frame: CGRect()) - - hasSentFramesToDisplay = { [weak self] in - guard let self else { - return - } - self.videoNode.isHidden = false - } - - self.videoNode.playbackCompleted = { [weak self] in - guard let self else { - return - } - self.playbackCompleted?() - } - - self.addSubview(self.videoNode.view) - - self.videoNode.ownsContentNodeUpdated = { [weak self] value in - guard let self else { - return - } - if value { - self.videoNode.seek(0.0) - if self.audioMode != .off { - self.videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop) - } else { - self.videoNode.play() - } - } - } - self.videoNode.canAttachContent = true - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func play() { - self.videoNode.play() - } - - func pause() { - self.videoNode.pause() - } - - func seek(timestamp: Double) { - self.videoNode.seek(timestamp) - } - - func setSoundMuted(soundMuted: Bool) { - self.videoNode.setSoundMuted(soundMuted: soundMuted) - } - - func continueWithOverridingAmbientMode(isAmbient: Bool) { - self.videoNode.continueWithOverridingAmbientMode(isAmbient: isAmbient) - } - - func setBaseRate(baseRate: Double) { - self.videoNode.setBaseRate(baseRate) - } - - func update(size: CGSize, transition: ComponentTransition) { - transition.setFrame(view: self.videoNode.view, frame: CGRect(origin: CGPoint(), size: size)) - self.videoNode.updateLayout(size: size, transition: transition.containedViewLayoutTransition) - } -} - -private final class ModernStoryVideoView: UIView, StoryVideoView { - private let player: ChunkMediaPlayerV2 - private let playerNode: MediaPlayerNode - - var audioMode: StoryContentItem.AudioMode - var playbackCompleted: (() -> Void)? - var isFirstPlay: Bool = true - - var status: Signal { - return self.player.status |> map(Optional.init) - } - - init( - context: AccountContext, - audioContext: ChunkMediaPlayerV2.AudioContext, - file: FileMediaReference, - audioMode: StoryContentItem.AudioMode, - baseRate: Double, - isCaptureProtected: Bool - ) { - self.audioMode = audioMode - - self.playerNode = MediaPlayerNode( - backgroundThread: false, - captureProtected: isCaptureProtected - ) - - var userLocation: MediaResourceUserLocation = .other - switch file { - case let .story(peer, _, _): - userLocation = .peer(peer.id) - default: - break - } - - self.player = ChunkMediaPlayerV2( - params: ChunkMediaPlayerV2.MediaDataReaderParams(context: context), - audioContext: audioContext, - source: .directFetch(ChunkMediaPlayerV2.SourceDescription.ResourceDescription( - postbox: context.account.postbox, - size: file.media.size ?? 0, - reference: file.resourceReference(file.media.resource), - userLocation: userLocation, - userContentType: .story, - statsCategory: statsCategoryForFileWithAttributes(file.media.attributes), - fetchAutomatically: false - )), - video: true, - playAutomatically: false, - enableSound: true, - baseRate: baseRate, - soundMuted: audioMode == .off, - ambient: audioMode == .ambient, - mixWithOthers: true, - continuePlayingWithoutSoundOnLostAudioSession: false, - isAudioVideoMessage: false, - playerNode: self.playerNode - ) - self.playerNode.isHidden = true - self.player.setBaseRate(baseRate) - - super.init(frame: CGRect()) - - self.addSubview(self.playerNode.view) - - self.playerNode.hasSentFramesToDisplay = { [weak self] in - guard let self else { - return - } - self.playerNode.isHidden = false - } - - self.player.actionAtEnd = .action({ [weak self] in - guard let self else { - return - } - self.playbackCompleted?() - }) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func play() { - if self.isFirstPlay { - self.isFirstPlay = false - - if self.audioMode != .off { - self.player.playOnceWithSound(playAndRecord: false, seek: .start) - } else { - self.player.play() - } - } else { - self.player.play() - } - } - - func pause() { - self.player.pause() - } - - func seek(timestamp: Double) { - self.player.seek(timestamp: timestamp, play: nil) - } - - func setSoundMuted(soundMuted: Bool) { - self.player.setSoundMuted(soundMuted: soundMuted) - } - - func continueWithOverridingAmbientMode(isAmbient: Bool) { - self.player.continueWithOverridingAmbientMode(isAmbient: isAmbient) - } - - func setBaseRate(baseRate: Double) { - self.player.setBaseRate(baseRate) - } - - func update(size: CGSize, transition: ComponentTransition) { - transition.containedViewLayoutTransition.updateFrame(node: self.playerNode, frame: CGRect(origin: CGPoint(), size: size)) - } - - func updateNext(nextVideoView: ModernStoryVideoView?) { - self.player.migrateToNextPlayerOnEnd = nextVideoView?.player - } -} - final class StoryItemContentComponent: Component { typealias EnvironmentType = StoryContentItem.Environment @@ -360,11 +91,10 @@ final class StoryItemContentComponent: Component { final class View: StoryContentItem.View { private let imageView: StoryItemImageView private let overlaysView: StoryItemOverlaysView + private var videoNode: UniversalVideoNode? private var loadingEffectView: StoryItemLoadingEffectView? private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer? - private var videoView: StoryVideoView? - private var mediaAreasEffectView: StoryItemLoadingEffectView? private var currentMessageMedia: EngineMedia? @@ -399,8 +129,6 @@ final class StoryItemContentComponent: Component { private var fetchPriorityResourceId: String? private var currentFetchPriority: (isMain: Bool, disposable: Disposable)? - private weak var nextItemView: StoryItemContentComponent.View? - override init(frame: CGRect) { self.hierarchyTrackingLayer = HierarchyTrackingLayer() self.imageView = StoryItemImageView() @@ -458,7 +186,10 @@ final class StoryItemContentComponent: Component { } private func initializeVideoIfReady(update: Bool) { - if self.videoView != nil { + if self.videoNode != nil { + return + } + if case .pause = self.progressMode { return } @@ -466,49 +197,48 @@ final class StoryItemContentComponent: Component { return } - var useLegacyImplementation = false - if let data = component.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_video_legacystoryplayer"] as? Double { - useLegacyImplementation = value != 0.0 - } - - if case .pause = self.progressMode { - if useLegacyImplementation { - return - } - } - if case let .file(file) = currentMessageMedia, let peerReference = PeerReference(component.peer._asPeer()) { - if self.videoView == nil { - let videoView: StoryVideoView - if useLegacyImplementation { - videoView = LegacyStoryVideoView( - context: component.context, - file: .story(peer: peerReference, id: component.item.id, media: file), - audioMode: component.audioMode, - baseRate: component.baseRate, - isCaptureProtected: component.item.isForwardingDisabled - ) - } else { - let audioContext: ChunkMediaPlayerV2.AudioContext - if let current = self.environment?.sharedState.audioContext { - audioContext = current - } else { - audioContext = ChunkMediaPlayerV2.AudioContext(audioSessionManager: component.context.sharedContext.mediaManager.audioSession) - self.environment?.sharedState.audioContext = audioContext - } - videoView = ModernStoryVideoView( - context: component.context, - audioContext: audioContext, - file: .story(peer: peerReference, id: component.item.id, media: file), - audioMode: component.audioMode, - baseRate: component.baseRate, - isCaptureProtected: component.item.isForwardingDisabled - ) - } - self.videoView = videoView - self.insertSubview(videoView, aboveSubview: self.imageView) + if self.videoNode == nil { + let videoNode = UniversalVideoNode( + context: component.context, + postbox: component.context.account.postbox, + audioSession: component.context.sharedContext.mediaManager.audioSession, + manager: component.context.sharedContext.mediaManager.universalVideoManager, + decoration: StoryVideoDecoration(), + content: NativeVideoContent( + id: .contextResult(0, "\(UInt64.random(in: 0 ... UInt64.max))"), + userLocation: .peer(peerReference.id), + fileReference: .story(peer: peerReference, id: component.item.id, media: file), + imageReference: nil, + streamVideo: .story, + loopVideo: true, + enableSound: true, + soundMuted: component.audioMode == .off, + beginWithAmbientSound: component.audioMode == .ambient, + mixWithOthers: true, + useLargeThumbnail: false, + autoFetchFullSizeThumbnail: false, + tempFilePath: nil, + captureProtected: component.item.isForwardingDisabled, + hintDimensions: file.dimensions?.cgSize, + storeAfterDownload: nil, + displayImage: false, + hasSentFramesToDisplay: { [weak self] in + guard let self else { + return + } + self.videoNode?.isHidden = false + } + ), + priority: .gallery + ) + videoNode.isHidden = true + videoNode.setBaseRate(component.baseRate) - videoView.playbackCompleted = { [weak self] in + self.videoNode = videoNode + self.insertSubview(videoNode.view, aboveSubview: self.imageView) + + videoNode.playbackCompleted = { [weak self] in guard let self else { return } @@ -523,24 +253,38 @@ final class StoryItemContentComponent: Component { if shouldLoop { self.rewind() - if let videoView = self.videoView { + if let videoNode = self.videoNode { if self.contentLoaded { - videoView.play() + videoNode.play() } } } else { self.environment?.presentationProgressUpdated(1.0, false, true) } } + videoNode.ownsContentNodeUpdated = { [weak self] value in + guard let self, let component = self.component else { + return + } + if value { + self.videoNode?.seek(0.0) + if component.audioMode != .off { + self.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop) + } else { + self.videoNode?.play() + } + } + } + videoNode.canAttachContent = true if update { self.state?.updated(transition: .immediate) } } } - if let videoView = self.videoView { + if let videoNode = self.videoNode { if self.videoProgressDisposable == nil { - self.videoProgressDisposable = (videoView.status + self.videoProgressDisposable = (videoNode.status |> deliverOnMainQueue).start(next: { [weak self] status in guard let self, let status else { return @@ -552,17 +296,7 @@ final class StoryItemContentComponent: Component { } }) } - - let canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy - - if canPlay { - videoView.play() - } else { - videoView.pause() - } } - - self.updateVideoNextItem() } override func setProgressMode(_ progressMode: StoryContentItem.ProgressMode) { @@ -576,62 +310,48 @@ final class StoryItemContentComponent: Component { } } - func setNextItemView(nextItemView: StoryItemContentComponent.View?) { - if self.nextItemView !== nextItemView { - self.nextItemView = nextItemView - self.updateVideoNextItem() - } - } - - private func updateVideoNextItem() { - if let videoView = self.videoView as? ModernStoryVideoView { - let nextVideoView = self.nextItemView?.videoView as? ModernStoryVideoView - videoView.updateNext(nextVideoView: nextVideoView) - } - } - override func rewind() { self.currentProgressTimerValue = 0.0 - if let videoView = self.videoView { + if let videoNode = self.videoNode { if self.contentLoaded { - videoView.seek(timestamp: 0.0) + videoNode.seek(0.0) } } } override func leaveAmbientMode() { - if let videoView = self.videoView { + if let videoNode = self.videoNode { self.ignoreBufferingTimestamp = CFAbsoluteTimeGetCurrent() - videoView.setSoundMuted(soundMuted: false) - videoView.continueWithOverridingAmbientMode(isAmbient: false) + videoNode.setSoundMuted(soundMuted: false) + videoNode.continueWithOverridingAmbientMode(isAmbient: false) } } override func enterAmbientMode(ambient: Bool) { - if let videoView = self.videoView { + if let videoNode = self.videoNode { self.ignoreBufferingTimestamp = CFAbsoluteTimeGetCurrent() if ambient { - videoView.continueWithOverridingAmbientMode(isAmbient: true) + videoNode.continueWithOverridingAmbientMode(isAmbient: true) } else { - videoView.setSoundMuted(soundMuted: true) + videoNode.setSoundMuted(soundMuted: true) } } } override func setBaseRate(_ baseRate: Double) { - if let videoView = self.videoView { - videoView.setBaseRate(baseRate: baseRate) + if let videoNode = self.videoNode { + videoNode.setBaseRate(baseRate) } } private func updateProgressMode(update: Bool) { - if let videoView = self.videoView { + if let videoNode = self.videoNode { let canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy if canPlay { - videoView.play() + videoNode.play() } else { - videoView.pause() + videoNode.pause() } } @@ -846,11 +566,11 @@ final class StoryItemContentComponent: Component { private var isSeeking = false func seekTo(_ timestamp: Double, apply: Bool) { - guard let videoView = self.videoView else { + guard let videoNode = self.videoNode else { return } if apply { - videoView.seek(timestamp: min(timestamp, self.effectiveDuration - 0.3)) + videoNode.seek(min(timestamp, self.effectiveDuration - 0.3)) } self.isSeeking = true self.updateVideoPlaybackProgress(timestamp) @@ -868,10 +588,6 @@ final class StoryItemContentComponent: Component { let environment = environment[StoryContentItem.Environment.self].value self.environment = environment - if let videoView = self.videoView { - videoView.audioMode = component.audioMode - } - var synchronousLoad = false if let hint = transition.userData(Hint.self) { synchronousLoad = hint.synchronousLoad @@ -916,12 +632,12 @@ final class StoryItemContentComponent: Component { self.currentMessageMedia = messageMedia reloadMedia = true - if let videoView = self.videoView { + if let videoNode = self.videoNode { self.videoProgressDisposable?.dispose() self.videoProgressDisposable = nil - self.videoView = nil - videoView.removeFromSuperview() + self.videoNode = nil + videoNode.view.removeFromSuperview() } } self.currentMessageMetadataMedia = component.item.media @@ -1051,10 +767,10 @@ final class StoryItemContentComponent: Component { } let _ = imageSize - if let videoView = self.videoView { + if let videoNode = self.videoNode { let videoSize = dimensions.aspectFilled(availableSize) - videoView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) * 0.5), y: floor((availableSize.height - videoSize.height) * 0.5)), size: videoSize) - videoView.update(size: videoSize, transition: .immediate) + videoNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) * 0.5), y: floor((availableSize.height - videoSize.height) * 0.5)), size: videoSize) + videoNode.updateLayout(size: videoSize, transition: .immediate) } } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 27b69c1316..856df6d79b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1478,7 +1478,7 @@ public final class StoryItemSetContainerComponent: Component { } if itemLayout.contentScaleFraction <= 0.0001 && !self.preparingToDisplayViewList { - if index != centralIndex && index != centralIndex + 1 { + if index != centralIndex { itemVisible = false } } @@ -1870,19 +1870,6 @@ public final class StoryItemSetContainerComponent: Component { } } - for i in 0 ..< component.slice.allItems.count { - guard let visibleItem = self.visibleItems[component.slice.allItems[i].id] else { - continue - } - var nextVisibleItem: VisibleItem? - if i != component.slice.allItems.count { - nextVisibleItem = self.visibleItems[component.slice.allItems[i + 1].id] - } - if let itemView = visibleItem.view.view as? StoryItemContentComponent.View { - itemView.setNextItemView(nextItemView: nextVisibleItem?.view.view as? StoryItemContentComponent.View) - } - } - self.trulyValidIds = trulyValidIds var removeIds: [StoryId] = [] diff --git a/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift b/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift index e4dca05f71..2115c94488 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift @@ -1093,7 +1093,7 @@ final class HLSVideoJSNativeContentNode: ASDisplayNode, UniversalVideoContentNod var onSeeked: (() -> Void)? self.player = ChunkMediaPlayerV2( params: ChunkMediaPlayerV2.MediaDataReaderParams(context: context), - audioContext: ChunkMediaPlayerV2.AudioContext(audioSessionManager: audioSessionManager), + audioSessionManager: audioSessionManager, source: .externalParts(self.chunkPlayerPartsState.get()), video: true, enableSound: self.enableSound, diff --git a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift index afb8dadbba..e8b67bd497 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift @@ -520,7 +520,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent } else { let mediaPlayer = ChunkMediaPlayerV2( params: ChunkMediaPlayerV2.MediaDataReaderParams(context: context), - audioContext: ChunkMediaPlayerV2.AudioContext(audioSessionManager: audioSessionManager), + audioSessionManager: audioSessionManager, source: .directFetch(ChunkMediaPlayerV2.SourceDescription.ResourceDescription( postbox: postbox, size: selectedFile.size ?? 0, From 995fea2943d2250532d472915fb960660147a32c Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 5 May 2025 22:30:03 +0200 Subject: [PATCH 05/22] Cleanup for release --- .../Sources/ChatChannelSubscriberInputPanelNode.swift | 8 +------- .../Sources/Chat/ChatControllerLoadDisplayNode.swift | 5 +++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index 993fab330b..5caccf8eb1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -469,13 +469,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { self.giftButton.isHidden = false self.helpButton.isHidden = true - //TODO:release - self.suggestedPostButton.isHidden = false - self.presentGiftOrSuggestTooltip() - } else if case .broadcast = peer.info { - self.giftButton.isHidden = true - self.helpButton.isHidden = true - self.suggestedPostButton.isHidden = false + self.suggestedPostButton.isHidden = true self.presentGiftOrSuggestTooltip() } else if peer.flags.contains(.isGigagroup), self.action == .muteNotifications || self.action == .unmuteNotifications { self.giftButton.isHidden = true diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 3ca76e4c6f..010ccc7f6e 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4131,7 +4131,8 @@ extension ChatControllerImpl { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) } }, openSuggestPost: { [weak self] in - guard let self else { + let _ = self + /*guard let self else { return } guard let peerId = self.chatLocation.peerId else { @@ -4152,7 +4153,7 @@ extension ChatControllerImpl { ) chatController.navigationPresentation = .modal - self.push(chatController) + self.push(chatController)*/ }, editMessageMedia: { [weak self] messageId, draw in if let strongSelf = self { strongSelf.controllerInteraction?.editMessageMedia(messageId, draw) From 9db5eb726d44a6ab6f79f37271961899385ffa82 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 5 May 2025 22:31:26 +0200 Subject: [PATCH 06/22] Change defaults --- submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift | 2 +- submodules/TelegramCallsUI/Sources/VideoChatScreen.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift index db19e00c36..e2bbc23abb 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift @@ -167,7 +167,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP self.conferenceAddParticipant?() } - var enableVideoSharpening = true + var enableVideoSharpening = false if let data = call.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_call_video_sharpening"] as? Double { enableVideoSharpening = value != 0.0 } diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift index d65a9c0fdd..b18aa64aa1 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift @@ -1247,7 +1247,7 @@ final class VideoChatScreenComponent: Component { } self.callState = component.initialData.callState - self.enableVideoSharpening = true + self.enableVideoSharpening = false if let data = component.initialCall.accountContext.currentAppConfiguration.with({ $0 }).data, let value = data["ios_call_video_sharpening"] as? Double { self.enableVideoSharpening = value != 0.0 } From 3865e253ffa0e4dba49d14301f6e3b7003ab6b6f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 02:09:44 +0400 Subject: [PATCH 07/22] Various fixes --- .../Sources/ChatMessageItemCommon.swift | 4 --- .../Sources/GiftItemComponent.swift | 20 +++++++----- .../Sources/GiftOptionsScreen.swift | 6 ++-- .../Sources/GiftStoreScreen.swift | 2 +- .../Sources/GiftViewScreen.swift | 32 +++++++++++++------ .../Sources/PeerInfoGiftsPaneNode.swift | 2 +- .../Sources/TabSelectorComponent.swift | 16 ++++++++-- 7 files changed, 52 insertions(+), 30 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift index 38c0ff482f..f749c3c46a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift @@ -295,10 +295,6 @@ public func canAddMessageReactions(message: Message) -> Bool { return true } } - } else if let story = media as? TelegramMediaStory { - if story.isMention { - return false - } } } return true diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index 001fa6f4fe..2a0b88458e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -559,7 +559,7 @@ public final class GiftItemComponent: Component { let price: String switch component.subject { case let .premium(_, priceValue), let .starGift(_, priceValue): - if priceValue.containsEmoji { + if priceValue.contains("#") { buttonColor = component.theme.overallDarkAppearance ? UIColor(rgb: 0xffc337) : UIColor(rgb: 0xd3720a) if !component.isSoldOut { starsColor = UIColor(rgb: 0xffbe27) @@ -867,10 +867,12 @@ public final class GiftItemComponent: Component { } ) let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat - let labelText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("#\(presentationStringsFormattedNumber(Int32(resellPrice), dateTimeFormat.groupingSeparator))", attributes: attributes)) - if let range = labelText.string.range(of: "#") { - labelText.addAttribute(NSAttributedString.Key.font, value: Font.semibold(10.0), range: NSRange(range, in: labelText.string)) - labelText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: NSRange(range, in: labelText.string)) + let labelText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("# \(presentationStringsFormattedNumber(Int32(resellPrice), dateTimeFormat.groupingSeparator))", attributes: attributes)) + let range = (labelText.string as NSString).range(of: "#") + if range.location != NSNotFound { + labelText.addAttribute(NSAttributedString.Key.font, value: Font.semibold(10.0), range: range) + labelText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: range) + labelText.addAttribute(.kern, value: -2.0, range: NSRange(location: range.lowerBound, length: 1)) } let resellSize = self.reselLabel.update( @@ -1048,11 +1050,13 @@ private final class ButtonContentComponent: Component { self.componentState = state let attributedText = NSMutableAttributedString(string: component.text, font: Font.semibold(11.0), textColor: component.color) - let range = (attributedText.string as NSString).range(of: "⭐️") + let range = (attributedText.string as NSString).range(of: "#") if range.location != NSNotFound { attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: component.tinted)), range: range) - attributedText.addAttribute(.font, value: Font.semibold(15.0), range: range) - attributedText.addAttribute(.baselineOffset, value: 2.0, range: NSRange(location: range.upperBound, length: attributedText.length - range.upperBound)) + attributedText.addAttribute(.font, value: Font.semibold(component.tinted ? 14.0 : 15.0), range: range) + attributedText.addAttribute(.baselineOffset, value: -3.0, range: range) + attributedText.addAttribute(.baselineOffset, value: 1.5, range: NSRange(location: range.upperBound + 1, length: attributedText.length - range.upperBound - 1)) + attributedText.addAttribute(.kern, value: -1.5, range: NSRange(location: range.upperBound, length: 1)) } let titleSize = self.title.update( diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index 5b79de4903..08eac311a9 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -412,12 +412,12 @@ final class GiftOptionsScreenComponent: Component { if let availability = gift.availability, availability.remains == 0, let minResaleStars = availability.minResaleStars { let priceString = presentationStringsFormattedNumber(Int32(minResaleStars), environment.dateTimeFormat.groupingSeparator) if let resaleConfiguration = self.resaleConfiguration, minResaleStars == resaleConfiguration.starGiftResaleMaxAmount || availability.resale == 1 { - subject = .starGift(gift: gift, price: "⭐️ \(priceString)") + subject = .starGift(gift: gift, price: "# \(priceString)") } else { - subject = .starGift(gift: gift, price: "⭐️ \(priceString)+") + subject = .starGift(gift: gift, price: "# \(priceString)+") } } else { - subject = .starGift(gift: gift, price: "⭐️ \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))") + subject = .starGift(gift: gift, price: "# \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))") } case let .unique(gift): subject = .uniqueGift(gift: gift, price: nil) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index ae889947fc..6827a62fbf 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -230,7 +230,7 @@ final class GiftStoreScreenComponent: Component { color: ribbonColor ) - let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "⭐️\(presentationStringsFormattedNumber(Int32(uniqueGift.resellStars ?? 0), environment.dateTimeFormat.groupingSeparator))") + let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "# \(presentationStringsFormattedNumber(Int32(uniqueGift.resellStars ?? 0), environment.dateTimeFormat.groupingSeparator))") let _ = visibleItem.update( transition: itemTransition, component: AnyComponent( diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 3d8134dfbc..5cc4395dcb 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -309,17 +309,29 @@ private final class GiftViewSheetContent: CombinedComponent { let context = self.context let action = { if gifts { - if let profileController = context.sharedContext.makePeerInfoController( - context: context, - updatedPresentationData: nil, - peer: peer._asPeer(), - mode: peer.id == context.account.peerId ? .myProfileGifts : .gifts, - avatarInitiallyExpanded: false, - fromChat: false, - requestsContext: nil - ) { - navigationController.pushViewController(profileController) + let profileGifts = ProfileGiftsContext(account: context.account, peerId: peer.id) + let _ = (profileGifts.state + |> filter { state in + if case .ready = state.dataState { + return true + } + return false } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak navigationController] _ in + if let profileController = context.sharedContext.makePeerInfoController( + context: context, + updatedPresentationData: nil, + peer: peer._asPeer(), + mode: peer.id == context.account.peerId ? .myProfileGifts : .gifts, + avatarInitiallyExpanded: false, + fromChat: false, + requestsContext: nil + ) { + navigationController?.pushViewController(profileController) + } + let _ = profileGifts + }) } else { context.sharedContext.navigateToChatController(NavigateToChatControllerParams( navigationController: navigationController, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 2a88b1ced1..51dd286db5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -488,7 +488,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr switch product.gift { case let .generic(gift): - subject = .starGift(gift: gift, price: "⭐️ \(gift.price)") + subject = .starGift(gift: gift, price: "# \(gift.price)") peer = product.fromPeer.flatMap { .peer($0) } ?? .anonymous if let availability = gift.availability { diff --git a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift index f107fd6366..efce5db5c6 100644 --- a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift +++ b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift @@ -117,6 +117,8 @@ public final class TabSelectorComponent: Component { private let selectionView: UIImageView private var visibleItems: [AnyHashable: VisibleItem] = [:] + private var didInitiallyScroll = false + override init(frame: CGRect) { self.selectionView = UIImageView() @@ -238,11 +240,15 @@ public final class TabSelectorComponent: Component { )), effectAlignment: .center, minSize: nil, - action: { [weak self] in + action: { [weak self, weak itemView] in guard let self, let component = self.component else { return } component.setSelectedId(itemId) + + if let view = itemView?.title.view, allowScroll && self.contentSize.width > self.bounds.width { + self.scrollRectToVisible(view.frame.insetBy(dx: -64.0, dy: 0.0), animated: true) + } }, animateScale: !isLineSelection )), @@ -336,11 +342,15 @@ public final class TabSelectorComponent: Component { self.selectionView.alpha = 0.0 } - self.contentSize = CGSize(width: contentWidth, height: baseHeight + verticalInset * 2.0) + let contentSize = CGSize(width: contentWidth, height: baseHeight + verticalInset * 2.0) + if self.contentSize != contentSize { + self.contentSize = contentSize + } self.disablesInteractiveTransitionGestureRecognizer = contentWidth > availableSize.width - if let selectedBackgroundRect, self.bounds.width > 0.0 { + if let selectedBackgroundRect, self.bounds.width > 0.0 && !self.didInitiallyScroll { self.scrollRectToVisible(selectedBackgroundRect.insetBy(dx: -spacing, dy: 0.0), animated: false) + self.didInitiallyScroll = true } return CGSize(width: min(contentWidth, availableSize.width), height: baseHeight + verticalInset * 2.0) From 7afa30f3d898d283693308aea1285b6d0c6fae46 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 03:43:48 +0400 Subject: [PATCH 08/22] Various fixes --- .../Sources/GiftItemComponent.swift | 2 +- .../Sources/MediaEditorValues.swift | 110 +++++++++++++++++- .../Sources/MediaEditorVideoExport.swift | 19 ++- .../WebUI/Sources/WebAppController.swift | 20 +++- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index 2a0b88458e..ae3d159ef2 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -872,7 +872,7 @@ public final class GiftItemComponent: Component { if range.location != NSNotFound { labelText.addAttribute(NSAttributedString.Key.font, value: Font.semibold(10.0), range: range) labelText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: range) - labelText.addAttribute(.kern, value: -2.0, range: NSRange(location: range.lowerBound, length: 1)) + labelText.addAttribute(.kern, value: -1.5, range: NSRange(location: range.upperBound, length: 1)) } let resellSize = self.reselLabel.update( diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index 47b4d4c792..373483fe9b 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -230,7 +230,7 @@ public enum MediaCropOrientation: Int32 { } } -public final class MediaEditorValues: Codable, Equatable { +public final class MediaEditorValues: Codable, Equatable, CustomStringConvertible { public static func == (lhs: MediaEditorValues, rhs: MediaEditorValues) -> Bool { if lhs.peerId != rhs.peerId { return false @@ -1010,6 +1010,114 @@ public final class MediaEditorValues: Codable, Equatable { } return false } + + public var description: String { + var components: [String] = [] + + components.append("originalDimensions: \(self.originalDimensions.width)x\(self.originalDimensions.height)") + + if self.cropOffset != .zero { + components.append("cropOffset: \(cropOffset)") + } + + if let cropRect = self.cropRect { + components.append("cropRect: \(cropRect)") + } + + if self.cropScale != 1.0 { + components.append("cropScale: \(self.cropScale)") + } + + if self.cropRotation != 0.0 { + components.append("cropRotation: \(self.cropRotation)") + } + + if self.cropMirroring { + components.append("cropMirroring: true") + } + + if let cropOrientation = self.cropOrientation { + components.append("cropOrientation: \(cropOrientation)") + } + + if let gradientColors = self.gradientColors, !gradientColors.isEmpty { + components.append("gradientColors: \(gradientColors.count) colors") + } + + if let videoTrimRange = self.videoTrimRange { + components.append("videoTrimRange: \(videoTrimRange.lowerBound) - \(videoTrimRange.upperBound)") + } + + if self.videoIsMuted { + components.append("videoIsMuted: true") + } + + if self.videoIsFullHd { + components.append("videoIsFullHd: true") + } + + if self.videoIsMirrored { + components.append("videoIsMirrored: true") + } + + if let videoVolume = self.videoVolume, videoVolume != 1.0 { + components.append("videoVolume: \(videoVolume)") + } + + if let additionalVideoPath = self.additionalVideoPath { + components.append("additionalVideo: \(additionalVideoPath)") + } + + if let position = self.additionalVideoPosition { + components.append("additionalVideoPosition: \(position)") + } + + if let scale = self.additionalVideoScale { + components.append("additionalVideoScale: \(scale)") + } + + if let rotation = self.additionalVideoRotation { + components.append("additionalVideoRotation: \(rotation)") + } + + if !self.additionalVideoPositionChanges.isEmpty { + components.append("additionalVideoPositionChanges: \(additionalVideoPositionChanges.count) changes") + } + + if !self.collage.isEmpty { + components.append("collage: \(collage.count) items") + } + + if self.nightTheme { + components.append("nightTheme: true") + } + + if self.drawing != nil { + components.append("drawing: true") + } + + if self.maskDrawing != nil { + components.append("maskDrawing: true") + } + + if !self.entities.isEmpty { + components.append("entities: \(self.entities.count) items") + } + + if !self.toolValues.isEmpty { + components.append("toolValues: \(self.toolValues.count) tools") + } + + if let audioTrack = self.audioTrack { + components.append("audioTrack: \(audioTrack.path)") + } + + if let qualityPreset = self.qualityPreset { + components.append("qualityPreset: \(qualityPreset)") + } + + return "MediaEditorValues(\(components.joined(separator: ", ")))" + } } public struct TintValue: Equatable, Codable { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index 6061765718..89bfd124cc 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -264,6 +264,11 @@ public final class MediaEditorVideoExport { self.outputPath = outputPath self.textScale = textScale + Logger.shared.log("VideoExport", "Init") + Logger.shared.log("VideoExport", "Subject: \(subject)") + Logger.shared.log("VideoExport", "Output Path: \(outputPath)") + Logger.shared.log("VideoExport", "Configuration: \(configuration)") + if FileManager.default.fileExists(atPath: outputPath) { try? FileManager.default.removeItem(atPath: outputPath) } @@ -297,6 +302,9 @@ public final class MediaEditorVideoExport { } private func setup() { + Logger.shared.log("VideoExport", "Setting up") + + var mainAsset: AVAsset? var signals: [Signal] = [] @@ -948,11 +956,6 @@ public final class MediaEditorVideoExport { return false } } - } else { -// if !writer.appendVideoBuffer(sampleBuffer) { -// writer.markVideoAsFinished() -// return false -// } } } return true @@ -983,17 +986,21 @@ public final class MediaEditorVideoExport { } private func start() { + Logger.shared.log("VideoExport", "Start") guard self.internalStatus == .idle, let writer = self.writer else { + Logger.shared.log("VideoExport", "Failed with invalid state") self.statusValue = .failed(.invalid) return } guard writer.startWriting() else { + Logger.shared.log("VideoExport", "Failed on startWriting") self.statusValue = .failed(.writing(nil)) return } if let reader = self.reader, !reader.startReading() { + Logger.shared.log("VideoExport", "Failed on startReading") self.statusValue = .failed(.reading(nil)) return } @@ -1067,6 +1074,7 @@ public final class MediaEditorVideoExport { } if cancelled { + Logger.shared.log("VideoExport", "Cancelled") try? FileManager.default.removeItem(at: outputUrl) self.internalStatus = .finished self.statusValue = .failed(.cancelled) @@ -1108,6 +1116,7 @@ public final class MediaEditorVideoExport { let exportDuration = end - self.startTimestamp print("video processing took \(exportDuration)s") if duration.seconds > 0 { + Logger.shared.log("VideoExport", "Completed with path \(self.outputPath)") Logger.shared.log("VideoExport", "Video processing took \(exportDuration / duration.seconds)") } }) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 4324257087..dda6079112 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -828,7 +828,11 @@ public final class WebAppController: ViewController, AttachmentContainable { } if let webView = self.webView { - var scrollInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0) + let inputHeight = self.validLayout?.0.inputHeight ?? 0.0 + + let intrinsicBottomInset = layout.intrinsicInsets.bottom > 40.0 ? layout.intrinsicInsets.bottom : 0.0 + + var scrollInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(inputHeight, intrinsicBottomInset), right: 0.0) var frameBottomInset: CGFloat = 0.0 if scrollInset.bottom > 40.0 { frameBottomInset = scrollInset.bottom @@ -841,12 +845,12 @@ public final class WebAppController: ViewController, AttachmentContainable { if !webView.frame.width.isZero && webView.frame != webViewFrame { self.updateWebViewWhenStable = true } - - var bottomInset = layout.intrinsicInsets.bottom + layout.additionalInsets.bottom - if let inputHeight = self.validLayout?.0.inputHeight, inputHeight > 44.0 { - bottomInset = max(bottomInset, inputHeight) + + var viewportBottomInset = max(frameBottomInset, scrollInset.bottom) + if (self.validLayout?.0.inputHeight ?? 0.0) < 44.0 { + viewportBottomInset += layout.additionalInsets.bottom } - let viewportFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: topInset), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - topInset - bottomInset))) + let viewportFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: topInset), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - topInset - viewportBottomInset))) if webView.scrollView.contentInset != scrollInset { webView.scrollView.contentInset = scrollInset @@ -1061,6 +1065,10 @@ public final class WebAppController: ViewController, AttachmentContainable { } else { self.lastExpansionTimestamp = currentTimestamp controller.requestAttachmentMenuExpansion() + + Queue.mainQueue().after(0.4) { + self.webView?.setNeedsLayout() + } } case "web_app_close": controller.dismiss() From b7f84a97b5be994c48121520e462a685df1ea95f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 04:14:21 +0400 Subject: [PATCH 09/22] Various fixes --- .../Sources/GiftOptionsScreen.swift | 3 +++ .../Sources/PeerInfoGiftsCoverComponent.swift | 16 +++++++++++++--- .../Wallet/QrIcon.imageset/Contents.json | 12 ------------ .../Wallet/QrIcon.imageset/ic_qrcode.pdf | Bin 4962 -> 0 bytes 4 files changed, 16 insertions(+), 15 deletions(-) delete mode 100644 submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json delete mode 100644 submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index 08eac311a9..643d1f7131 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -1567,6 +1567,9 @@ final class GiftOptionsScreenComponent: Component { } } } + if disallowedGifts.contains(.unique) && gift.availability?.remains == 0 { + return false + } } return true } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift index 81ba25209d..4f32eca5a3 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift @@ -184,7 +184,9 @@ public final class PeerInfoGiftsCoverComponent: Component { } } + private var scheduledAnimateIn = false public func willAnimateIn() { + self.scheduledAnimateIn = true for (_, layer) in self.iconLayers { layer.opacity = 0.0 } @@ -194,6 +196,7 @@ public final class PeerInfoGiftsCoverComponent: Component { guard let _ = self.currentSize, let component = self.component else { return } + self.scheduledAnimateIn = false for (_, layer) in self.iconLayers { layer.opacity = 1.0 @@ -319,8 +322,12 @@ public final class PeerInfoGiftsCoverComponent: Component { self.iconLayers[id] = iconLayer self.layer.addSublayer(iconLayer) - iconLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - iconLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + if self.scheduledAnimateIn { + iconLayer.opacity = 0.0 + } else { + iconLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + iconLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + } iconLayer.startAnimations(index: index) } @@ -349,7 +356,10 @@ public final class PeerInfoGiftsCoverComponent: Component { iconTransition.setPosition(layer: iconLayer, position: absolutePosition) iconLayer.updateRotation(effectiveAngle, transition: iconTransition) iconTransition.setScale(layer: iconLayer, scale: iconPosition.scale * (1.0 - itemScaleFraction)) - iconTransition.setAlpha(layer: iconLayer, alpha: 1.0 - itemScaleFraction) + + if !self.scheduledAnimateIn { + iconTransition.setAlpha(layer: iconLayer, alpha: 1.0 - itemScaleFraction) + } index += 1 } diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json deleted file mode 100644 index f8015db3bc..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "ic_qrcode.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf b/submodules/TelegramUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf deleted file mode 100644 index cf234b82239e82c027356a047d742e9c99dadf49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4962 zcmai&cT`hL*TyNKqvDkgLO_ZlB?$>d=~V;?h#=LFgwUIW-g^xo%>YVA=|w?Bklv)c z1f+>lL#WS2XZG1^&SbA={bmI9)HKAv;*#`&9mGxIO3_yC zU`H1{6bJ&kpzP`8(jB9QLAzLCfC>uqSa&xJ z(uv-i9Al)NFeSq{dC05~g6=u~`Vd&gog1P|?Jch~xJ&a`L11k>1>jStmKMkS=uOVX z;+~D8)4Q(cx=u4)gePB@?adHwfOA_z1gYnk%w5mVnQtp8H6H97O^$7#qluxu#5d>a zh}^^emECvS9fwB~Ju9U9rLtBg$1!^#;XQ`4(J7v7ZzXU6_4jKkV$V^tifNneQiHlP zi@d%0_+bf}J|!vbA;hqq_t{=#b>K(5Vb4Dx<>qShcoF}NL8Q(85( zWd8tuqu{}mYPb8qA#P{5#Z@O@Vt~V9st7YJmFi@#+0Oe6tth?O!akq|jXNK>#d0o$ zuKLJy$d4-ax$L|8TCNc%bo5fD+rdaB(UkE@@ZgiG^V!N;bvcU>?i9>d7s~quAIkY= zw^p{gzSVuWFc7hYNBqtmO@S2&)=%sz3nMKNcAIP-EQ z|5E6o`{2{)O^Wa--m=_1*1&s?uJhEp9~rd$waU=L$x%+vVWL}UXK%T2G>M3AIw^Yx zw^nDR^58Hlxy&|9nOA16q$!@dsP9@(F4FSzN|V7uzmisbBC2GjM3UU+i(y z+>EtuzQ7~=;Ql}yJla1i+YhEPc&3zbgROh`I`uwZ4W&rTy+pC|;7NEdrkG2&sS;#7 zAShSip;V&~^1+$s;3yMmze^3{^rL&x{1KJz+7&;dA z2Pg9fYi~4s$*bn`F9C?aWY~2|rtjFZ_FkYmZ@R>40Atc94Cs$G(`v4*dPuS9SjwA{ z_Dz4eJkS!jPnnGlOU|<78t~fZ(j(Ij?BUhs@7a}SWl@QoH{}^vY}b5210nZG`q*7( zsIN=At3Sjj#C+wTB7Z0He#FJxQd@F)5fLLpwcR{y(3Bn+p=3Br;X48io1#JvmSrA7?UU`PCYm7d_qE957(!P;YQM|_{<*44a(shxVLuzW{!?de^o4HS_uBchg5oIE z)&kEo6yM_4rL>L77?+kqBe}ix&I?%iQ}h|8xlOdXvs5$N`QENMoqnuk)6|0l-S2}x z#`+eHy&J3GeA9hYM0;H$DoQOfc5#dBVBecp&(b07Mrfqk1=nKu7z5wPdBuzT2&(E& z5oBC1;BUgagZ3?}#Vjt5v1^a9M4x@PwB{=i?Md~VK_b#G%gL}H9WC1IM{|Hi;f^4? zA(KJz#|c}tiN`p_?*}R<#9VnZf|q^4ZXN&b%*%;r-rCpzb@JY!?>Q|&iHgUR!n-wb z^cZKWpN2*1o%q~|xt+9Nuq^0g^rR2@%>!@wxMF}3dPtiezit?3ccA3oes~jub@6aR zW3WJ}zXeqnXLrI@;JH&49rXTFp1U!JPx=7KiBxC6~dN!2uf7C;GA7e^O216L#( z13WQZRV)}N{WHODIb?pz@za-Oe?&>>k|rdfeB#Z{Bx43js9`+q(3qQ=D*xNEv+jG{ zGMHeEA~Z@j4*tat6x`s(*QE(JRi`DtVFmRW0SfaabW#mKit`HYO>Yh5)vpT&c%8s3XG>j`1Uj)Ri; zrBwx}$>`NbrQ`yI*oYLV1~r`=kYhmacG@LKBpAY)N0(pDKi5&^xKp%nxuGW=FEpRW zX)?g^3Yv{;(3{Q_oGris^32l(KiCxb(_k8JX=RcTcP|bP?>*EZ12JbH`WJ=r9AhBr z%p9L%HYvzxQ#)i|xt)&Nm$l6%^L}hI9}9@lLOnh^o^D56dTo8MQDd-=0$)yTzcCx_ ztwY$ZWlR9E1y{M;S0@)VamuhwTC3n*TQeD3u-$K-nBfUDIFf(uduX^Jv`WZU#$}t# zIIfmUG_sQI2$()NU(=LkNLK4atwfwHhpz+4HmS&Jryp+dL^|F{?)bWC%J zaS*?}Bz>3@>eAaBDZ87|_xaH1>SH#=kPBKYk{EU3#00z9())A09M18^kUpOv4eF<-ly~fglTrkkzZx zqIvBiPo{c-Qovc!mm*-rj0sHty6Kf|MN$lPtPPsBL25TzON49E%$frZ>XhXj+vE(b4-eWC1lc6%k z#AQnBqpGYY!%W$!8gq{+S!sVzsF+Z{zenr(d_)Vx_9(y+C>pS@nks&M*68`~_`7c$WZL|Y22(ra4EAq`_% zt_?Mnd&UCfXZakG_zNjY)HCm$F64R{e=X_mJSBBh9x|PswToje$^Op+n{p^s3bPaYc!mn(YGNu7NVFVAb@Z!ukt|54({nV_`hW9IxY3B9NGp*P!D1W{t`XKHgo|#16u4dtTbfy4@ z&@+7ZfdY;Kx0pC-7io{e&$dDJLyF<<{EyJyig-wO3xK5O*eKEr91FSfgB)0@JzCzUg||5X83h(b(INS+3PQre3X1t zAKg4v>^x#znqHq}3U5>kxE>&(nIehb!q4?oq?9E&q^;on1mUUUcrQFcJVzM87j*sm z<8O5o`mIW>1|bF2MtK^uwQcedTI!kkX{cyaCMpIM_7T>pFZe1sB>BZ(@Diov>NkRq z_&8ODANjngtG!Ws?Ta{z_^0uwS*MYwC6?+Kx{NZ9W|caX!bd>% zLiR}e3H!rQ@v?YA5y2~)kcEIA&wq>hR9zqATX$gpwf&R)rxeai&L&O{PD4&Sv73bE57H3!P}APTbA{NS^}fPf~^p= z4A#({Up;AJQm!$UE88yj+P}c(!Tx0i`8eaaEY>Mjgy@>6f!K;DugN77Dr2YGPt^zQ z?>aIaEYJbnk@j=wnt{ht3(qP_#AZC3*8;9)T2xQ5Ht04Gc24i`9||3UY2>54qZ&`| zzIFG!*bp8tdUj=Lwr*epH%BAvC;Vhst7s<6WZ=@kseu9pMz$|(BVz1sLw9HExZRS( zt;BH%Z`ZsL!}n=(rYi^iH^$=^Jv*E_rryd=9Mv6OKBIjmNi(iHwJ2qglfZB%8eerg z>2}qNrLd%6#ed-Lo1HHWf zVkmVr)sBX1iLc+h){Kj1)n@h5=+W#{AJx2}?x&7d*Tx%*n$*l|;7oDA$MUSaTr7S9 zo^B<(Z>F@P=2kz_&wUn`G{0Z7`p)ecv80bsc@NQQa#w_hi}(Bt({aqXwi2RFA_ z7ZEU?(hzl+yx4VlF@D2h5A!VvfDeBfKBR{4u!uoxRcf(l%~8OWpMCInir5U9FG;W`cfMz<=P(dv4fFD_Y4ZFY`K=Z9+2M3W z#8{>I``tD7usYt|RKR9>p+&o+t; zn4Na)bYyW{o3gH{uz!!|zccg|)%P*>BV2GYd1b~V;P(Dh|4#T? z%!S5JelOOt-gUnpZ=P`q8YYjY?4(iPVdj1n^m>6NCs- zTf4TAM9_@TeAL~R=rMYAWL&yfI#sGIKDq13+}&=MqYqzI{mzvw>q!>8+ylt)$!>h? z-s4@*W|W)oi47=S7Cy?^c|K?wYnnGUITrK0P|D6v$8TmicsT3)zFy+Zo4bg*UrDms;sV@?hCwJ6#DwE4 zF3LFSgU{kke#dtv<(C7dkA`SU&fHP#_mw%aJwmU}lw^wt= zEeSQzHi?mSsuIU(`&ICEDnbe2vPP}OC(Q~?0&X8S<@;p2^GhENKRg%FeChl1-L#>y zoItO`MjKR5#}|j8l?=t})5T4)2V*-5{t0{o&fAk zpvA9{6NvuB#Q!ih7ASE8V`Ya_aq$M4fk+{eC-)CX_8^fr7$~7_XN4uf^9iDpvi%0g z;FIuwXH-SHBOP6A{>FFgPj3GQ%faBEZ(MXjqTvQWBa9o?&czuB28n~A;xM53MHLS_ zM=Ky$MgszpG`|GY_dvS2`vOVa{sZ;B-7o$68t|_z!$q|#5D;lAAq##X$op5ApavLNxb1=_51Tb^Y?bcSkr@mU`cw= z|6M>S7)%ldvn;zFvLlgc11q?WU_3#o+u?yHfCNDd6MvbKiEqDZSGg@R#V5Njk71OXv2V5AfT g1BEKk|L>5$ixBHhDvck12 Date: Tue, 6 May 2025 06:46:51 +0400 Subject: [PATCH 10/22] Fix text formatting when pasting from notes on iPad --- .../Pasteboard/Sources/Pasteboard.swift | 32 +++++++++++++------ submodules/TelegramUI/BUILD | 1 + .../Sources/ChatTextInputPanelNode.swift | 10 ++++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/submodules/Pasteboard/Sources/Pasteboard.swift b/submodules/Pasteboard/Sources/Pasteboard.swift index bbd784a466..7ffa9fc9d4 100644 --- a/submodules/Pasteboard/Sources/Pasteboard.swift +++ b/submodules/Pasteboard/Sources/Pasteboard.swift @@ -53,15 +53,29 @@ private func chatInputStateString(attributedString: NSAttributedString) -> NSAtt } if let value = attributes[.font], let font = value as? UIFont { let fontName = font.fontName.lowercased() - if fontName.contains("bolditalic") { - string.addAttribute(ChatTextInputAttributes.bold, value: true as NSNumber, range: range) - string.addAttribute(ChatTextInputAttributes.italic, value: true as NSNumber, range: range) - } else if fontName.contains("bold") { - string.addAttribute(ChatTextInputAttributes.bold, value: true as NSNumber, range: range) - } else if fontName.contains("italic") { - string.addAttribute(ChatTextInputAttributes.italic, value: true as NSNumber, range: range) - } else if fontName.contains("menlo") || fontName.contains("courier") || fontName.contains("sfmono") { - string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range) + if fontName.hasPrefix(".sfui") { + let traits = font.fontDescriptor.symbolicTraits + if traits.contains(.traitMonoSpace) { + string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range) + } else { + if traits.contains(.traitBold) { + string.addAttribute(ChatTextInputAttributes.bold, value: true as NSNumber, range: range) + } + if traits.contains(.traitItalic) { + string.addAttribute(ChatTextInputAttributes.italic, value: true as NSNumber, range: range) + } + } + } else { + if fontName.contains("bolditalic") { + string.addAttribute(ChatTextInputAttributes.bold, value: true as NSNumber, range: range) + string.addAttribute(ChatTextInputAttributes.italic, value: true as NSNumber, range: range) + } else if fontName.contains("bold") { + string.addAttribute(ChatTextInputAttributes.bold, value: true as NSNumber, range: range) + } else if fontName.contains("italic") { + string.addAttribute(ChatTextInputAttributes.italic, value: true as NSNumber, range: range) + } else if fontName.contains("menlo") || fontName.contains("courier") || fontName.contains("sfmono") { + string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range) + } } } if let value = attributes[.backgroundColor] as? UIColor, value.rgb == UIColor.gray.rgb { diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 3f0e1ae3d6..4c64d17ab7 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -70,6 +70,7 @@ swift_library( "//submodules/MediaPlayer:UniversalMediaPlayer", "//submodules/TelegramVoip:TelegramVoip", "//submodules/DeviceAccess:DeviceAccess", + "//submodules/Utils/DeviceModel", "//submodules/WatchCommon/Host:WatchCommon", "//submodules/BuildConfig:BuildConfig", "//submodules/BuildConfigExtra:BuildConfigExtra", diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 193ccfa97b..1e884bf566 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1,4 +1,5 @@ import Foundation +import UniformTypeIdentifiers import UIKit import Display import AsyncDisplayKit @@ -44,6 +45,7 @@ import TelegramNotices import AnimatedCountLabelNode import TelegramStringFormatting import TextNodeWithEntities +import DeviceModel private let accessoryButtonFont = Font.medium(14.0) private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers]) @@ -4473,10 +4475,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch var attributedString: NSAttributedString? if let data = pasteboard.data(forPasteboardType: "private.telegramtext"), let value = chatInputStateStringFromAppSpecificString(data: data) { attributedString = value - } else if let data = pasteboard.data(forPasteboardType: kUTTypeRTF as String) { + } else if let data = pasteboard.data(forPasteboardType: "public.rtf") { attributedString = chatInputStateStringFromRTF(data, type: NSAttributedString.DocumentType.rtf) } else if let data = pasteboard.data(forPasteboardType: "com.apple.flat-rtfd") { - attributedString = chatInputStateStringFromRTF(data, type: NSAttributedString.DocumentType.rtfd) + if let _ = pasteboard.data(forPasteboardType: "com.apple.notes.richtext"), DeviceModel.current.isIpad, let htmlData = pasteboard.data(forPasteboardType: "public.html") { + attributedString = chatInputStateStringFromRTF(htmlData, type: NSAttributedString.DocumentType.html) + } else { + attributedString = chatInputStateStringFromRTF(data, type: NSAttributedString.DocumentType.rtfd) + } } if let attributedString = attributedString { From e788bb7801a0384fe0bd9e1f56f108902c119907 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 15:26:36 +0400 Subject: [PATCH 11/22] Fix translation --- .../TranslateUI/Sources/ChatTranslation.swift | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/submodules/TranslateUI/Sources/ChatTranslation.swift b/submodules/TranslateUI/Sources/ChatTranslation.swift index 8c03916535..98b317cd03 100644 --- a/submodules/TranslateUI/Sources/ChatTranslation.swift +++ b/submodules/TranslateUI/Sources/ChatTranslation.swift @@ -76,10 +76,14 @@ public struct ChatTranslationState: Codable { } private func cachedChatTranslationState(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?) -> Signal { - let key = EngineDataBuffer(length: 8) - key.setInt64(0, value: peerId.id._internalGetInt64Value()) + let key: EngineDataBuffer if let threadId { + key = EngineDataBuffer(length: 16) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) key.setInt64(8, value: threadId) + } else { + key = EngineDataBuffer(length: 8) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) } return engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.translationState, id: key)) @@ -89,10 +93,14 @@ private func cachedChatTranslationState(engine: TelegramEngine, peerId: EnginePe } private func updateChatTranslationState(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?, state: ChatTranslationState?) -> Signal { - let key = EngineDataBuffer(length: 8) - key.setInt64(0, value: peerId.id._internalGetInt64Value()) + let key: EngineDataBuffer if let threadId { + key = EngineDataBuffer(length: 16) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) key.setInt64(8, value: threadId) + } else { + key = EngineDataBuffer(length: 8) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) } if let state { @@ -103,10 +111,14 @@ private func updateChatTranslationState(engine: TelegramEngine, peerId: EnginePe } public func updateChatTranslationStateInteractively(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?, _ f: @escaping (ChatTranslationState?) -> ChatTranslationState?) -> Signal { - let key = EngineDataBuffer(length: 8) - key.setInt64(0, value: peerId.id._internalGetInt64Value()) + let key: EngineDataBuffer if let threadId { + key = EngineDataBuffer(length: 16) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) key.setInt64(8, value: threadId) + } else { + key = EngineDataBuffer(length: 8) + key.setInt64(0, value: peerId.id._internalGetInt64Value()) } return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.translationState, id: key)) From fe49c781343bb09f09f2eb1e8351fbbd6e6ae334 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 15:33:10 +0400 Subject: [PATCH 12/22] Fix build --- submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m b/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m index 23a0498bef..85ddce8a9e 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m +++ b/submodules/FFMpegBinding/Sources/FFMpegLiveMuxer.m @@ -2,8 +2,8 @@ #import #include "libavutil/timestamp.h" -#include "libavformat/avformat.h">" -#include "libavcodec/avcodec.h">" +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" #include "libswresample/swresample.h" #define MOV_TIMESCALE 1000 From 59f69bea053352de803444ace47c110753c83e91 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 6 May 2025 17:00:37 +0200 Subject: [PATCH 13/22] Fix empty proxy update --- .../Sources/State/ManagedProxyInfoUpdates.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift index b745a5b107..1384758737 100644 --- a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift @@ -202,6 +202,16 @@ func managedPromoInfoUpdates(accountPeerId: PeerId, postbox: Postbox, network: N switch data { case .promoDataEmpty: transaction.replaceAdditionalChatListItems([]) + + let suggestionInfo = ServerSuggestionInfo( + legacyItems: [], + items: [], + dismissedIds: [] + ) + + transaction.updatePreferencesEntry(key: PreferencesKeys.serverSuggestionInfo(), { _ in + return PreferencesEntry(suggestionInfo) + }) case let .promoData(flags, expires, peer, psaType, psaMessage, pendingSuggestions, dismissedSuggestions, customPendingSuggestion, chats, users): let _ = expires From 80a901704cd5a811e65a16c92b0e60bad690b9f4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 6 May 2025 19:06:42 +0400 Subject: [PATCH 14/22] Fix boost perks order --- .../PremiumUI/Sources/PremiumBoostLevelsScreen.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index d9c5782795..f01f636801 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -1108,6 +1108,10 @@ private final class SheetContent: CombinedComponent { func layoutLevel(_ level: Int32) { var perks: [LevelSectionComponent.Perk] = [] + if !isGroup && level >= requiredBoostSubjectLevel(subject: .autoTranslate, group: isGroup, context: component.context, configuration: premiumConfiguration) { + perks.append(.autoTranslate) + } + perks.append(.story(level)) if !isGroup { @@ -1171,12 +1175,6 @@ private final class SheetContent: CombinedComponent { if !isGroup && level >= requiredBoostSubjectLevel(subject: .noAds, group: isGroup, context: component.context, configuration: premiumConfiguration) { perks.append(.noAds) } - if !isGroup && level >= requiredBoostSubjectLevel(subject: .autoTranslate, group: isGroup, context: component.context, configuration: premiumConfiguration) { - perks.append(.autoTranslate) - } -// if !isGroup && level >= requiredBoostSubjectLevel(subject: .wearGift, group: isGroup, context: component.context, configuration: premiumConfiguration) { -// perks.append(.wearGift) -// } levelItems.append( AnyComponentWithIdentity( From 6e00be479637dc122ef0f1cd4ce9d958b0393ea5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 May 2025 15:56:38 +0400 Subject: [PATCH 15/22] Various fixes --- .../TelegramEngine/Payments/StarGifts.swift | 2 +- .../Sources/GiftStoreScreen.swift | 24 +++++++++++++++---- .../Sources/GiftViewScreen.swift | 2 +- .../Sources/StarsWithdrawalScreen.swift | 4 ++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 7b5a1c63b5..e7ca7d8f5c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -2463,7 +2463,7 @@ private final class ResaleGiftsContextImpl { let filterAttributes = self.filterAttributes let currentAttributesHash = self.attributesHash - let dataState = self.dataState + let dataState = self.dataState if case let .ready(true, initialNextOffset) = dataState { self.dataState = .loading diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index 6827a62fbf..62c712d1f4 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -95,7 +95,8 @@ final class GiftStoreScreenComponent: Component { private var starsStateDisposable: Disposable? private var starsState: StarsContext.State? - private var initialCount: Int? + private var initialCount: Int32? + private var showLoading = true private var component: GiftStoreScreenComponent? private(set) weak var state: State? @@ -451,7 +452,7 @@ final class GiftStoreScreenComponent: Component { } let bottomContentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height) - if interactive, bottomContentOffset < 320.0 { + if interactive, bottomContentOffset < 1000.0 { self.state?.starGiftsContext.loadMore() } } @@ -471,6 +472,7 @@ final class GiftStoreScreenComponent: Component { guard let self else { return } + self.showLoading = true self.state?.starGiftsContext.updateSorting(.value) self.scrollToTop() }))) @@ -481,6 +483,7 @@ final class GiftStoreScreenComponent: Component { guard let self else { return } + self.showLoading = true self.state?.starGiftsContext.updateSorting(.date) self.scrollToTop() }))) @@ -491,6 +494,7 @@ final class GiftStoreScreenComponent: Component { guard let self else { return } + self.showLoading = true self.state?.starGiftsContext.updateSorting(.number) self.scrollToTop() }))) @@ -564,6 +568,7 @@ final class GiftStoreScreenComponent: Component { updatedFilterAttributes.append(attribute) } } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() }, @@ -577,6 +582,7 @@ final class GiftStoreScreenComponent: Component { } return true } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() } @@ -657,6 +663,7 @@ final class GiftStoreScreenComponent: Component { updatedFilterAttributes.append(attribute) } } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() }, @@ -670,6 +677,7 @@ final class GiftStoreScreenComponent: Component { } return true } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() } @@ -750,6 +758,7 @@ final class GiftStoreScreenComponent: Component { updatedFilterAttributes.append(attribute) } } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() }, @@ -763,6 +772,7 @@ final class GiftStoreScreenComponent: Component { } return true } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes) self.scrollToTop() } @@ -804,6 +814,12 @@ final class GiftStoreScreenComponent: Component { self.component = component let isLoading = self.effectiveIsLoading + if case let .ready(loadMore, nextOffset) = self.state?.starGiftsState?.dataState { + if loadMore && nextOffset == nil { + } else { + self.showLoading = false + } + } let theme = environment.theme let strings = environment.strings @@ -927,7 +943,7 @@ final class GiftStoreScreenComponent: Component { } let effectiveCount: Int32 - if let count = self.effectiveGifts?.count, count > 0 || self.initialCount != nil { + if let count = self.state?.starGiftsState?.count, count > 0 || self.initialCount != nil { if self.initialCount == nil { self.initialCount = count } @@ -1112,7 +1128,7 @@ final class GiftStoreScreenComponent: Component { self.updateScrolling(transition: transition) - if isLoading { + if isLoading && self.showLoading { self.loadingNode.update(size: availableSize, theme: environment.theme, transition: .immediate) loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 1.0) } else { diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 5cc4395dcb..c07308affd 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -958,7 +958,7 @@ private final class GiftViewSheetContent: CombinedComponent { let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in - return .ignore + return .dismiss(consume: false) }) controller.present(tooltipController, in: .current) } diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 237d089380..3f42f70678 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -279,7 +279,7 @@ private final class SheetContent: CombinedComponent { case .starGiftResell: let amountInfoString: NSAttributedString if let value = state.amount?.value, value > 0 { - let starsValue = Int32(floor(Float(value) * Float(resaleConfiguration.paidMessageCommissionPermille) / 1000.0)) + let starsValue = Int32(floor(Float(value) * Float(resaleConfiguration.starGiftCommissionPermille) / 1000.0)) let starsString = environment.strings.Stars_SellGift_AmountInfo_Stars(starsValue) amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo(starsString).string, attributes: amountMarkdownAttributes, textAlignment: .natural)) @@ -288,7 +288,7 @@ private final class SheetContent: CombinedComponent { amountRightLabel = "≈\(formatTonUsdValue(Int64(starsValue), divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))" } } else { - amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo("\(resaleConfiguration.paidMessageCommissionPermille / 10)%").string, attributes: amountMarkdownAttributes, textAlignment: .natural)) + amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo("\(resaleConfiguration.starGiftCommissionPermille / 10)%").string, attributes: amountMarkdownAttributes, textAlignment: .natural)) } amountFooter = AnyComponent(MultilineTextComponent( text: .plain(amountInfoString), From 064024514cee79965bfee184ed2957193e39e090 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 May 2025 16:01:08 +0400 Subject: [PATCH 16/22] Various fixes --- .../Sources/GiftStoreScreen.swift | 5 ++++- .../Sources/LoadingShimmerComponent.swift | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index 62c712d1f4..c2400ed3d3 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -339,6 +339,7 @@ final class GiftStoreScreenComponent: Component { guard let self else { return } + self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes([]) }, animateScale: false @@ -1063,6 +1064,7 @@ final class GiftStoreScreenComponent: Component { let loadingTransition: ComponentTransition = .easeInOut(duration: 0.25) + var showingFilters = false let filterSize = self.filterSelector.update( transition: transition, component: AnyComponent(FilterSelectorComponent( @@ -1085,6 +1087,7 @@ final class GiftStoreScreenComponent: Component { if let initialCount = self.initialCount, initialCount >= minimumCountToDisplayFilters { loadingTransition.setAlpha(view: filterSelectorView, alpha: 1.0) + showingFilters = true } } @@ -1129,7 +1132,7 @@ final class GiftStoreScreenComponent: Component { self.updateScrolling(transition: transition) if isLoading && self.showLoading { - self.loadingNode.update(size: availableSize, theme: environment.theme, transition: .immediate) + self.loadingNode.update(size: availableSize, theme: environment.theme, showFilters: !showingFilters, transition: .immediate) loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 1.0) } else { loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 0.0) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/LoadingShimmerComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/LoadingShimmerComponent.swift index 73e314820f..af253d4b3e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/LoadingShimmerComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/LoadingShimmerComponent.swift @@ -125,7 +125,7 @@ final class LoadingShimmerNode: ASDisplayNode { private let backgroundColorNode: ASDisplayNode private let effectNode: SearchShimmerEffectNode private let maskNode: ASImageNode - private var currentParams: (size: CGSize, theme: PresentationTheme)? + private var currentParams: (size: CGSize, theme: PresentationTheme, showFilters: Bool)? override init() { self.backgroundColorNode = ASDisplayNode() @@ -142,11 +142,11 @@ final class LoadingShimmerNode: ASDisplayNode { self.addSubnode(self.maskNode) } - func update(size: CGSize, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, theme: PresentationTheme, showFilters: Bool, transition: ContainedViewLayoutTransition) { let color = theme.list.itemSecondaryTextColor.mixedWith(theme.list.blocksBackgroundColor, alpha: 0.85) - if self.currentParams?.size != size || self.currentParams?.theme !== theme { - self.currentParams = (size, theme) + if self.currentParams?.size != size || self.currentParams?.theme !== theme || self.currentParams?.showFilters != showFilters { + self.currentParams = (size, theme, showFilters) self.backgroundColorNode.backgroundColor = color @@ -156,10 +156,12 @@ final class LoadingShimmerNode: ASDisplayNode { let sideInset: CGFloat = 16.0 - let filterSpacing: CGFloat = 6.0 - let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0 - for i in 0 ..< 4 { - context.addPath(CGPath(roundedRect: CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0), size: CGSize(width: filterWidth, height: 28.0)), cornerWidth: 14.0, cornerHeight: 14.0, transform: nil)) + if showFilters { + let filterSpacing: CGFloat = 6.0 + let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0 + for i in 0 ..< 4 { + context.addPath(CGPath(roundedRect: CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0), size: CGSize(width: filterWidth, height: 28.0)), cornerWidth: 14.0, cornerHeight: 14.0, transform: nil)) + } } var currentY: CGFloat = 39.0 + 7.0 From 2fc5a19f50001b6407ae573f0eca56f4b45e5260 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 May 2025 16:17:40 +0400 Subject: [PATCH 17/22] Various fixes --- .../Sources/MediaPickerScreen.swift | 58 ++++++++++--------- .../CameraScreen/Sources/CameraScreen.swift | 2 + 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index f8a7049e91..5d848b66c9 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -528,29 +528,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .cover, .createSticker, .createAvatar].contains(mode) { } else { - let selectionGesture = MediaPickerGridSelectionGesture() - selectionGesture.delegate = self.wrappedGestureRecognizerDelegate - selectionGesture.began = { [weak self] in - self?.controller?.cancelPanGesture() - } - selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in - self?.gridNode.scrollView.isScrollEnabled = isEnabled - } - selectionGesture.itemAt = { [weak self] point in - if let self, let itemNode = self.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem { - return (selectableItem, self.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false) - } else { - return nil - } - } - selectionGesture.updateSelection = { [weak self] asset, selected in - if let strongSelf = self { - strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil) - } - } - selectionGesture.sideInset = 44.0 - self.gridNode.view.addGestureRecognizer(selectionGesture) - self.selectionGesture = selectionGesture + self.setupSelectionGesture() } if let controller = self.controller, case let .assets(collection, _) = controller.subject, collection != nil { @@ -713,6 +691,35 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } } + func setupSelectionGesture() { + guard self.selectionGesture == nil else { + return + } + let selectionGesture = MediaPickerGridSelectionGesture() + selectionGesture.delegate = self.wrappedGestureRecognizerDelegate + selectionGesture.began = { [weak self] in + self?.controller?.cancelPanGesture() + } + selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in + self?.gridNode.scrollView.isScrollEnabled = isEnabled + } + selectionGesture.itemAt = { [weak self] point in + if let self, let itemNode = self.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem { + return (selectableItem, self.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false) + } else { + return nil + } + } + selectionGesture.updateSelection = { [weak self] asset, selected in + if let strongSelf = self { + strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil) + } + } + selectionGesture.sideInset = 44.0 + self.gridNode.view.addGestureRecognizer(selectionGesture) + self.selectionGesture = selectionGesture + } + @objc private func cameraTapped() { guard let camera = self.modernCamera, let previewView = self.modernCameraView else { return @@ -2352,9 +2359,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut) var moreIsVisible = false if case let .assets(_, mode) = self.subject, [.story, .createSticker].contains(mode) { - if count == 1 { - self.requestAttachmentMenuExpansion() - } moreIsVisible = true } else if case let .media(media) = self.subject { self.titleView.title = media.count == 1 ? self.presentationData.strings.Attachment_Pasteboard : self.presentationData.strings.Attachment_SelectedMedia(count) @@ -2618,6 +2622,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.navigationItem.setRightBarButton(nil, animated: true) self.explicitMultipleSelection = true + self.controllerNode.setupSelectionGesture() + self.requestAttachmentMenuExpansion() if let state = self.controllerNode.state { self.controllerNode.updateState(state) diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 5b57038105..c647baa75a 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -2553,6 +2553,8 @@ public class CameraScreenImpl: ViewController, CameraScreen { transitionCircleLayer.animateScale(from: sourceLocalFrame.width / 320.0, to: 6.0, duration: 0.6, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in self.view.mask = nil colorFillView.removeFromSuperview() + + self.requestUpdateLayout(hasAppeared: true, transition: .immediate) }) } else { if case .story = controller.mode { From 641310f4a822f388d988a55be11240b36ba2c345 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 May 2025 16:46:41 +0400 Subject: [PATCH 18/22] Various fixes --- .../Sources/GiftStoreScreen.swift | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index c2400ed3d3..470d29256e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -519,7 +519,13 @@ final class GiftStoreScreenComponent: Component { } else { return false } - } + }.sorted(by: { lhs, rhs in + if case let .model(_, lhsFile, _) = lhs, case let .model(_, rhsFile, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.model(lhsFile.fileId.id)], let rhsCount = self.state?.starGiftsState?.attributeCount[.model(rhsFile.fileId.id)] { + return lhsCount > rhsCount + } else { + return false + } + }) let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? [] let selectedModelAttributes = currentFilterAttributes.filter { attribute in @@ -614,7 +620,13 @@ final class GiftStoreScreenComponent: Component { } else { return false } - } + }.sorted(by: { lhs, rhs in + if case let .backdrop(_, lhsId, _, _, _, _, _) = lhs, case let .backdrop(_, rhsId, _, _, _, _, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.backdrop(lhsId)], let rhsCount = self.state?.starGiftsState?.attributeCount[.backdrop(rhsId)] { + return lhsCount > rhsCount + } else { + return false + } + }) let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? [] let selectedBackdropAttributes = currentFilterAttributes.filter { attribute in @@ -709,7 +721,13 @@ final class GiftStoreScreenComponent: Component { } else { return false } - } + }.sorted(by: { lhs, rhs in + if case let .pattern(_, lhsFile, _) = lhs, case let .pattern(_, rhsFile, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.pattern(lhsFile.fileId.id)], let rhsCount = self.state?.starGiftsState?.attributeCount[.pattern(rhsFile.fileId.id)] { + return lhsCount > rhsCount + } else { + return false + } + }) let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? [] let selectedPatternAttributes = currentFilterAttributes.filter { attribute in From a523f70a148ad3c8e340f4d7ec8e847d0dc60788 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 May 2025 17:14:29 +0400 Subject: [PATCH 19/22] Various fixes --- .../Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index 470d29256e..4ca433b12f 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -341,6 +341,7 @@ final class GiftStoreScreenComponent: Component { } self.showLoading = true self.state?.starGiftsContext.updateFilterAttributes([]) + self.scrollToTop() }, animateScale: false ) @@ -359,7 +360,7 @@ final class GiftStoreScreenComponent: Component { var emptyResultsActionFrame = CGRect( origin: CGPoint( x: floorToScreenPixels((availableWidth - emptyResultsActionSize.width) / 2.0), - y: max(self.scrollView.contentSize.height - 8.0, availableHeight - bottomInset - emptyResultsActionSize.height - 16.0) + y: max(self.scrollView.contentSize.height - 70.0, availableHeight - bottomInset - emptyResultsActionSize.height - 16.0) ), size: emptyResultsActionSize ) @@ -437,7 +438,7 @@ final class GiftStoreScreenComponent: Component { if view.superview == nil { view.alpha = 0.0 fadeTransition.setAlpha(view: view, alpha: 1.0) - self.insertSubview(view, belowSubview: self.loadingNode.view) + self.scrollView.addSubview(view) } view.bounds = CGRect(origin: .zero, size: emptyResultsActionFrame.size) ComponentTransition.immediate.setPosition(view: view, position: emptyResultsActionFrame.center) @@ -847,7 +848,7 @@ final class GiftStoreScreenComponent: Component { self.backgroundColor = environment.theme.list.blocksBackgroundColor } - let bottomContentInset: CGFloat = 24.0 + let bottomContentInset: CGFloat = 56.0 let sideInset: CGFloat = 16.0 + environment.safeInsets.left let headerSideInset: CGFloat = 24.0 + environment.safeInsets.left From 4e9d624877be5a2790a4a58b90fe4bc56477e6c6 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 7 May 2025 22:07:40 +0100 Subject: [PATCH 20/22] Fix apple intelligence gesture conflict --- submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 1e884bf566..e900a5c165 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -539,6 +539,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch var customEmojiContainerView: CustomEmojiContainerView? let textInputBackgroundNode: ASImageNode + var textInputBackgroundTapRecognizer: TouchDownGestureRecognizer? private var transparentTextInputBackgroundImage: UIImage? let actionButtons: ChatTextInputActionButtonsNode private let slowModeButton: BoostSlowModeButton @@ -1089,6 +1090,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch return false } } + self.textInputBackgroundTapRecognizer = recognizer self.textInputBackgroundNode.isUserInteractionEnabled = true self.textInputBackgroundNode.view.addGestureRecognizer(recognizer) @@ -1166,6 +1168,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch textInputNode.isUserInteractionEnabled = !self.sendingTextDisabled self.textInputNode = textInputNode + if let textInputBackgroundTapRecognizer = self.textInputBackgroundTapRecognizer { + self.textInputBackgroundTapRecognizer = nil + self.textInputBackgroundNode.view.removeGestureRecognizer(textInputBackgroundTapRecognizer) + } + var accessoryButtonsWidth: CGFloat = 0.0 var firstButton = true for (_, button) in self.accessoryItemButtons { From 064ab690580923f5f8b68e5b608356ac581303e4 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 7 May 2025 22:07:50 +0100 Subject: [PATCH 21/22] Fix disposable leak --- submodules/TelegramCore/Sources/Network/FetchV2.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/submodules/TelegramCore/Sources/Network/FetchV2.swift b/submodules/TelegramCore/Sources/Network/FetchV2.swift index 72703446ee..702c560994 100644 --- a/submodules/TelegramCore/Sources/Network/FetchV2.swift +++ b/submodules/TelegramCore/Sources/Network/FetchV2.swift @@ -103,6 +103,10 @@ private final class FetchImpl { init(range: Range) { self.range = range } + + deinit { + self.disposable?.dispose() + } } private final class HashRangeData { From aa357d463c0a185ee87fe37c9257d1699880f5c7 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 7 May 2025 22:09:26 +0100 Subject: [PATCH 22/22] Remove post suggestions --- .../PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 3ef862c66c..6d5e77e865 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -2214,9 +2214,9 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt })) //TODO:localize - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: { + /*items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: { interaction.editingOpenPostSuggestionsSetup() - })) + }))*/ } if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {