Compare commits

..

32 Commits

Author SHA1 Message Date
Kylmakalle
7cd957152e Fix iOS 13-14 crash concurrency-dylib.patch 2025-05-21 18:35:22 +03:00
Kylmakalle
f3570387ff Disable ipa post processor 2025-05-21 18:35:22 +03:00
Kylmakalle
b03178483e Fix iOS 13-14 concurrency backport 2025-05-21 18:35:22 +03:00
Kylmakalle
a4dce02329 Fix missing watchOS DSYMs 2025-05-21 18:35:16 +03:00
Kylmakalle
86987f5059 Merge Fixes 2025-05-21 18:35:08 +03:00
Kylmakalle
bc1796f172 Swiftgram Pro label 2025-05-21 18:35:08 +03:00
Kylmakalle
e031002f40 Updated submodules to match upstream 2025-05-21 18:35:07 +03:00
Kylmakalle
541c9d41d4 Fix alert for pro 2025-05-21 18:35:07 +03:00
Kylmakalle
836ed086f4 WatchOS keyboard allow emoji 2025-05-21 18:35:07 +03:00
Kylmakalle
be7626f30b Build armv7k for watch (hope it works) 2025-05-21 18:35:07 +03:00
Kylmakalle
341d22ae1a merge fixes 2025-05-21 18:35:07 +03:00
Kylmakalle
b7cbb997bd Resolve Swiftgram build variables 2025-05-21 18:35:07 +03:00
Kylmakalle
31d160fc23 WatchOS fullsize keyboard 2025-05-21 18:35:07 +03:00
Kylmakalle
de1d5851c1 Support WatchOS 7.0 2025-05-21 18:35:07 +03:00
Kylmakalle
46c160e25e Version 11.9 2025-05-21 18:35:07 +03:00
Ilya Laktyushin
e5762bd9c8 Various fixes 2025-05-18 04:35:56 +04:00
Ilya Laktyushin
3b7ddc33ee Merge commit '0401bc8ef1ac1ea2038e4c986072d4cb52ecb4b6' into beta 2025-05-14 18:53:42 +04:00
Ilya Laktyushin
0401bc8ef1 Various fixes 2025-05-14 18:51:52 +04:00
Ilya Laktyushin
4882a3b9b2 Update localization
(cherry picked from commit ada73e1e20dfd5f48856e0bcc448b813d136b2bd)
2025-05-14 01:02:57 +08:00
Ilya Laktyushin
3fe4720668 Fix build
(cherry picked from commit fb0479facdf066f09cd93253e14bcda1c0e213fb)
2025-05-14 01:02:37 +08:00
Isaac
a47dd6ff9e Merge commit '0ae2f9b53a2b0c1d369492dfb9c2bb36f6ea6e8d' into beta 2025-05-14 01:02:21 +08:00
Isaac
96be5f6410 Bump version 2025-05-14 01:01:28 +08:00
Ilya Laktyushin
e39a3dbdd6 Various fixes
(cherry picked from commit 0b448bf68fd7ae92526bb6ca8ac6b0e2d20a366b)
2025-05-14 00:51:38 +08:00
Isaac
25a3ae793b Attempt to fix gallery glitch
(cherry picked from commit c4b0444e538a9f0ec8130cc6c2f1165d25b7741b)
2025-05-14 00:51:16 +08:00
Isaac
15b769a274 Don't show fake call UI on old iOS versions
(cherry picked from commit 78f382a8f1d0fd4d8f2c071433e07435b4d370f6)
2025-05-14 00:51:08 +08:00
Ilya Laktyushin
ada73e1e20 Update localization 2025-05-13 16:16:30 +04:00
Ilya Laktyushin
fb0479facd Fix build 2025-05-13 16:15:08 +04:00
Ilya Laktyushin
9927abd73a Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios 2025-05-13 13:42:39 +04:00
Ilya Laktyushin
0b448bf68f Various fixes 2025-05-13 13:41:41 +04:00
Isaac
a775b2c62c Merge commit '0ae2f9b53a2b0c1d369492dfb9c2bb36f6ea6e8d' 2025-05-12 11:13:34 +04:00
Isaac
c4b0444e53 Attempt to fix gallery glitch 2025-05-12 11:13:00 +04:00
Isaac
78f382a8f1 Don't show fake call UI on old iOS versions 2025-05-12 11:11:45 +04:00
24 changed files with 419 additions and 126 deletions

View File

@ -14,6 +14,12 @@
# fi # fi
# done # done
# concurrency-dylib.patch must be applied in build-system/bazel-rules/rules_apple
# cd Swiftgram/FixConcurrencyBackport
# git apply ../../../Swiftgram/FixConcurrencyBackport/concurrency-dylib.patch
# # Make a build
# git apply -R ../../../Swiftgram/FixConcurrencyBackport/concurrency-dylib.patch
# Refs: # Refs:
# https://stackoverflow.com/questions/79522371/when-building-the-project-with-xcode-16-2-the-app-crashes-due-to-an-incorrect-l # https://stackoverflow.com/questions/79522371/when-building-the-project-with-xcode-16-2-the-app-crashes-due-to-an-incorrect-l
# https://github.com/swiftlang/swift/issues/74303 # https://github.com/swiftlang/swift/issues/74303

View File

