mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Compare commits
32 Commits
11.11.1-22
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
7cd957152e | ||
|
f3570387ff | ||
|
b03178483e | ||
|
a4dce02329 | ||
|
86987f5059 | ||
|
bc1796f172 | ||
|
e031002f40 | ||
|
541c9d41d4 | ||
|
836ed086f4 | ||
|
be7626f30b | ||
|
341d22ae1a | ||
|
b7cbb997bd | ||
|
31d160fc23 | ||
|
de1d5851c1 | ||
|
46c160e25e | ||
|
e5762bd9c8 | ||
|
3b7ddc33ee | ||
|
0401bc8ef1 | ||
|
4882a3b9b2 | ||
|
3fe4720668 | ||
![]() |
a47dd6ff9e | ||
![]() |
96be5f6410 | ||
|
e39a3dbdd6 | ||
![]() |
25a3ae793b | ||
![]() |
15b769a274 | ||
|
ada73e1e20 | ||
|
fb0479facd | ||
|
9927abd73a | ||
|
0b448bf68f | ||
![]() |
a775b2c62c | ||
![]() |
c4b0444e53 | ||
![]() |
78f382a8f1 |
@ -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
|
||||||
|
25
Swiftgram/FixConcurrencyBackport/concurrency-dylib.patch
Normal file
25
Swiftgram/FixConcurrencyBackport/concurrency-dylib.patch
Normal 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):
|
@ -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 %@.";
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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> {
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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> {
|
||||||
|
@ -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) {
|
||||||
|
@ -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() {
|
||||||
|
@ -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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user