@ -0,0 +1,25 @@
diff --git a/tools/swift_stdlib_tool/swift_stdlib_tool.py b/tools/swift_stdlib_tool/swift_stdlib_tool.py
index fbb7f4fb..5a2277c5 100644
--- a/tools/swift_stdlib_tool/swift_stdlib_tool.py
+++ b/tools/swift_stdlib_tool/swift_stdlib_tool.py
@@ -134,6 +134,20 @@ def _copy_swift_stdlibs(binaries_to_scan, sdk_platform, destination_path):
if os.path.exists(libswiftcore_path):
os.remove(libswiftcore_path)
+ # MARK: Swiftgram
+ if sdk_platform == "iphoneos":
+ # Copy the concurrency runtime to the destination path.
+ _, stdout, stderr = execute.execute_and_filter_output(
+ [
+ "ditto",
+ f"{developer_dir}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/iphoneos/libswift_Concurrency.dylib",
+ os.path.join(destination_path, "libswift_Concurrency.dylib")
+ ], raise_on_failure=True)
+ if stderr:
+ print(stderr)
+ if stdout:
+ print(stdout)
+
def _lipo_exec_files(exec_files, target_archs, strip_bitcode, source_path,
destination_path):

View File

@ -14320,3 +14320,19 @@ Sorry for the inconvenience.";
"Story.Editor.TooltipSelection_1" = "Tap here to view your %@ story"; "Story.Editor.TooltipSelection_1" = "Tap here to view your %@ story";
"Story.Editor.TooltipSelection_any" = "Tap here to view your %@ stories"; "Story.Editor.TooltipSelection_any" = "Tap here to view your %@ stories";
"Gift.Buy.ErrorUnknown" = "An error occurred. Please try again.";
"Gift.Buy.ErrorPriceChanged.Title" = "Warning";
"Gift.Buy.ErrorPriceChanged.Text" = "The price has increased from **%1$@** to **%2$@**. Buy the gift anyway?";
"Gift.Buy.ErrorPriceChanged.Text.Stars_1" = "%@ Star";
"Gift.Buy.ErrorPriceChanged.Text.Stars_any" = "%@ Stars";
"ChatbotSetup.Gift.Warning.Title" = "Warning";
"ChatbotSetup.Gift.Warning.CombinedText" = "The bot **%@** will be able to **manage your gifts and stars**, including giving them away to other users.";
"ChatbotSetup.Gift.Warning.GiftsText" = "The bot **%@** will be able to **manage your gifts**, including giving them away to other users.";
"ChatbotSetup.Gift.Warning.StarsText" = "The bot **%@** will be able to **transfer your stars**.";
"ChatbotSetup.Gift.Warning.UsernameText" = "The bot **%@** will be able to **set and remove usernames** for your account, which may result in the loss of your current username.";
"ChatbotSetup.Gift.Warning.Proceed" = "Proceed";
"Gift.Buy.ErrorTooEarly.Title" = "Try Later";
"Gift.Buy.ErrorTooEarly.Text" = "You will be able to buy this gift on %@.";

View File

@ -341,8 +341,13 @@ final class ChatSendMessageContextScreenComponent: Component {
guard let animateInTimestamp = self.animateInTimestamp, animateInTimestamp < CFAbsoluteTimeGetCurrent() - 0.35 else { guard let animateInTimestamp = self.animateInTimestamp, animateInTimestamp < CFAbsoluteTimeGetCurrent() - 0.35 else {
return return
} }
let localPoint: CGPoint
actionsStackNode.highlightGestureMoved(location: actionsStackNode.view.convert(location, from: view)) if let metrics = self.environment?.metrics, metrics.isTablet, availableSize.width > availableSize.height, let view {
localPoint = view.convert(location, to: nil)
} else {
localPoint = self.convert(location, from: view)
}
actionsStackNode.highlightGestureMoved(location: self.convert(localPoint, to: actionsStackNode.view))
} }
component.gesture.externalEnded = { [weak self] viewAndLocation in component.gesture.externalEnded = { [weak self] viewAndLocation in
guard let self, let actionsStackNode = self.actionsStackNode else { guard let self, let actionsStackNode = self.actionsStackNode else {

View File

@ -1841,7 +1841,7 @@ public final class ContactListNode: ASDisplayNode {
} }
var isEmpty = false var isEmpty = false
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty { if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty && topPeers.isEmpty {
isEmpty = true isEmpty = true
} }

View File

@ -109,6 +109,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
public var onInteractionUpdated: (Bool) -> Void = { _ in } public var onInteractionUpdated: (Bool) -> Void = { _ in }
public var edgePreviewUpdated: (Bool) -> Void = { _ in } public var edgePreviewUpdated: (Bool) -> Void = { _ in }
public var onTextEditingEnded: (Bool) -> Void = { _ in }
private let hapticFeedback = HapticFeedback() private let hapticFeedback = HapticFeedback()

View File

@ -3078,6 +3078,11 @@ public final class DrawingToolsInteraction {
self.onInteractionUpdated(isInteracting) self.onInteractionUpdated(isInteracting)
} }
} }
self.entitiesView.onTextEditingEnded = { [weak self] reset in
if let self {
self.onTextEditingEnded(reset)
}
}
self.entitiesView.requestedMenuForEntityView = { [weak self] entityView, isTopmost in self.entitiesView.requestedMenuForEntityView = { [weak self] entityView, isTopmost in
guard let self, let node = self.getControllerNode() else { guard let self, let node = self.getControllerNode() else {
return return
@ -3267,7 +3272,6 @@ public final class DrawingToolsInteraction {
public func endTextEditing(reset: Bool) { public func endTextEditing(reset: Bool) {
if let entityView = self.entitiesView.selectedEntityView as? DrawingTextEntityView { if let entityView = self.entitiesView.selectedEntityView as? DrawingTextEntityView {
entityView.endEditing(reset: reset) entityView.endEditing(reset: reset)
self.onTextEditingEnded(reset)
} }
} }

View File

@ -357,6 +357,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
selectionView.alpha = 1.0 selectionView.alpha = 1.0
selectionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) selectionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
parentView.onTextEditingEnded(reset)
} }
func suspendEditing() { func suspendEditing() {

View File

@ -796,8 +796,8 @@ public class GalleryController: ViewController, StandalonePresentableController,
let syncResult = Atomic<(Bool, (() -> Void)?)>(value: (false, nil)) let syncResult = Atomic<(Bool, (() -> Void)?)>(value: (false, nil))
self.disposable.set(combineLatest( self.disposable.set(combineLatest(
messageView, messageView,
self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]), self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) |> take(1),
translateToLanguage translateToLanguage |> take(1)
).start(next: { [weak self] view, preferencesView, translateToLanguage in ).start(next: { [weak self] view, preferencesView, translateToLanguage in
let f: () -> Void = { let f: () -> Void = {
if let strongSelf = self { if let strongSelf = self {

View File

@ -1540,8 +1540,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
strongSelf.videoNode?.playOnceWithSound(playAndRecord: false, seek: .none, actionAtEnd: isAnimated ? .loop : strongSelf.actionAtEnd) strongSelf.videoNode?.playOnceWithSound(playAndRecord: false, seek: .none, actionAtEnd: isAnimated ? .loop : strongSelf.actionAtEnd)
} }
if let playbackRate = strongSelf.playbackRate { Queue.mainQueue().after(0.1) {
strongSelf.videoNode?.setBaseRate(playbackRate) if let playbackRate = strongSelf.playbackRate {
strongSelf.videoNode?.setBaseRate(playbackRate)
}
} }
} }
} }

View File

@ -179,6 +179,7 @@ public enum BotPaymentFormRequestError {
case alreadyActive case alreadyActive
case noPaymentNeeded case noPaymentNeeded
case disallowedStarGift case disallowedStarGift
case starGiftResellTooEarly(Int32)
} }
extension BotPaymentInvoice { extension BotPaymentInvoice {
@ -482,6 +483,11 @@ func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, netw
return .fail(.noPaymentNeeded) return .fail(.noPaymentNeeded)
} else if error.errorDescription == "USER_DISALLOWED_STARGIFTS" { } else if error.errorDescription == "USER_DISALLOWED_STARGIFTS" {
return .fail(.disallowedStarGift) return .fail(.disallowedStarGift)
} else if error.errorDescription.hasPrefix("STARGIFT_RESELL_TOO_EARLY_") {
let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "STARGIFT_RESELL_TOO_EARLY_".count)...])
if let value = Int32(timeout) {
return .fail(.starGiftResellTooEarly(value))
}
} }
return .fail(.generic) return .fail(.generic)
} }

View File

@ -854,6 +854,8 @@ public enum TransferStarGiftError {
public enum BuyStarGiftError { public enum BuyStarGiftError {
case generic case generic
case priceChanged(Int64)
case starGiftResellTooEarly(Int32)
} }
public enum UpdateStarGiftPriceError { public enum UpdateStarGiftPriceError {
@ -865,15 +867,21 @@ public enum UpgradeStarGiftError {
case generic case generic
} }
func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId) let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId)
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil) return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|> map(Optional.init) |> map(Optional.init)
|> `catch` { error -> Signal<BotPaymentForm?, BuyStarGiftError> in |> `catch` { error -> Signal<BotPaymentForm?, BuyStarGiftError> in
if case let .starGiftResellTooEarly(timestamp) = error {
return .fail(.starGiftResellTooEarly(timestamp))
}
return .fail(.generic) return .fail(.generic)
} }
|> mapToSignal { paymentForm in |> mapToSignal { paymentForm in
if let paymentForm { if let paymentForm {
if let paymentPrice = paymentForm.invoice.prices.first?.amount, let price, paymentPrice > price {
return .fail(.priceChanged(paymentPrice))
}
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source) return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|> mapError { _ -> BuyStarGiftError in |> mapError { _ -> BuyStarGiftError in
return .generic return .generic
@ -1148,10 +1156,12 @@ private final class ProfileGiftsContextImpl {
self.filteredGifts = [] self.filteredGifts = []
self.filteredCount = nil self.filteredCount = nil
} }
let isUniqueOnlyFilter = self.filter == [.unique, .displayed, .hidden]
let dataState = isFiltered ? self.filteredDataState : self.dataState let dataState = isFiltered ? self.filteredDataState : self.dataState
if case let .ready(true, initialNextOffset) = dataState { if case let .ready(true, initialNextOffset) = dataState {
if !isFiltered, self.gifts.isEmpty, initialNextOffset == nil, !reload { if !isFiltered || isUniqueOnlyFilter, self.gifts.isEmpty, initialNextOffset == nil, !reload {
self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in
let cachedGifts = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileGifts.self) let cachedGifts = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileGifts.self)
cachedGifts?.render(transaction: transaction) cachedGifts?.render(transaction: transaction)
@ -1160,7 +1170,22 @@ private final class ProfileGiftsContextImpl {
guard let self, let cachedGifts else { guard let self, let cachedGifts else {
return return
} }
if case .loading = self.dataState { if isUniqueOnlyFilter, case .loading = self.filteredDataState {
var gifts = cachedGifts.gifts
if isUniqueOnlyFilter {
gifts = gifts.filter({ gift in
if case .unique = gift.gift {
return true
} else {
return false
}
})
}
self.gifts = gifts
self.count = cachedGifts.count
self.notificationsEnabled = cachedGifts.notificationsEnabled
self.pushState()
} else if case .loading = self.dataState {
self.gifts = cachedGifts.gifts self.gifts = cachedGifts.gifts
self.count = cachedGifts.count self.count = cachedGifts.count
self.notificationsEnabled = cachedGifts.notificationsEnabled self.notificationsEnabled = cachedGifts.notificationsEnabled
@ -1473,26 +1498,53 @@ private final class ProfileGiftsContextImpl {
return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId) return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId)
} }
func buyStarGift(slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
if let count = self.count { var listingPrice: Int64?
self.count = max(0, count - 1) if let gift = self.gifts.first(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
}), case let .unique(uniqueGift) = gift.gift {
listingPrice = uniqueGift.resellStars
} }
self.gifts.removeAll(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
})
self.filteredGifts.removeAll(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
})
self.pushState()
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId) if listingPrice == nil {
if let gift = self.filteredGifts.first(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
}), case let .unique(uniqueGift) = gift.gift {
listingPrice = uniqueGift.resellStars
}
}
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price ?? listingPrice)
|> afterCompleted { [weak self] in
guard let self else {
return
}
self.queue.async {
if let count = self.count {
self.count = max(0, count - 1)
}
self.gifts.removeAll(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
})
self.filteredGifts.removeAll(where: { gift in
if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
return true
}
return false
})
self.pushState()
}
}
} }
func removeStarGift(gift: TelegramCore.StarGift) { func removeStarGift(gift: TelegramCore.StarGift) {
@ -1975,11 +2027,11 @@ public final class ProfileGiftsContext {
} }
} }
public func buyStarGift(slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal<Never, BuyStarGiftError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.impl.with { impl in self.impl.with { impl in
disposable.set(impl.buyStarGift(slug: slug, peerId: peerId).start(error: { error in disposable.set(impl.buyStarGift(slug: slug, peerId: peerId, price: price).start(error: { error in
subscriber.putError(error) subscriber.putError(error)
}, completed: { }, completed: {
subscriber.putCompletion() subscriber.putCompletion()
@ -2606,8 +2658,18 @@ private final class ResaleGiftsContextImpl {
self.loadMore() self.loadMore()
} }
func buyStarGift(slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId) var listingPrice: Int64?
if let gift = self.gifts.first(where: { gift in
if case let .unique(uniqueGift) = gift, uniqueGift.slug == slug {
return true
}
return false
}), case let .unique(uniqueGift) = gift {
listingPrice = uniqueGift.resellStars
}
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price ?? listingPrice)
|> afterCompleted { [weak self] in |> afterCompleted { [weak self] in
guard let self else { guard let self else {
return return
@ -2754,11 +2816,11 @@ public final class ResaleGiftsContext {
} }
} }
public func buyStarGift(slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal<Never, BuyStarGiftError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.impl.with { impl in self.impl.with { impl in
disposable.set(impl.buyStarGift(slug: slug, peerId: peerId).start(error: { error in disposable.set(impl.buyStarGift(slug: slug, peerId: peerId, price: price).start(error: { error in
subscriber.putError(error) subscriber.putError(error)
}, completed: { }, completed: {
subscriber.putCompletion() subscriber.putCompletion()

View File

@ -125,8 +125,8 @@ public extension TelegramEngine {
return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId) return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId)
} }
public func buyStarGift(slug: String, peerId: EnginePeer.Id) -> Signal<Never, BuyStarGiftError> { public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId) return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price)
} }
public func upgradeStarGift(formId: Int64?, reference: StarGiftReference, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> { public func upgradeStarGift(formId: Int64?, reference: StarGiftReference, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {

View File

@ -601,6 +601,22 @@ final class GiftOptionsScreenComponent: Component {
return return
} }
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let canTransferDate = gift.canTransferDate, currentTime < canTransferDate {
let dateString = stringForFullDate(timestamp: canTransferDate, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)
let alertController = textAlertController(
context: component.context,
title: environment.strings.Gift_Transfer_Unavailable_Title,
text: environment.strings.Gift_Transfer_Unavailable_Text(dateString).string,
actions: [
TextAlertAction(type: .defaultAction, title: environment.strings.Common_OK, action: {})
],
parseMarkdown: true
)
controller.present(alertController, in: .window(.root))
return
}
let mainController: ViewController let mainController: ViewController
if let parentController = controller.parentController() { if let parentController = controller.parentController() {
mainController = parentController mainController = parentController

View File

@ -270,8 +270,8 @@ final class GiftStoreScreenComponent: Component {
subject: .uniqueGift(uniqueGift, state.peerId), subject: .uniqueGift(uniqueGift, state.peerId),
allSubjects: allSubjects, allSubjects: allSubjects,
index: index, index: index,
buyGift: { slug, peerId in buyGift: { slug, peerId, price in
return self.state?.starGiftsContext.buyStarGift(slug: slug, peerId: peerId) ?? .complete() return self.state?.starGiftsContext.buyStarGift(slug: slug, peerId: peerId, price: price) ?? .complete()
}, },
updateResellStars: { price in updateResellStars: { price in
return self.state?.starGiftsContext.updateStarGiftResellPrice(slug: uniqueGift.slug, price: price) ?? .complete() return self.state?.starGiftsContext.updateStarGiftResellPrice(slug: uniqueGift.slug, price: price) ?? .complete()

View File

@ -107,6 +107,7 @@ private final class GiftViewSheetContent: CombinedComponent {
var buyForm: BotPaymentForm? var buyForm: BotPaymentForm?
var buyFormDisposable: Disposable? var buyFormDisposable: Disposable?
var buyDisposable: Disposable? var buyDisposable: Disposable?
var resellTooEarlyTimestamp: Int32?
var inWearPreview = false var inWearPreview = false
var pendingWear = false var pendingWear = false
@ -180,6 +181,14 @@ private final class GiftViewSheetContent: CombinedComponent {
} }
self.buyForm = paymentForm self.buyForm = paymentForm
self.updated() self.updated()
}, error: { [weak self] error in
guard let self else {
return
}
if case let .starGiftResellTooEarly(remaining) = error {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
self.resellTooEarlyTimestamp = currentTime + remaining
}
}) })
} }
} else if case let .generic(gift) = arguments.gift { } else if case let .generic(gift) = arguments.gift {
@ -871,7 +880,7 @@ private final class GiftViewSheetContent: CombinedComponent {
title = nil title = nil
text = presentationData.strings.Gift_Send_ErrorUnknown text = presentationData.strings.Gift_Send_ErrorUnknown
case let .starGiftResellTooEarly(canResaleDate): case let .starGiftResellTooEarly(canResaleDate):
let dateString = stringForFullDate(timestamp: canResaleDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) let dateString = stringForFullDate(timestamp: currentTime + canResaleDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
title = presentationData.strings.Gift_Resale_Unavailable_Title title = presentationData.strings.Gift_Resale_Unavailable_Title
text = presentationData.strings.Gift_Resale_Unavailable_Text(dateString).string text = presentationData.strings.Gift_Resale_Unavailable_Text(dateString).string
} }
@ -1145,19 +1154,37 @@ private final class GiftViewSheetContent: CombinedComponent {
} }
} }
func commitBuy(skipConfirmation: Bool = false) { func commitBuy(acceptedPrice: Int64? = nil, skipConfirmation: Bool = false) {
guard let resellStars = self.subject.arguments?.resellStars, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, case let .unique(uniqueGift) = self.subject.arguments?.gift else { guard let resellStars = self.subject.arguments?.resellStars, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, case let .unique(uniqueGift) = self.subject.arguments?.gift else {
return return
} }
let giftTitle = "\(uniqueGift.title) #\(uniqueGift.number)"
let context = self.context let context = self.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
if let resellTooEarlyTimestamp = self.resellTooEarlyTimestamp {
guard let controller = self.getController() else {
return
}
let dateString = stringForFullDate(timestamp: resellTooEarlyTimestamp, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
let alertController = textAlertController(
context: context,
title: presentationData.strings.Gift_Buy_ErrorTooEarly_Title,
text: presentationData.strings.Gift_Buy_ErrorTooEarly_Text(dateString).string,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
],
parseMarkdown: true
)
controller.present(alertController, in: .window(.root))
return
}
let giftTitle = "\(uniqueGift.title) #\(uniqueGift.number)"
let recipientPeerId = self.recipientPeerId ?? self.context.account.peerId let recipientPeerId = self.recipientPeerId ?? self.context.account.peerId
let action = { let action = {
let proceed: (Int64) -> Void = { formId in let proceed: () -> Void = {
guard let controller = self.getController() as? GiftViewScreen else { guard let controller = self.getController() as? GiftViewScreen else {
return return
} }
@ -1165,38 +1192,68 @@ private final class GiftViewSheetContent: CombinedComponent {
self.inProgress = true self.inProgress = true
self.updated() self.updated()
let buyGiftImpl: ((String, EnginePeer.Id) -> Signal<Never, BuyStarGiftError>) let buyGiftImpl: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)
if let buyGift = controller.buyGift { if let buyGift = controller.buyGift {
buyGiftImpl = { slug, peerId in buyGiftImpl = { slug, peerId, price in
return buyGift(slug, peerId) return buyGift(slug, peerId, price)
|> afterCompleted { |> afterCompleted {
context.starsContext?.load(force: true) context.starsContext?.load(force: true)
} }
} }
} else { } else {
buyGiftImpl = { slug, peerId in buyGiftImpl = { slug, peerId, price in
return self.context.engine.payments.buyStarGift(slug: slug, peerId: peerId) return self.context.engine.payments.buyStarGift(slug: slug, peerId: peerId, price: price)
|> afterCompleted { |> afterCompleted {
context.starsContext?.load(force: true) context.starsContext?.load(force: true)
} }
} }
} }
self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId) self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId, acceptedPrice ?? resellStars)
|> deliverOnMainQueue).start(error: { [weak self] error in |> deliverOnMainQueue).start(
guard let self, let controller = self.getController() else { error: { [weak self] error in
return guard let self, let controller = self.getController() else {
} return
}
self.inProgress = false
self.updated() self.inProgress = false
self.updated()
let errorText = presentationData.strings.Gift_Send_ErrorUnknown
switch error {
let alertController = textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true) case let .priceChanged(newPrice):
controller.present(alertController, in: .window(.root)) let errorTitle = presentationData.strings.Gift_Buy_ErrorPriceChanged_Title
}, completed: { [weak self, weak starsContext] in let originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellStars))
guard let self, let controller = self.getController() as? GiftViewScreen else { let newPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(newPrice))
let errorText = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text(originalPriceString, newPriceString).string
let alertController = textAlertController(
context: context,
title: errorTitle,
text: errorText,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(newPrice)), action: { [weak self] in
guard let self else {
return
}
self.commitBuy(acceptedPrice: newPrice, skipConfirmation: true)
}),
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
})
],
actionLayout: .vertical,
parseMarkdown: true
)
controller.present(alertController, in: .window(.root))
HapticFeedback().error()
default:
let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Gift_Buy_ErrorUnknown, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true)
controller.present(alertController, in: .window(.root))
}
},
completed: { [weak self, weak starsContext] in
guard let self,
let controller = self.getController() as? GiftViewScreen else {
return return
} }
self.inProgress = false self.inProgress = false
@ -1303,7 +1360,7 @@ private final class GiftViewSheetContent: CombinedComponent {
self.commitBuy(skipConfirmation: true) self.commitBuy(skipConfirmation: true)
} else { } else {
proceed(buyForm.id) proceed()
} }
}); });
}) })
@ -1312,8 +1369,14 @@ private final class GiftViewSheetContent: CombinedComponent {
controller.push(purchaseController) controller.push(purchaseController)
}) })
} else { } else {
proceed(buyForm.id) proceed()
} }
} else {
guard let controller = self.getController() else {
return
}
let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Gift_Buy_ErrorUnknown, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true)
controller.present(alertController, in: .window(.root))
} }
} }
@ -1321,7 +1384,7 @@ private final class GiftViewSheetContent: CombinedComponent {
action() action()
} else { } else {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: recipientPeerId)) let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: recipientPeerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
guard let self, let peer else { guard let self, let peer else {
return return
} }
@ -3565,7 +3628,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
fileprivate let convertToStars: (() -> Void)? fileprivate let convertToStars: (() -> Void)?
fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)?
fileprivate let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? fileprivate let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
fileprivate let buyGift: ((String, EnginePeer.Id) -> Signal<Never, BuyStarGiftError>)? fileprivate let buyGift: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)?
fileprivate let updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)? fileprivate let updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)?
fileprivate let togglePinnedToTop: ((Bool) -> Bool)? fileprivate let togglePinnedToTop: ((Bool) -> Bool)?
fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)? fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)?
@ -3582,7 +3645,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
convertToStars: (() -> Void)? = nil, convertToStars: (() -> Void)? = nil,
transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? = nil, transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? = nil,
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil, upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil,
buyGift: ((String, EnginePeer.Id) -> Signal<Never, BuyStarGiftError>)? = nil, buyGift: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)? = nil,
updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil, updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil,
togglePinnedToTop: ((Bool) -> Bool)? = nil, togglePinnedToTop: ((Bool) -> Bool)? = nil,
shareStory: ((StarGift.UniqueGift) -> Void)? = nil shareStory: ((StarGift.UniqueGift) -> Void)? = nil

View File

@ -3085,6 +3085,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
private var hiddenMediaDisposable: Disposable? private var hiddenMediaDisposable: Disposable?
private let hiddenAvatarRepresentationDisposable = MetaDisposable() private let hiddenAvatarRepresentationDisposable = MetaDisposable()
private var autoTranslateDisposable: Disposable?
private var resolvePeerByNameDisposable: MetaDisposable? private var resolvePeerByNameDisposable: MetaDisposable?
private let navigationActionDisposable = MetaDisposable() private let navigationActionDisposable = MetaDisposable()
private let enqueueMediaMessageDisposable = MetaDisposable() private let enqueueMediaMessageDisposable = MetaDisposable()
@ -5150,11 +5152,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} }
return profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo) return profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
}, },
buyGift: { [weak profileGifts] slug, peerId in buyGift: { [weak profileGifts] slug, peerId, price in
guard let profileGifts else { guard let profileGifts else {
return .never() return .never()
} }
return profileGifts.buyStarGift(slug: slug, peerId: peerId) return profileGifts.buyStarGift(slug: slug, peerId: peerId, price: price)
}, },
shareStory: { [weak self] uniqueGift in shareStory: { [weak self] uniqueGift in
guard let self, let controller = self.controller else { guard let self, let controller = self.controller else {
@ -5374,6 +5376,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.joinChannelDisposable.dispose() self.joinChannelDisposable.dispose()
self.boostStatusDisposable?.dispose() self.boostStatusDisposable?.dispose()
self.personalChannelsDisposable?.dispose() self.personalChannelsDisposable?.dispose()
self.autoTranslateDisposable?.dispose()
} }
override func didLoad() { override func didLoad() {
@ -9453,7 +9456,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} }
private func displayAutoTranslateLocked() { private func displayAutoTranslateLocked() {
let _ = combineLatest( guard self.autoTranslateDisposable == nil else {
return
}
self.autoTranslateDisposable = combineLatest(
queue: Queue.mainQueue(), queue: Queue.mainQueue(),
context.engine.peers.getChannelBoostStatus(peerId: self.peerId), context.engine.peers.getChannelBoostStatus(peerId: self.peerId),
context.engine.peers.getMyBoostStatus() context.engine.peers.getMyBoostStatus()
@ -9467,6 +9473,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} }
}) })
controller.push(boostController) controller.push(boostController)
self.autoTranslateDisposable?.dispose()
self.autoTranslateDisposable = nil
}) })
} }

View File

@ -604,11 +604,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
} }
return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo) return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
}, },
buyGift: { [weak self] slug, peerId in buyGift: { [weak self] slug, peerId, price in
guard let self else { guard let self else {
return .never() return .never()
} }
return self.profileGifts.buyStarGift(slug: slug, peerId: peerId) return self.profileGifts.buyStarGift(slug: slug, peerId: peerId, price: price)
}, },
updateResellStars: { [weak self] price in updateResellStars: { [weak self] price in
guard let self, let reference = product.reference else { guard let self, let reference = product.reference else {

View File

@ -128,7 +128,7 @@ final class ChatbotSearchResultItemComponent: Component {
animateScale: false animateScale: false
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: 100.0, height: 100.0) containerSize: CGSize(width: 140.0, height: 100.0)
) )
} else { } else {
if let addButton = self.addButton { if let addButton = self.addButton {

View File

@ -174,6 +174,8 @@ final class ChatbotSetupScreenComponent: Component {
private var permissions: [Permission] = [] private var permissions: [Permission] = []
private var botRights: TelegramBusinessBotRights = [] private var botRights: TelegramBusinessBotRights = []
private var temporaryEnabledPermissions = Set<String>()
override init(frame: CGRect) { override init(frame: CGRect) {
self.scrollView = ScrollView() self.scrollView = ScrollView()
self.scrollView.showsVerticalScrollIndicator = true self.scrollView.showsVerticalScrollIndicator = true
@ -505,6 +507,44 @@ final class ChatbotSetupScreenComponent: Component {
} }
} }
private func presentStarGiftsWarningIfNeeded(_ key: TelegramBusinessBotRights, completion: @escaping (Bool) -> Void) -> Bool {
guard let component = self.component, let environment = self.environment, let botResolutionState = self.botResolutionState, case let .found(peer, _) = botResolutionState.state, let controller = environment.controller() else {
return false
}
if !key.contains(.transferAndUpgradeGifts) && !key.contains(.transferStars) && !key.contains(.editUsername) {
completion(true)
return false
} else {
let botUsername = "@\(peer.addressName ?? "")"
let text: String
if key.contains(.editUsername) {
text = environment.strings.ChatbotSetup_Gift_Warning_UsernameText(botUsername).string
} else if key == .transferAndUpgradeGifts {
text = environment.strings.ChatbotSetup_Gift_Warning_GiftsText(botUsername).string
} else if key == .transferStars {
text = environment.strings.ChatbotSetup_Gift_Warning_StarsText(botUsername).string
} else {
text = environment.strings.ChatbotSetup_Gift_Warning_CombinedText(botUsername).string
}
let alertController = textAlertController(context: component.context, title: environment.strings.ChatbotSetup_Gift_Warning_Title, text: text, actions: [
TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {
completion(false)
}),
TextAlertAction(type: .defaultAction, title: environment.strings.ChatbotSetup_Gift_Warning_Proceed, action: {
completion(true)
})
], parseMarkdown: true)
alertController.dismissed = { byOutsideTap in
if byOutsideTap {
completion(false)
}
}
controller.present(alertController, in: .window(.root))
return true
}
}
func update(component: ChatbotSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize { func update(component: ChatbotSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
self.isUpdating = true self.isUpdating = true
defer { defer {
@ -741,7 +781,7 @@ final class ChatbotSetupScreenComponent: Component {
if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.isBusiness) { if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.isBusiness) {
botResolutionState.state = .found(peer: peer, isInstalled: true) botResolutionState.state = .found(peer: peer, isInstalled: true)
self.botResolutionState = botResolutionState self.botResolutionState = botResolutionState
self.botRights = .All self.botRights = [.reply, .readMessages, .deleteSentMessages, .deleteReceivedMessages]
self.state?.updated(transition: .spring(duration: 0.3)) self.state?.updated(transition: .spring(duration: 0.3))
} else { } else {
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [ self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [
@ -1074,6 +1114,10 @@ final class ChatbotSetupScreenComponent: Component {
selectedCount += 1 selectedCount += 1
} }
} }
if self.temporaryEnabledPermissions.contains(permission.id) {
value = true
}
titleItems.append( titleItems.append(
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
@ -1101,11 +1145,33 @@ final class ChatbotSetupScreenComponent: Component {
return return
} }
if let subpermissions = permission.subpermissions { if let subpermissions = permission.subpermissions {
for subpermission in subpermissions { if value {
if subpermission.enabled { var combinedKey: TelegramBusinessBotRights = []
if let key = subpermission.key { for subpermission in subpermissions {
if subpermission.enabled, let key = subpermission.key {
combinedKey.insert(key)
}
}
self.temporaryEnabledPermissions.insert(permission.id)
let presentedWarning = self.presentStarGiftsWarningIfNeeded(combinedKey, completion: { [weak self] value in
guard let self else {
return
}
if value {
self.botRights.insert(combinedKey)
}
self.temporaryEnabledPermissions.remove(permission.id)
self.state?.updated(transition: .spring(duration: 0.4))
})
if !presentedWarning {
self.state?.updated(transition: .spring(duration: 0.4))
}
} else {
for subpermission in subpermissions {
if subpermission.enabled, let key = subpermission.key {
if value { if value {
self.botRights.insert(key)
} else { } else {
self.botRights.remove(key) self.botRights.remove(key)
} }
@ -1170,7 +1236,15 @@ final class ChatbotSetupScreenComponent: Component {
} }
if let key = subpermission.key { if let key = subpermission.key {
if !value { if !value {
self.botRights.insert(key) let _ = self.presentStarGiftsWarningIfNeeded(key, completion: { [weak self] value in
guard let self else {
return
}
if value {
self.botRights.insert(key)
}
self.state?.updated(transition: .spring(duration: 0.4))
})
} else { } else {
self.botRights.remove(key) self.botRights.remove(key)
} }

View File

@ -2471,30 +2471,32 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
} }
private func reportFailedIncomingCallKitCall() { private func reportFailedIncomingCallKitCall() {
guard let callKitIntegration = CallKitIntegration.shared else { if #available(iOS 14.4, *) {
return guard let callKitIntegration = CallKitIntegration.shared else {
} return
let uuid = CallSessionInternalId() }
callKitIntegration.reportIncomingCall( let uuid = CallSessionInternalId()
uuid: uuid, callKitIntegration.reportIncomingCall(
stableId: Int64.random(in: Int64.min ... Int64.max), uuid: uuid,
handle: "Unknown", stableId: Int64.random(in: Int64.min ... Int64.max),
phoneNumber: nil, handle: "Unknown",
isVideo: false, phoneNumber: nil,
displayTitle: "Unknown", isVideo: false,
completion: { error in displayTitle: "Unknown",
if let error = error { completion: { error in
if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) { if let error = error {
Logger.shared.log("PresentationCall", "reportFailedIncomingCallKitCall device in DND mode") if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) {
} else { Logger.shared.log("PresentationCall", "reportFailedIncomingCallKitCall device in DND mode")
Logger.shared.log("PresentationCall", "reportFailedIncomingCallKitCall error \(error)") } else {
Logger.shared.log("PresentationCall", "reportFailedIncomingCallKitCall error \(error)")
}
} }
} }
} )
) Queue.mainQueue().after(1.0, {
Queue.mainQueue().after(1.0, { callKitIntegration.dropCall(uuid: uuid)
callKitIntegration.dropCall(uuid: uuid) })
}) }
} }
private func authorizedContext() -> Signal<AuthorizedApplicationContext, NoError> { private func authorizedContext() -> Signal<AuthorizedApplicationContext, NoError> {

View File

@ -1318,27 +1318,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
} }
} }
recognizer.waitForTouchUp = { [weak self] in recognizer.waitForTouchUp = {
guard let strongSelf = self, let textInputNode = strongSelf.textInputNode else { return true
return true
}
if textInputNode.textView.isFirstResponder {
return true
} else if let (_, _, _, bottomInset, _, _, metrics, _, _) = strongSelf.validLayout {
let textFieldWaitsForTouchUp: Bool
if case .regular = metrics.widthClass, bottomInset.isZero {
textFieldWaitsForTouchUp = true
} else if !textInputNode.textView.text.isEmpty {
textFieldWaitsForTouchUp = true
} else {
textFieldWaitsForTouchUp = false
}
return textFieldWaitsForTouchUp
} else {
return false
}
} }
textInputNode.view.addGestureRecognizer(recognizer) textInputNode.view.addGestureRecognizer(recognizer)
self.touchDownGestureRecognizer = recognizer self.touchDownGestureRecognizer = recognizer
@ -4136,6 +4117,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
self.inputMenu.activate() self.inputMenu.activate()
if let touchDownGestureRecognizer = self.touchDownGestureRecognizer {
self.textInputNode?.view.addGestureRecognizer(touchDownGestureRecognizer)
}
} }
@objc func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) { @objc func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
@ -4170,6 +4155,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
} }
} }
if let touchDownGestureRecognizer = self.touchDownGestureRecognizer {
self.textInputNode?.view.removeGestureRecognizer(touchDownGestureRecognizer)
}
} }
func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) { func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {

View File

@ -179,7 +179,18 @@ final class ChatTranslationPanelNode: ASDisplayNode {
} }
@objc private func closePressed() { @objc private func closePressed() {
let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: self.context.sharedContext.accountManager, count: -100, timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 7).startStandalone() let isPremium = self.chatInterfaceState?.isPremium ?? false
var translationAvailable = isPremium
if let channel = self.chatInterfaceState?.renderedPeer?.chatMainPeer as? TelegramChannel, channel.flags.contains(.autoTranslateEnabled) {
translationAvailable = true
}
if translationAvailable {
self.interfaceInteraction?.hideTranslationPanel()
} else if !isPremium {
let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: self.context.sharedContext.accountManager, count: -100, timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 7).startStandalone()
}
} }
@objc private func buttonPressed() { @objc private func buttonPressed() {

View File

@ -1,5 +1,5 @@
{ {
"app": "11.11.1", "app": "11.11.2",
"xcode": "16.2", "xcode": "16.2",
"bazel": "7.3.1:981f82a470bad1349322b6f51c9c6ffa0aa291dab1014fac411543c12e661dff", "bazel": "7.3.1:981f82a470bad1349322b6f51c9c6ffa0aa291dab1014fac411543c12e661dff",
"macos": "15" "macos": "15"