Merge commit '8d5dc7ff600836a88d23777def61e556d15eb7cd'

This commit is contained in:
Isaac 2025-07-24 22:47:05 +02:00
commit 0d2cffb033
55 changed files with 1105 additions and 433 deletions

View File

@ -14700,6 +14700,9 @@ Sorry for the inconvenience.";
"AccessDenied.AgeVerificationCamera" = "Telegram needs access to your camera for age verification.\n\nOpen your device's Settings > Privacy > Camera and set Telegram to ON.";
"PeerInfo.Gifts.Collections.All" = "All Gifts";
"PeerInfo.Gifts.Collections.Add" = "Add Collection";
"PeerInfo.Gifts.AddGiftsButton" = "Add Gifts";
"PeerInfo.Gifts.AddCollection" = "Add Collection";
@ -14728,6 +14731,12 @@ Sorry for the inconvenience.";
"PeerInfo.Gifts.EmptyCollection.Text" = "Add some gifts to this collection.";
"PeerInfo.Gifts.EmptyCollection.Action" = "Add to Collection";
"PeerInfo.Gifts.AddedToCollection" = "The gift has been added to **%@**.";
"PeerInfo.Gifts.RemovedFromCollection" = "The gift has been removed from **%@**.";
"PeerInfo.Gifts.AddedToCollectionUnique" = "**%1$@** has been added to **%2$@**.";
"PeerInfo.Gifts.RemovedFromCollectionUnique" = "**%1$@** has been removed from **%2$@**.";
"AddGifts.Title" = "Add Gifts";
"AddGifts.AddGifts_1" = "Add %@ Gift";
"AddGifts.AddGifts_any" = "Add %@ Gifts";
@ -14736,10 +14745,13 @@ Sorry for the inconvenience.";
"Gift.Options.Gift.BuyLimitReached_any" = "You've already sent %@ of these gifts, and it's the limit.";
"AgeVerification.Title" = "Age Verification";
"AgeVerification.Text" = "To access this content, you must confirm you are at least **18** years old as required by UK law.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram.";
"AgeVerification.Text.gb" = "To access this content, you must confirm you are at least **18** years old as required by UK law.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram.";
"AgeVerification.Text" = "To access this content, you must confirm you are at least **18** years old.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram.";
"AgeVerification.Text.GB" = "To access this content, you must confirm you are at least **18** years old as required by UK law.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram.";
"AgeVerification.Verify" = "Verify My Age";
"AgeVerification.Unavailable.Title" = "18+";
"AgeVerification.Unavailable.Text" = "This media may contain sensitive content suitable only for adults.";
"AgeVerification.Success.Title" = "Age check passed!";
"AgeVerification.Success.Text" = "You can now view this content.";

View File

@ -1296,7 +1296,7 @@ public protocol SharedAccountContext: AnyObject {
func makeIncomingMessagePrivacyScreen(context: AccountContext, value: GlobalPrivacySettings.NonContactChatsPrivacy, exceptions: SelectivePrivacySettings, update: @escaping (GlobalPrivacySettings.NonContactChatsPrivacy) -> Void) -> ViewController
func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?)
func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?, verifyAgeCompletion: ((Int) -> Void)?)
func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id, mode: AffiliateProgramSetupScreenMode) -> Signal<AffiliateProgramSetupScreenInitialData, NoError>
func makeAffiliateProgramSetupScreen(context: AccountContext, initialData: AffiliateProgramSetupScreenInitialData) -> ViewController

View File

@ -12,6 +12,7 @@ public final class BotCheckoutController: ViewController {
public enum FetchError {
case generic
case disallowedStarGifts
case starGiftsUserLimit
}
public let form: BotPaymentForm
@ -58,6 +59,8 @@ public final class BotCheckoutController: ViewController {
switch error {
case .disallowedStarGift:
return .disallowedStarGifts
case .starGiftUserLimit:
return .starGiftsUserLimit
default:
return .generic
}

View File

@ -1250,7 +1250,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
}

View File

@ -3282,7 +3282,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
interaction.dismissSearch()
}
@ -4268,7 +4269,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
}
}
@ -4287,7 +4289,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
interaction.dismissSearch()
} else {

View File

@ -871,7 +871,7 @@
if (_intent == TGMediaAssetsControllerSendMediaIntent && _selectionContext.allowGrouping)
[[NSUserDefaults standardUserDefaults] setObject:@(!_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
return [TGMediaAssetsController resultSignalsForSelectionContext:_selectionContext editingContext:_editingContext intent:_intent currentItem:currentItem storeAssets:storeAssets convertToJpeg:false descriptionGenerator:descriptionGenerator saveEditedPhotos:_saveEditedPhotos];
}
@ -889,6 +889,9 @@
if (selectedItems.count == 0 && currentItem != nil)
[selectedItems addObject:currentItem];
if (intent == TGMediaAssetsControllerSendMediaIntent)
[[NSUserDefaults standardUserDefaults] setObject:@(editingContext.isHighQualityPhoto) forKey:@"TG_photoHighQuality_v0"];
if (saveEditedPhotos && storeAssets && editingContext != nil)
{
NSMutableArray *fullSizeSignals = [[NSMutableArray alloc] init];

View File

@ -2159,6 +2159,12 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
strongSelf.controllerNode.dismissInput()
}
}, selectionState: selectionContext, editingState: editingContext ?? TGMediaEditingContext())
let highQualityPhoto = UserDefaults.standard.bool(forKey: "TG_photoHighQuality_v0")
if highQualityPhoto {
self.interaction?.editingState.setHighQualityPhoto(highQualityPhoto)
}
self.interaction?.selectionState?.grouping = true
self.interaction?.editingState.sendPaidMessageStars = sendPaidMessageStars ?? 0

View File

@ -220,10 +220,10 @@ private func indexToRoman(_ index: Int) -> String {
}
private func chatInputStateString(attributedString: NSAttributedString) -> NSAttributedString? {
let preprocessedString = preprocessLists(attributedString: attributedString)
//let preprocessedString = preprocessLists(attributedString: attributedString)
let string = NSMutableAttributedString(string: preprocessedString.string)
preprocessedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: [], using: { attributes, range, _ in
let string = NSMutableAttributedString(string: attributedString.string)
attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: [], using: { attributes, range, _ in
if let value = attributes[.link], let url = (value as? URL)?.absoluteString {
string.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: range)
}

View File

@ -3096,13 +3096,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
products.append(PremiumProduct(option: option, storeProduct: product))
}
}
//TODO:release
if let product = availableProducts.first(where: { $0.id.hasSuffix(".annual") }) {
let (currency, price) = product.priceCurrencyAndAmount
products.insert(PremiumProduct(option: PremiumPromoConfiguration.PremiumProductOption(isCurrent: false, months: 24, currency: currency, amount: price * 2, botUrl: "", transactionId: nil, availableForUpgrade: true, storeProductId: "org.telegram.telegramPremium.biannual"), storeProduct: product), at: 0)
}
strongSelf.products = products
strongSelf.isPremium = forceHasPremium || isPremium
strongSelf.otherPeerName = otherPeerName

View File

@ -912,12 +912,6 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
}
})
updateSensitiveContentDisposable.set(updateRemoteContentSettingsConfiguration(postbox: context.account.postbox, network: context.account.network, sensitiveContentEnabled: value).start())
if !value {
let _ = updateAgeVerificationState(engine: context.engine, { _ in
return AgeVerificationState(verificationPassed: false)
}).start()
}
}
if value {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -46,7 +46,7 @@ func confirmRevenueWithdrawalController(context: AccountContext, updatedPresenta
}
contentNode.updateIsChecking(true)
let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: false, amount: nil, password: contentNode.password)
let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: true, amount: nil, password: contentNode.password)
disposable.set((signal |> deliverOnMainQueue).start(next: { url in
dismissImpl?()
completion(url)

View File

@ -388,7 +388,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-625298705] = { return Api.InputInvoice.parse_inputInvoicePremiumGiftStars($0) }
dict[-1020867857] = { return Api.InputInvoice.parse_inputInvoiceSlug($0) }
dict[-396206446] = { return Api.InputInvoice.parse_inputInvoiceStarGift($0) }
dict[1674298252] = { return Api.InputInvoice.parse_inputInvoiceStarGiftResale($0) }
dict[-1012968668] = { return Api.InputInvoice.parse_inputInvoiceStarGiftResale($0) }
dict[1247763417] = { return Api.InputInvoice.parse_inputInvoiceStarGiftTransfer($0) }
dict[1300335965] = { return Api.InputInvoice.parse_inputInvoiceStarGiftUpgrade($0) }
dict[1710230755] = { return Api.InputInvoice.parse_inputInvoiceStars($0) }
@ -603,7 +603,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) }
dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
dict[1192749220] = { return Api.MessageAction.parse_messageActionStarGift($0) }
dict[775611918] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
dict[888627955] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) }
dict[-293988970] = { return Api.MessageAction.parse_messageActionSuggestedPostApproval($0) }
dict[1777932024] = { return Api.MessageAction.parse_messageActionSuggestedPostRefund($0) }
@ -944,7 +944,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) }
dict[12386139] = { return Api.StarGift.parse_starGift($0) }
dict[-164136786] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[975654224] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }
@ -992,8 +992,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) }
dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) }
dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) }
dict[-1826262950] = { return Api.StoryAlbum.parse_storyAlbum($0) }
dict[-1205411504] = { return Api.StoryFwdHeader.parse_storyFwdHeader($0) }
dict[2041735716] = { return Api.StoryItem.parse_storyItem($0) }
dict[-302947087] = { return Api.StoryItem.parse_storyItem($0) }
dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) }
dict[1620104917] = { return Api.StoryReaction.parse_storyReaction($0) }
@ -1476,6 +1477,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[172975040] = { return Api.storage.FileType.parse_filePng($0) }
dict[-1432995067] = { return Api.storage.FileType.parse_fileUnknown($0) }
dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) }
dict[-1013417414] = { return Api.stories.Albums.parse_albums($0) }
dict[1448008427] = { return Api.stories.Albums.parse_albumsNotModified($0) }
dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) }
dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) }
dict[-1014513586] = { return Api.stories.CanSendStoryCount.parse_canSendStoryCount($0) }
@ -2196,6 +2199,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.StoriesStealthMode:
_1.serialize(buffer, boxed)
case let _1 as Api.StoryAlbum:
_1.serialize(buffer, boxed)
case let _1 as Api.StoryFwdHeader:
_1.serialize(buffer, boxed)
case let _1 as Api.StoryItem:
@ -2614,6 +2619,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.storage.FileType:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.Albums:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.AllStories:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.CanSendStoryCount:

View File

@ -255,7 +255,7 @@ public extension Api {
case inputInvoicePremiumGiftStars(flags: Int32, userId: Api.InputUser, months: Int32, message: Api.TextWithEntities?)
case inputInvoiceSlug(slug: String)
case inputInvoiceStarGift(flags: Int32, peer: Api.InputPeer, giftId: Int64, message: Api.TextWithEntities?)
case inputInvoiceStarGiftResale(slug: String, toId: Api.InputPeer)
case inputInvoiceStarGiftResale(flags: Int32, slug: String, toId: Api.InputPeer)
case inputInvoiceStarGiftTransfer(stargift: Api.InputSavedStarGift, toId: Api.InputPeer)
case inputInvoiceStarGiftUpgrade(flags: Int32, stargift: Api.InputSavedStarGift)
case inputInvoiceStars(purpose: Api.InputStorePaymentPurpose)
@ -313,10 +313,11 @@ public extension Api {
serializeInt64(giftId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)}
break
case .inputInvoiceStarGiftResale(let slug, let toId):
case .inputInvoiceStarGiftResale(let flags, let slug, let toId):
if boxed {
buffer.appendInt32(1674298252)
buffer.appendInt32(-1012968668)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(slug, buffer: buffer, boxed: false)
toId.serialize(buffer, true)
break
@ -359,8 +360,8 @@ public extension Api {
return ("inputInvoiceSlug", [("slug", slug as Any)])
case .inputInvoiceStarGift(let flags, let peer, let giftId, let message):
return ("inputInvoiceStarGift", [("flags", flags as Any), ("peer", peer as Any), ("giftId", giftId as Any), ("message", message as Any)])
case .inputInvoiceStarGiftResale(let slug, let toId):
return ("inputInvoiceStarGiftResale", [("slug", slug as Any), ("toId", toId as Any)])
case .inputInvoiceStarGiftResale(let flags, let slug, let toId):
return ("inputInvoiceStarGiftResale", [("flags", flags as Any), ("slug", slug as Any), ("toId", toId as Any)])
case .inputInvoiceStarGiftTransfer(let stargift, let toId):
return ("inputInvoiceStarGiftTransfer", [("stargift", stargift as Any), ("toId", toId as Any)])
case .inputInvoiceStarGiftUpgrade(let flags, let stargift):
@ -491,16 +492,19 @@ public extension Api {
}
}
public static func parse_inputInvoiceStarGiftResale(_ reader: BufferReader) -> InputInvoice? {
var _1: String?
_1 = parseString(reader)
var _2: Api.InputPeer?
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Api.InputPeer?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputPeer
_3 = Api.parse(reader, signature: signature) as? Api.InputPeer
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputInvoice.inputInvoiceStarGiftResale(slug: _1!, toId: _2!)
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.InputInvoice.inputInvoiceStarGiftResale(flags: _1!, slug: _2!, toId: _3!)
}
else {
return nil

View File

@ -394,7 +394,7 @@ public extension Api {
case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper)
case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?)
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?)
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?)
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleAmount: Api.StarsAmount?, canTransferAt: Int32?, canResellAt: Int32?)
case messageActionSuggestProfilePhoto(photo: Api.Photo)
case messageActionSuggestedPostApproval(flags: Int32, rejectComment: String?, scheduleDate: Int32?, price: Api.StarsAmount?)
case messageActionSuggestedPostRefund(flags: Int32)
@ -797,9 +797,9 @@ public extension Api {
if Int(flags) & Int(1 << 12) != 0 {peer!.serialize(buffer, true)}
if Int(flags) & Int(1 << 12) != 0 {serializeInt64(savedId!, buffer: buffer, boxed: false)}
break
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleStars, let canTransferAt, let canResellAt):
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt):
if boxed {
buffer.appendInt32(775611918)
buffer.appendInt32(888627955)
}
serializeInt32(flags, buffer: buffer, boxed: false)
gift.serialize(buffer, true)
@ -808,7 +808,7 @@ public extension Api {
if Int(flags) & Int(1 << 6) != 0 {fromId!.serialize(buffer, true)}
if Int(flags) & Int(1 << 7) != 0 {peer!.serialize(buffer, true)}
if Int(flags) & Int(1 << 7) != 0 {serializeInt64(savedId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(resaleStars!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {resaleAmount!.serialize(buffer, true)}
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(canTransferAt!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(canResellAt!, buffer: buffer, boxed: false)}
break
@ -993,8 +993,8 @@ public extension Api {
return ("messageActionSetMessagesTTL", [("flags", flags as Any), ("period", period as Any), ("autoSettingFrom", autoSettingFrom as Any)])
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId):
return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any)])
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleStars, let canTransferAt, let canResellAt):
return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleStars", resaleStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)])
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt):
return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleAmount", resaleAmount as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)])
case .messageActionSuggestProfilePhoto(let photo):
return ("messageActionSuggestProfilePhoto", [("photo", photo as Any)])
case .messageActionSuggestedPostApproval(let flags, let rejectComment, let scheduleDate, let price):
@ -1792,8 +1792,10 @@ public extension Api {
} }
var _7: Int64?
if Int(_1!) & Int(1 << 7) != 0 {_7 = reader.readInt64() }
var _8: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_8 = reader.readInt64() }
var _8: Api.StarsAmount?
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.StarsAmount
} }
var _9: Int32?
if Int(_1!) & Int(1 << 9) != 0 {_9 = reader.readInt32() }
var _10: Int32?
@ -1809,7 +1811,7 @@ public extension Api {
let _c9 = (Int(_1!) & Int(1 << 9) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleStars: _8, canTransferAt: _9, canResellAt: _10)
return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleAmount: _8, canTransferAt: _9, canResellAt: _10)
}
else {
return nil

View File

@ -637,7 +637,7 @@ public extension Api {
public extension Api {
enum StarGift: TypeConstructorDescription {
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?)
case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellStars: Int64?, releasedBy: Api.Peer?)
case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -662,9 +662,9 @@ public extension Api {
if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserTotal!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)}
break
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellStars, let releasedBy):
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy):
if boxed {
buffer.appendInt32(-164136786)
buffer.appendInt32(975654224)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -682,7 +682,11 @@ public extension Api {
serializeInt32(availabilityIssued, buffer: buffer, boxed: false)
serializeInt32(availabilityTotal, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 3) != 0 {serializeString(giftAddress!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt64(resellStars!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(resellAmount!.count))
for item in resellAmount! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)}
break
}
@ -692,8 +696,8 @@ public extension Api {
switch self {
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains):
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any)])
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellStars, let releasedBy):
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellStars", resellStars as Any), ("releasedBy", releasedBy as Any)])
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy):
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any)])
}
}
@ -786,8 +790,10 @@ public extension Api {
_11 = reader.readInt32()
var _12: String?
if Int(_1!) & Int(1 << 3) != 0 {_12 = parseString(reader) }
var _13: Int64?
if Int(_1!) & Int(1 << 4) != 0 {_13 = reader.readInt64() }
var _13: [Api.StarsAmount]?
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
_13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsAmount.self)
} }
var _14: Api.Peer?
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
_14 = Api.parse(reader, signature: signature) as? Api.Peer
@ -807,7 +813,7 @@ public extension Api {
let _c13 = (Int(_1!) & Int(1 << 4) == 0) || _13 != nil
let _c14 = (Int(_1!) & Int(1 << 5) == 0) || _14 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!, giftAddress: _12, resellStars: _13, releasedBy: _14)
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!, giftAddress: _12, resellAmount: _13, releasedBy: _14)
}
else {
return nil

View File

@ -324,6 +324,62 @@ public extension Api {
}
}
public extension Api {
enum StoryAlbum: TypeConstructorDescription {
case storyAlbum(flags: Int32, albumId: Int32, title: String, iconPhoto: Api.Photo?, iconVideo: Api.Document?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyAlbum(let flags, let albumId, let title, let iconPhoto, let iconVideo):
if boxed {
buffer.appendInt32(-1826262950)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(albumId, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {iconPhoto!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {iconVideo!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyAlbum(let flags, let albumId, let title, let iconPhoto, let iconVideo):
return ("storyAlbum", [("flags", flags as Any), ("albumId", albumId as Any), ("title", title as Any), ("iconPhoto", iconPhoto as Any), ("iconVideo", iconVideo as Any)])
}
}
public static func parse_storyAlbum(_ reader: BufferReader) -> StoryAlbum? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: Api.Photo?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _5: Api.Document?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Document
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.StoryAlbum.storyAlbum(flags: _1!, albumId: _2!, title: _3!, iconPhoto: _4, iconVideo: _5)
}
else {
return nil
}
}
}
}
public extension Api {
enum StoryFwdHeader: TypeConstructorDescription {
case storyFwdHeader(flags: Int32, from: Api.Peer?, fromName: String?, storyId: Int32?)
@ -376,15 +432,15 @@ public extension Api {
}
public extension Api {
indirect enum StoryItem: TypeConstructorDescription {
case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?)
case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?, albums: [Int32]?)
case storyItemDeleted(id: Int32)
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
if boxed {
buffer.appendInt32(2041735716)
buffer.appendInt32(-302947087)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -411,6 +467,11 @@ public extension Api {
}}
if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)}
if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)}
if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(albums!.count))
for item in albums! {
serializeInt32(item, buffer: buffer, boxed: false)
}}
break
case .storyItemDeleted(let id):
if boxed {
@ -432,8 +493,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)])
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any), ("albums", albums as Any)])
case .storyItemDeleted(let id):
return ("storyItemDeleted", [("id", id as Any)])
case .storyItemSkipped(let flags, let id, let date, let expireDate):
@ -484,6 +545,10 @@ public extension Api {
if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.Reaction
} }
var _14: [Int32]?
if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() {
_14 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -497,8 +562,9 @@ public extension Api {
let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13)
let _c14 = (Int(_1!) & Int(1 << 19) == 0) || _14 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14)
}
else {
return nil

View File

@ -482,6 +482,64 @@ public extension Api.storage {
}
}
public extension Api.stories {
enum Albums: TypeConstructorDescription {
case albums(hash: Int64, albums: [Api.StoryAlbum])
case albumsNotModified
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .albums(let hash, let albums):
if boxed {
buffer.appendInt32(-1013417414)
}
serializeInt64(hash, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(albums.count))
for item in albums {
item.serialize(buffer, true)
}
break
case .albumsNotModified:
if boxed {
buffer.appendInt32(1448008427)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .albums(let hash, let albums):
return ("albums", [("hash", hash as Any), ("albums", albums as Any)])
case .albumsNotModified:
return ("albumsNotModified", [])
}
}
public static func parse_albums(_ reader: BufferReader) -> Albums? {
var _1: Int64?
_1 = reader.readInt64()
var _2: [Api.StoryAlbum]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryAlbum.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.stories.Albums.albums(hash: _1!, albums: _2!)
}
else {
return nil
}
}
public static func parse_albumsNotModified(_ reader: BufferReader) -> Albums? {
return Api.stories.Albums.albumsNotModified
}
}
}
public extension Api.stories {
enum AllStories: TypeConstructorDescription {
case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode)
@ -1428,55 +1486,3 @@ public extension Api.updates {
}
}
public extension Api.updates {
enum State: TypeConstructorDescription {
case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .state(let pts, let qts, let date, let seq, let unreadCount):
if boxed {
buffer.appendInt32(-1519637954)
}
serializeInt32(pts, buffer: buffer, boxed: false)
serializeInt32(qts, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(seq, buffer: buffer, boxed: false)
serializeInt32(unreadCount, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .state(let pts, let qts, let date, let seq, let unreadCount):
return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)])
}
}
public static func parse_state(_ reader: BufferReader) -> State? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
_5 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,55 @@
public extension Api.updates {
enum State: TypeConstructorDescription {
case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .state(let pts, let qts, let date, let seq, let unreadCount):
if boxed {
buffer.appendInt32(-1519637954)
}
serializeInt32(pts, buffer: buffer, boxed: false)
serializeInt32(qts, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(seq, buffer: buffer, boxed: false)
serializeInt32(unreadCount, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .state(let pts, let qts, let date, let seq, let unreadCount):
return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)])
}
}
public static func parse_state(_ reader: BufferReader) -> State? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
_5 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!)
}
else {
return nil
}
}
}
}
public extension Api.upload {
enum CdnFile: TypeConstructorDescription {
case cdnFile(bytes: Buffer)

View File

@ -10058,12 +10058,12 @@ public extension Api.functions.payments {
}
}
public extension Api.functions.payments {
static func updateStarGiftPrice(stargift: Api.InputSavedStarGift, resellStars: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func updateStarGiftPrice(stargift: Api.InputSavedStarGift, resellAmount: Api.StarsAmount) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1001301217)
buffer.appendInt32(-306287413)
stargift.serialize(buffer, true)
serializeInt64(resellStars, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.updateStarGiftPrice", parameters: [("stargift", String(describing: stargift)), ("resellStars", String(describing: resellStars))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
resellAmount.serialize(buffer, true)
return (FunctionDescription(name: "payments.updateStarGiftPrice", parameters: [("stargift", String(describing: stargift)), ("resellAmount", String(describing: resellAmount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -11371,6 +11371,43 @@ public extension Api.functions.stories {
})
}
}
public extension Api.functions.stories {
static func createAlbum(peer: Api.InputPeer, title: String, stories: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StoryAlbum>) {
let buffer = Buffer()
buffer.appendInt32(-1553754395)
peer.serialize(buffer, true)
serializeString(title, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(stories.count))
for item in stories {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "stories.createAlbum", parameters: [("peer", String(describing: peer)), ("title", String(describing: title)), ("stories", String(describing: stories))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StoryAlbum? in
let reader = BufferReader(buffer)
var result: Api.StoryAlbum?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.StoryAlbum
}
return result
})
}
}
public extension Api.functions.stories {
static func deleteAlbum(peer: Api.InputPeer, albumId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-1925949744)
peer.serialize(buffer, true)
serializeInt32(albumId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.deleteAlbum", parameters: [("peer", String(describing: peer)), ("albumId", String(describing: albumId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Bool
}
return result
})
}
}
public extension Api.functions.stories {
static func deleteStories(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) {
let buffer = Buffer()
@ -11441,6 +11478,40 @@ public extension Api.functions.stories {
})
}
}
public extension Api.functions.stories {
static func getAlbumStories(peer: Api.InputPeer, albumId: Int32, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) {
let buffer = Buffer()
buffer.appendInt32(-1400869535)
peer.serialize(buffer, true)
serializeInt32(albumId, buffer: buffer, boxed: false)
serializeInt32(offset, buffer: buffer, boxed: false)
serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.getAlbumStories", parameters: [("peer", String(describing: peer)), ("albumId", String(describing: albumId)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in
let reader = BufferReader(buffer)
var result: Api.stories.Stories?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stories.Stories
}
return result
})
}
}
public extension Api.functions.stories {
static func getAlbums(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Albums>) {
let buffer = Buffer()
buffer.appendInt32(632548039)
peer.serialize(buffer, true)
serializeInt64(hash, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.getAlbums", parameters: [("peer", String(describing: peer)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Albums? in
let reader = BufferReader(buffer)
var result: Api.stories.Albums?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stories.Albums
}
return result
})
}
}
public extension Api.functions.stories {
static func getAllReadPeerStories() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
@ -11671,6 +11742,26 @@ public extension Api.functions.stories {
})
}
}
public extension Api.functions.stories {
static func reorderAlbums(peer: Api.InputPeer, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-2060059687)
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(order.count))
for item in order {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "stories.reorderAlbums", parameters: [("peer", String(describing: peer)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Bool
}
return result
})
}
}
public extension Api.functions.stories {
static func report(peer: Api.InputPeer, id: [Int32], option: Buffer, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ReportResult>) {
let buffer = Buffer()
@ -11732,9 +11823,9 @@ public extension Api.functions.stories {
}
}
public extension Api.functions.stories {
static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?, fwdFromId: Api.InputPeer?, fwdFromStory: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?, fwdFromId: Api.InputPeer?, fwdFromStory: Int32?, albums: [Int32]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-454661813)
buffer.appendInt32(1937752812)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
media.serialize(buffer, true)
@ -11758,7 +11849,12 @@ public extension Api.functions.stories {
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 6) != 0 {fwdFromId!.serialize(buffer, true)}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fwdFromStory!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period)), ("fwdFromId", String(describing: fwdFromId)), ("fwdFromStory", String(describing: fwdFromStory))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
if Int(flags) & Int(1 << 8) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(albums!.count))
for item in albums! {
serializeInt32(item, buffer: buffer, boxed: false)
}}
return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period)), ("fwdFromId", String(describing: fwdFromId)), ("fwdFromStory", String(describing: fwdFromStory)), ("albums", String(describing: albums))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -11840,6 +11936,39 @@ public extension Api.functions.stories {
})
}
}
public extension Api.functions.stories {
static func updateAlbum(flags: Int32, peer: Api.InputPeer, albumId: Int32, title: String?, deleteStories: [Int32]?, addStories: [Int32]?, order: [Int32]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StoryAlbum>) {
let buffer = Buffer()
buffer.appendInt32(1582455222)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(albumId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(deleteStories!.count))
for item in deleteStories! {
serializeInt32(item, buffer: buffer, boxed: false)
}}
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(addStories!.count))
for item in addStories! {
serializeInt32(item, buffer: buffer, boxed: false)
}}
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(order!.count))
for item in order! {
serializeInt32(item, buffer: buffer, boxed: false)
}}
return (FunctionDescription(name: "stories.updateAlbum", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("albumId", String(describing: albumId)), ("title", String(describing: title)), ("deleteStories", String(describing: deleteStories)), ("addStories", String(describing: addStories)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StoryAlbum? in
let reader = BufferReader(buffer)
var result: Api.StoryAlbum?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.StoryAlbum
}
return result
})
}
}
public extension Api.functions.updates {
static func getChannelDifference(flags: Int32, channel: Api.InputChannel, filter: Api.ChannelMessagesFilter, pts: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.updates.ChannelDifference>) {
let buffer = Buffer()

View File

@ -192,11 +192,11 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return nil
}
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId))
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleStars, canTransferDate, canResaleDate):
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleAmount, canTransferDate, canResaleDate):
guard let gift = StarGift(apiStarGift: apiGift) else {
return nil
}
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleStars: resaleStars, canTransferDate: canTransferDate, canResaleDate: canResaleDate))
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate))
case let .messageActionPaidMessagesRefunded(count, stars):
return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars))
case let .messageActionPaidMessagesPrice(flags, stars):

View File

@ -10,6 +10,13 @@ public struct AppUpdateInfo: Equatable {
public let version: String
public let text: String
public let entities: [MessageTextEntity]
public init(blocking: Bool, version: String, text: String, entities: [MessageTextEntity]) {
self.blocking = blocking
self.version = version
self.text = text
self.entities = entities
}
}
extension AppUpdateInfo {

View File

@ -354,7 +354,8 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne
],
availability: StarGift.UniqueGift.Availability(issued: 0, total: 0),
giftAddress: nil,
resellStars: nil,
resellAmounts: nil,
resellForTonOnly: false,
releasedBy: nil
)
if let entry = CodableEntry(RecentStarGiftItem(gift)) {

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 210
return 211
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -244,7 +244,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?)
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?)
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleStars: Int64?, canTransferDate: Int32?, canResaleDate: Int32?)
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?)
case paidMessagesRefunded(count: Int32, stars: Int64)
case paidMessagesPriceEdited(stars: Int64, broadcastMessagesAllowed: Bool)
case conferenceCall(ConferenceCall)
@ -377,7 +377,13 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case 44:
self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"))
case 45:
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleStars: decoder.decodeOptionalInt64ForKey("resaleStars"), canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate"))
var resaleAmount: CurrencyAmount?
if let amount = decoder.decodeCodable(CurrencyAmount.self, forKey: "resaleAmount") {
resaleAmount = amount
} else if let stars = decoder.decodeOptionalInt64ForKey("resaleStars") {
resaleAmount = CurrencyAmount(amount: StarsAmount(value: stars, nanos: 0), currency: .stars)
}
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate"))
case 46:
self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0))
case 47:
@ -745,7 +751,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
} else {
encoder.encodeNil(forKey: "savedId")
}
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, peerId, senderId, savedId, resaleStars, canTransferDate, canResaleDate):
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate):
encoder.encodeInt32(45, forKey: "_rawValue")
encoder.encodeObject(gift, forKey: "gift")
encoder.encodeBool(isUpgrade, forKey: "isUpgrade")
@ -777,10 +783,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
} else {
encoder.encodeNil(forKey: "savedId")
}
if let resaleStars {
encoder.encodeInt64(resaleStars, forKey: "resaleStars")
if let resaleAmount {
encoder.encodeCodable(resaleAmount, forKey: "resaleAmount")
} else {
encoder.encodeNil(forKey: "resaleStars")
encoder.encodeNil(forKey: "resaleAmount")
}
if let canTransferDate {
encoder.encodeInt32(canTransferDate, forKey: "canTransferDate")

View File

@ -1235,7 +1235,8 @@ func _internal_uploadStoryImpl(
randomId: randomId,
period: Int32(period),
fwdFromId: fwdFromId,
fwdFromStory: fwdFromStory
fwdFromStory: fwdFromStory,
albums: nil
))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
@ -1259,7 +1260,7 @@ func _internal_uploadStoryImpl(
for update in updates.allUpdates {
if case let .updateStory(_, story) = update {
switch story {
case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _):
case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _, _):
if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) {
var items = transaction.getStoryItems(peerId: toPeerId)
var updatedItems: [Stories.Item] = []
@ -1648,7 +1649,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng
for update in updates.allUpdates {
if case let .updateStory(_, story) = update {
switch story {
case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _):
case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _, _):
let parsedMedia = textMediaAndExpirationTimerFromApiMedia(media, account.peerId).media
if let parsedMedia = parsedMedia, let originalMedia = originalMedia {
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false, skipPreviews: updatingCoverTime)
@ -2009,7 +2010,7 @@ func _internal_updatePinnedToTopStories(account: Account, peerId: PeerId, ids: [
extension Api.StoryItem {
var id: Int32 {
switch self {
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _):
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _, _):
return id
case let .storyItemDeleted(id):
return id
@ -2072,7 +2073,7 @@ extension Stories.Item.ForwardInfo {
extension Stories.StoredItem {
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
switch apiStoryItem {
case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction):
case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction, _):
let parsedMedia = textMediaAndExpirationTimerFromApiMedia(media, peerId).media
if let parsedMedia = parsedMedia {
var parsedPrivacy: Stories.Item.Privacy?

View File

@ -17,7 +17,7 @@ public enum BotPaymentInvoiceSource {
case starGiftUpgrade(keepOriginalInfo: Bool, reference: StarGiftReference)
case starGiftTransfer(reference: StarGiftReference, toPeerId: EnginePeer.Id)
case premiumGift(peerId: EnginePeer.Id, option: CachedPremiumGiftOption, text: String?, entities: [MessageTextEntity]?)
case starGiftResale(slug: String, toPeerId: EnginePeer.Id)
case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool)
}
public struct BotPaymentInvoiceFields: OptionSet {
@ -180,6 +180,7 @@ public enum BotPaymentFormRequestError {
case noPaymentNeeded
case disallowedStarGift
case starGiftResellTooEarly(Int32)
case starGiftUserLimit
}
extension BotPaymentInvoice {
@ -402,11 +403,15 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv
message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? [])
}
return .inputInvoicePremiumGiftStars(flags: flags, userId: inputUser, months: option.months, message: message)
case let .starGiftResale(slug, toPeerId):
case let .starGiftResale(slug, toPeerId, ton):
guard let peer = transaction.getPeer(toPeerId), let inputPeer = apiInputPeer(peer) else {
return nil
}
return .inputInvoiceStarGiftResale(slug: slug, toId: inputPeer)
var flags: Int32 = 0
if ton {
flags |= 1 << 0
}
return .inputInvoiceStarGiftResale(flags: flags, slug: slug, toId: inputPeer)
}
}
@ -488,6 +493,8 @@ func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, netw
if let value = Int32(timeout) {
return .fail(.starGiftResellTooEarly(value))
}
} else if error.errorDescription == "STARGIFT_USER_USAGE_LIMITED" {
return .fail(.starGiftUserLimit)
}
return .fail(.generic)
}
@ -651,6 +658,7 @@ public enum SendBotPaymentFormError {
case alreadyPaid
case starGiftOutOfStock
case disallowedStarGift
case starGiftUserLimit
}
public enum SendBotPaymentResult {

View File

@ -301,6 +301,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
case availability
case giftAddress
case resellStars
case resellAmounts
case resellForTonOnly
case releasedBy
}
@ -554,10 +556,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
public let attributes: [Attribute]
public let availability: Availability
public let giftAddress: String?
public let resellStars: Int64?
public let resellAmounts: [CurrencyAmount]?
public let resellForTonOnly: Bool
public let releasedBy: EnginePeer.Id?
public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellStars: Int64?, releasedBy: EnginePeer.Id?) {
public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?) {
self.id = id
self.title = title
self.number = number
@ -566,7 +569,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.attributes = attributes
self.availability = availability
self.giftAddress = giftAddress
self.resellStars = resellStars
self.resellAmounts = resellAmounts
self.resellForTonOnly = resellForTonOnly
self.releasedBy = releasedBy
}
@ -588,7 +592,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.attributes = try container.decode([UniqueGift.Attribute].self, forKey: .attributes)
self.availability = try container.decode(UniqueGift.Availability.self, forKey: .availability)
self.giftAddress = try container.decodeIfPresent(String.self, forKey: .giftAddress)
self.resellStars = try container.decodeIfPresent(Int64.self, forKey: .resellStars)
if let resellAmounts = try container.decodeIfPresent([CurrencyAmount].self, forKey: .resellAmounts) {
self.resellAmounts = resellAmounts
} else if let resellStars = try container.decodeIfPresent(Int64.self, forKey: .resellStars) {
self.resellAmounts = [CurrencyAmount(amount: StarsAmount(value: resellStars, nanos: 0), currency: .stars)]
} else {
self.resellAmounts = []
}
self.resellForTonOnly = try container.decodeIfPresent(Bool.self, forKey: .resellForTonOnly) ?? false
self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy)
}
@ -609,7 +620,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.attributes = (try? decoder.decodeObjectArrayWithCustomDecoderForKey(CodingKeys.attributes.rawValue, decoder: { UniqueGift.Attribute(decoder: $0) })) ?? []
self.availability = decoder.decodeObjectForKey(CodingKeys.availability.rawValue, decoder: { UniqueGift.Availability(decoder: $0) }) as! UniqueGift.Availability
self.giftAddress = decoder.decodeOptionalStringForKey(CodingKeys.giftAddress.rawValue)
self.resellStars = decoder.decodeOptionalInt64ForKey(CodingKeys.resellStars.rawValue)
if let resellAmounts = decoder.decodeCodable([CurrencyAmount].self, forKey: CodingKeys.resellAmounts.rawValue) {
self.resellAmounts = resellAmounts
} else if let resellStars = decoder.decodeOptionalInt64ForKey(CodingKeys.resellStars.rawValue) {
self.resellAmounts = [CurrencyAmount(amount: StarsAmount(value: resellStars, nanos: 0), currency: .stars)]
} else {
self.resellAmounts = nil
}
self.resellForTonOnly = decoder.decodeBoolForKey(CodingKeys.resellForTonOnly.rawValue, orElse: false)
self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) }
}
@ -630,7 +648,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
try container.encode(self.attributes, forKey: .attributes)
try container.encode(self.availability, forKey: .availability)
try container.encodeIfPresent(self.giftAddress, forKey: .giftAddress)
try container.encodeIfPresent(self.resellStars, forKey: .resellStars)
try container.encodeIfPresent(self.resellAmounts, forKey: .resellAmounts)
try container.encode(self.resellForTonOnly, forKey: .resellForTonOnly)
try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy)
}
@ -654,11 +673,12 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
} else {
encoder.encodeNil(forKey: CodingKeys.giftAddress.rawValue)
}
if let resellStars = self.resellStars {
encoder.encodeInt64(resellStars, forKey: CodingKeys.resellStars.rawValue)
if let resellAmounts = self.resellAmounts {
encoder.encodeCodable(resellAmounts, forKey: CodingKeys.resellAmounts.rawValue)
} else {
encoder.encodeNil(forKey: CodingKeys.resellStars.rawValue)
encoder.encodeNil(forKey: CodingKeys.resellAmounts.rawValue)
}
encoder.encodeBool(self.resellForTonOnly, forKey: CodingKeys.resellForTonOnly.rawValue)
if let releasedBy = self.releasedBy {
encoder.encodeInt64(releasedBy.toInt64(), forKey: CodingKeys.releasedBy.rawValue)
} else {
@ -666,7 +686,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
}
}
public func withResellStars(_ resellStars: Int64?) -> UniqueGift {
public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift {
return UniqueGift(
id: self.id,
title: self.title,
@ -676,7 +696,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
attributes: self.attributes,
availability: self.availability,
giftAddress: self.giftAddress,
resellStars: resellStars,
resellAmounts: resellAmounts,
resellForTonOnly: self.resellForTonOnly,
releasedBy: self.releasedBy
)
}
@ -787,7 +808,7 @@ extension StarGift {
return nil
}
self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit))
case let .starGiftUnique(_, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellStars, releasedBy):
case let .starGiftUnique(flags, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy):
let owner: StarGift.UniqueGift.Owner
if let ownerAddress {
owner = .address(ownerAddress)
@ -798,7 +819,8 @@ extension StarGift {
} else {
return nil
}
self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellStars: resellStars, releasedBy: releasedBy?.peerId))
let resellAmounts = resellAmounts?.compactMap { CurrencyAmount(apiAmount: $0) }
self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (flags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId))
}
}
}
@ -948,8 +970,8 @@ public enum UpgradeStarGiftError {
case generic
}
func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId)
func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal<Never, BuyStarGiftError> {
let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId, ton: price?.currency == .ton)
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|> map(Optional.init)
|> `catch` { error -> Signal<BotPaymentForm?, BuyStarGiftError> in
@ -960,7 +982,7 @@ func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id
}
|> mapToSignal { paymentForm in
if let paymentForm {
if let paymentPrice = paymentForm.invoice.prices.first?.amount, let price, paymentPrice > price {
if let paymentPrice = paymentForm.invoice.prices.first?.amount, let price, paymentPrice > price.amount.value {
return .fail(.priceChanged(paymentPrice))
}
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
@ -1594,15 +1616,15 @@ private final class ProfileGiftsContextImpl {
return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId)
}
func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
var listingPrice: Int64?
func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal<Never, BuyStarGiftError> {
var listingPrice: CurrencyAmount?
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
listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })
}
if listingPrice == nil {
@ -1612,7 +1634,7 @@ private final class ProfileGiftsContextImpl {
}
return false
}), case let .unique(uniqueGift) = gift.gift {
listingPrice = uniqueGift.resellStars
listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })
}
}
@ -1776,7 +1798,7 @@ private final class ProfileGiftsContextImpl {
}
}
func updateStarGiftResellPrice(reference: StarGiftReference, price: Int64?, id: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
func updateStarGiftResellPrice(reference: StarGiftReference, price: CurrencyAmount?, id: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
return Signal { [weak self] subscriber in
guard let self else {
return EmptyDisposable
@ -1806,7 +1828,7 @@ private final class ProfileGiftsContextImpl {
return false
}) {
if case let .unique(uniqueGift) = self.gifts[index].gift {
let updatedUniqueGift = uniqueGift.withResellStars(price)
let updatedUniqueGift = uniqueGift.withResellAmounts(price.flatMap { [$0] })
let updatedGift = self.gifts[index].withGift(.unique(updatedUniqueGift))
self.gifts[index] = updatedGift
}
@ -1829,7 +1851,7 @@ private final class ProfileGiftsContextImpl {
return false
}) {
if case let .unique(uniqueGift) = self.filteredGifts[index].gift {
let updatedUniqueGift = uniqueGift.withResellStars(price)
let updatedUniqueGift = uniqueGift.withResellAmounts(price.flatMap { [$0] })
let updatedGift = self.filteredGifts[index].withGift(.unique(updatedUniqueGift))
self.filteredGifts[index] = updatedGift
}
@ -2143,6 +2165,28 @@ public final class ProfileGiftsContext {
collectionIds: self.collectionIds
)
}
public func withCollectionIds(_ collectionIds: [Int32]?) -> StarGift {
return StarGift(
gift: self.gift,
reference: self.reference,
fromPeer: self.fromPeer,
date: self.date,
text: self.text,
entities: self.entities,
nameHidden: self.nameHidden,
savedToProfile: self.savedToProfile,
pinnedToTop: self.pinnedToTop,
convertStars: self.convertStars,
canUpgrade: self.canUpgrade,
canExportDate: self.canExportDate,
upgradeStars: self.upgradeStars,
transferStars: self.transferStars,
canTransferDate: self.canTransferDate,
canResaleDate: self.canResaleDate,
collectionIds: collectionIds
)
}
}
public enum DataState: Equatable {
@ -2232,7 +2276,7 @@ public final class ProfileGiftsContext {
}
}
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal<Never, BuyStarGiftError> {
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount? = nil) -> Signal<Never, BuyStarGiftError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
@ -2300,7 +2344,7 @@ public final class ProfileGiftsContext {
}
}
public func updateStarGiftResellPrice(reference: StarGiftReference, price: Int64?, id: Int64? = nil) -> Signal<Never, UpdateStarGiftPriceError> {
public func updateStarGiftResellPrice(reference: StarGiftReference, price: CurrencyAmount?, id: Int64? = nil) -> Signal<Never, UpdateStarGiftPriceError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
@ -2646,7 +2690,7 @@ func _internal_toggleStarGiftsNotifications(account: Account, peerId: EnginePeer
}
}
func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftReference, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftReference, price: CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError> {
return account.postbox.transaction { transaction in
return reference.apiStarGiftReference(transaction: transaction)
}
@ -2655,7 +2699,8 @@ func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftRe
guard let starGift else {
return .complete()
}
return account.network.request(Api.functions.payments.updateStarGiftPrice(stargift: starGift, resellStars: price ?? 0))
let apiAmount = (price ?? CurrencyAmount(amount: .zero, currency: .stars)).apiAmount
return account.network.request(Api.functions.payments.updateStarGiftPrice(stargift: starGift, resellAmount: apiAmount))
|> mapError { error -> UpdateStarGiftPriceError in
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)...])
@ -2818,7 +2863,7 @@ private final class ResaleGiftsContextImpl {
var mappedGifts: [StarGift] = []
for gift in gifts {
if let mappedGift = StarGift(apiStarGift: gift), case let .unique(uniqueGift) = mappedGift, let resellStars = uniqueGift.resellStars, resellStars > 0 {
if let mappedGift = StarGift(apiStarGift: gift), case let .unique(uniqueGift) = mappedGift, let resellAmount = uniqueGift.resellAmounts?.first, resellAmount.amount.value > 0 {
mappedGifts.append(mappedGift)
}
}
@ -2882,15 +2927,15 @@ private final class ResaleGiftsContextImpl {
self.loadMore()
}
func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
var listingPrice: Int64?
func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal<Never, BuyStarGiftError> {
var listingPrice: CurrencyAmount?
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
listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })
}
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price ?? listingPrice)
@ -2913,7 +2958,7 @@ private final class ResaleGiftsContextImpl {
}
}
func updateStarGiftResellPrice(slug: String, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
func updateStarGiftResellPrice(slug: String, price: CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError> {
return Signal { [weak self] subscriber in
guard let self else {
return EmptyDisposable
@ -2936,7 +2981,7 @@ private final class ResaleGiftsContextImpl {
}) {
if let price {
if case let .unique(uniqueGift) = self.gifts[index] {
self.gifts[index] = .unique(uniqueGift.withResellStars(price))
self.gifts[index] = .unique(uniqueGift.withResellAmounts([price]))
}
} else {
self.gifts.remove(at: index)
@ -3040,7 +3085,7 @@ public final class ResaleGiftsContext {
}
}
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal<Never, BuyStarGiftError> {
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount? = nil) -> Signal<Never, BuyStarGiftError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
@ -3054,7 +3099,7 @@ public final class ResaleGiftsContext {
}
}
public func updateStarGiftResellPrice(slug: String, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
public func updateStarGiftResellPrice(slug: String, price: CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in

View File

@ -190,10 +190,15 @@ private func _internal_reorderStarGiftCollections(account: Account, peerId: Engi
}
}
private func _internal_updateStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32, giftsContext: ProfileGiftsContext?, actions: [ProfileGiftsCollectionsContext.UpdateAction]) -> Signal<StarGiftCollection?, NoError> {
private func _internal_updateStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32, giftsContext: ProfileGiftsContext?, allGiftsContext: ProfileGiftsContext?, actions: [ProfileGiftsCollectionsContext.UpdateAction]) -> Signal<StarGiftCollection?, NoError> {
for action in actions {
switch action {
case let .addGifts(gifts):
let gifts = gifts.map { gift in
var collectionIds = gift.collectionIds ?? []
collectionIds.append(collectionId)
return gift.withCollectionIds(collectionIds)
}
giftsContext?.insertStarGifts(gifts: gifts)
case let .removeGifts(gifts):
giftsContext?.removeStarGifts(references: gifts)
@ -294,6 +299,7 @@ public final class ProfileGiftsCollectionsContext {
private let queue: Queue = .mainQueue()
private let account: Account
private let peerId: EnginePeer.Id
private weak var allGiftsContext: ProfileGiftsContext?
private let disposable = MetaDisposable()
@ -306,9 +312,10 @@ public final class ProfileGiftsCollectionsContext {
return self.stateValue.get()
}
public init(account: Account, peerId: EnginePeer.Id) {
public init(account: Account, peerId: EnginePeer.Id, allGiftsContext: ProfileGiftsContext?) {
self.account = account
self.peerId = peerId
self.allGiftsContext = allGiftsContext
self.reload()
}
@ -362,7 +369,7 @@ public final class ProfileGiftsCollectionsContext {
public func updateCollection(id: Int32, actions: [UpdateAction]) -> Signal<StarGiftCollection?, NoError> {
let giftsContext = self.giftsContextForCollection(id: id)
return _internal_updateStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id, giftsContext: giftsContext, actions: actions)
return _internal_updateStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id, giftsContext: giftsContext, allGiftsContext: self.allGiftsContext, actions: actions)
|> deliverOn(self.queue)
|> afterNext { [weak self] collection in
guard let self else {

View File

@ -129,7 +129,7 @@ public extension TelegramEngine {
return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId)
}
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal<Never, BuyStarGiftError> {
public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal<Never, BuyStarGiftError> {
return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price)
}
@ -157,7 +157,7 @@ public extension TelegramEngine {
return _internal_toggleStarGiftsNotifications(account: self.account, peerId: peerId, enabled: enabled)
}
public func updateStarGiftResalePrice(reference: StarGiftReference, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
public func updateStarGiftResalePrice(reference: StarGiftReference, price: CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError> {
return _internal_updateStarGiftResalePrice(account: self.account, reference: reference, price: price)
}

View File

@ -1204,7 +1204,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = NSAttributedString(string: strings.Notification_StarsGift_SentSomeone, font: titleFont, textColor: primaryTextColor)
} else if message.author?.id == accountPeerId {
if let resaleStars {
let starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars))
let starsString: String
switch resaleStars.currency {
case .stars:
starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value))
case .ton:
//TODO:localize
starsString = "\(Int32(resaleStars.amount.value)) TON"
}
if message.id.peerId == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_BoughtForYouself(starsString)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
} else {
@ -1237,7 +1244,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} else {
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
if let resaleStars {
let starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars))
let starsString: String
switch resaleStars.currency {
case .stars:
starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value))
case .ton:
//TODO:localize
starsString = "\(Int32(resaleStars.amount.value)) TON"
}
let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))"
attributes[1] = boldAttributes
attributes[2] = boldAttributes

View File

@ -398,72 +398,92 @@ func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor
public func presentAgeVerification(context: AccountContext, parentController: ViewController, completion: @escaping () -> Void) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.ApplicationSpecificPreference(key: ApplicationSpecificPreferencesKeys.ageVerificationState)
) |> deliverOnMainQueue).start(next: { [weak parentController] ageVerificationStatePreference in
let state = ageVerificationStatePreference?.get(AgeVerificationState.self) ?? AgeVerificationState.default
if state.verificationPassed {
completion()
} else {
let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in
if check {
let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { [weak parentController] passed in
if passed {
let _ = updateAgeVerificationState(engine: context.engine, { _ in
return AgeVerificationState(verificationPassed: passed)
}).start()
completion()
let navigationController = parentController?.navigationController
Queue.mainQueue().after(2.0) {
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AgeVerification_Success_Title, text: presentationData.strings.AgeVerification_Success_Text, cancel: nil, destructive: false), action: { _ in return true })
(navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
}
} else {
let controller = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: presentationData.strings.AgeVerification_Fail_Title, text: presentationData.strings.AgeVerification_Fail_Text, customUndoText: nil, timeout: nil), action: { _ in return true })
parentController?.present(controller, in: .window(.root))
}
})
parentController?.push(scanScreen)
let _ = (contentSettingsConfiguration(network: context.account.network)
|> deliverOnMainQueue).start(next: { [weak parentController] settings in
if !settings.canAdjustSensitiveContent {
let alertController = textAlertController(
context: context,
title: presentationData.strings.AgeVerification_Unavailable_Title,
text: presentationData.strings.AgeVerification_Unavailable_Text,
actions: []
)
parentController?.present(alertController, in: .window(.root))
return
}
let miniappPromise = Promise<EnginePeer?>(nil)
var useVerifyAgeBot = false
if let value = context.currentAppConfiguration.with({ $0 }).data?["force_verify_age_bot"] as? Bool, value {
useVerifyAgeBot = value
}
if useVerifyAgeBot, let verifyAgeBotUsername = context.currentAppConfiguration.with({ $0 }).data?["verify_age_bot_username"] as? String {
miniappPromise.set(context.engine.peers.resolvePeerByName(name: verifyAgeBotUsername, referrer: nil)
|> mapToSignal { result in
if case let .result(peer) = result {
return .single(peer)
}
return .complete()
})
parentController?.push(infoScreen)
}
let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in
if check {
var requiredAge = 18
if let value = context.currentAppConfiguration.with({ $0 }).data?["verify_age_min"] as? Double {
requiredAge = Int(value)
}
let success = { [weak parentController] in
completion()
let navigationController = parentController?.navigationController
Queue.mainQueue().after(2.0) {
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AgeVerification_Success_Title, text: presentationData.strings.AgeVerification_Success_Text, cancel: nil, destructive: false), action: { _ in return true })
(navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .current)
}
}
let failure = { [weak parentController] in
let controller = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: presentationData.strings.AgeVerification_Fail_Title, text: presentationData.strings.AgeVerification_Fail_Text, customUndoText: nil, timeout: nil), action: { _ in return true })
parentController?.present(controller, in: .current)
}
let _ = (miniappPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
if let peer, let parentController {
context.sharedContext.openWebApp(
context: context,
parentController: parentController,
updatedPresentationData: nil,
botPeer: peer,
chatPeer: nil,
threadId: nil,
buttonText: "",
url: "",
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil,
verifyAgeCompletion: { age in
if age >= requiredAge {
success()
} else {
failure()
}
}
)
} else {
let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { age in
if age >= requiredAge {
success()
} else {
failure()
}
})
parentController?.push(scanScreen)
}
})
}
})
parentController?.push(infoScreen)
})
}
public func updateAgeVerificationState(engine: TelegramEngine, _ f: @escaping (AgeVerificationState) -> AgeVerificationState) -> Signal<Never, NoError> {
return engine.preferences.update(id: ApplicationSpecificPreferencesKeys.ageVerificationState, { entry in
let currentSettings: AgeVerificationState
if let entry = entry?.get(AgeVerificationState.self) {
currentSettings = entry
} else {
currentSettings = .default
}
return SharedPreferencesEntry(f(currentSettings))
})
}
public struct AgeVerificationState: Equatable, Codable {
public var verificationPassed: Bool
public static var `default`: AgeVerificationState {
return AgeVerificationState(verificationPassed: false)
}
public init(verificationPassed: Bool) {
self.verificationPassed = verificationPassed
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.verificationPassed = (try container.decode(Int32.self, forKey: "verificationPassed")) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.verificationPassed ? 1 : 0) as Int32, forKey: "verificationPassed")
}
}

View File

@ -20,8 +20,6 @@ import ZipArchive
import PlainButtonComponent
import MultilineTextComponent
private let requiredAge = 18
final class FaceScanScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -109,7 +107,7 @@ final class FaceScanScreenComponent: Component {
self.backgroundColor = .black
self.previewLayer.backgroundColor = UIColor.red.cgColor
//self.previewLayer.backgroundColor = UIColor.red.cgColor
self.previewLayer.videoGravity = .resizeAspectFill
self.layer.addSublayer(previewLayer)
@ -240,7 +238,7 @@ final class FaceScanScreenComponent: Component {
let targetCenter = CGPoint(x: 0.5, y: 0.5)
let distance = sqrt(pow(faceCenter.x - targetCenter.x, 2) + pow(faceCenter.y - targetCenter.y, 2))
if distance < 0.35 {
if distance < 0.24 {
switch processState {
case .waitingForFace:
self.processState = .positioning
@ -320,7 +318,7 @@ final class FaceScanScreenComponent: Component {
if !self.ages.isEmpty {
let averageAge = self.ages.reduce(0, +) / Double(self.ages.count)
if let environment = self.environment, let controller = environment.controller() as? FaceScanScreen {
controller.completion(averageAge >= Double(requiredAge))
controller.completion(Int(averageAge))
controller.dismiss(animated: true)
}
} else {
@ -436,7 +434,7 @@ final class FaceScanScreenComponent: Component {
let center = CGPoint(x: availableSize.width / 2, y: environment.statusBarHeight + 10.0 + widthRadius * 1.3)
var previewScale = 1.0
var previewScale = 0.85
if self.processState == .tracking || self.processState == .readyToStart || self.processState == .completed || self.transitioningToViewFinder {
let circlePath = CGPath(roundedRect: CGRect(x: center.x - radius, y: center.y - radius, width: radius * 2, height: radius * 2), cornerWidth: radius, cornerHeight: radius, transform: nil)
path.addPath(circlePath)
@ -457,7 +455,6 @@ final class FaceScanScreenComponent: Component {
self.frameView.frame = frameViewFrame
self.frameView.update(size: frameViewFrame.size)
//TODO:localize
var instructionString = environment.strings.FaceScan_Instruction_Position
switch self.processState {
case .waitingForFace, .positioning:
@ -545,12 +542,12 @@ final class FaceScanScreenComponent: Component {
public final class FaceScanScreen: ViewControllerComponentContainer {
private let context: AccountContext
fileprivate let completion: (Bool) -> Void
fileprivate let completion: (Int) -> Void
public init(
context: AccountContext,
availability: Signal<AgeVerificationAvailability, NoError>,
completion: @escaping (Bool) -> Void
completion: @escaping (Int) -> Void
) {
self.context = context
self.completion = completion

View File

@ -363,6 +363,8 @@ final class GiftSetupScreenComponent: Component {
let entities = generateChatInputTextEntities(self.textInputState.text)
var finalPrice: Int64
var perUserLimit: Int32?
var giftFile: TelegramMediaFile?
let source: BotPaymentInvoiceSource
switch component.subject {
case let .premium(product):
@ -377,6 +379,8 @@ final class GiftSetupScreenComponent: Component {
if self.includeUpgrade, let upgradeStars = starGift.upgradeStars {
finalPrice += upgradeStars
}
perUserLimit = starGift.perUserLimit?.total
giftFile = starGift.file
source = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities)
}
@ -395,6 +399,8 @@ final class GiftSetupScreenComponent: Component {
switch error {
case .disallowedStarGifts:
return .fail(.disallowedStarGift)
case .starGiftsUserLimit:
return .fail(.starGiftUserLimit)
default:
return .fail(.generic)
}
@ -468,6 +474,14 @@ final class GiftSetupScreenComponent: Component {
var errorText: String?
switch error {
case .starGiftUserLimit:
if let perUserLimit, let giftFile {
let text = presentationData.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit)
let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: giftFile, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false })
controller.present(undoController, in: .current)
return
}
return
case .starGiftOutOfStock:
errorText = presentationData.strings.Gift_Send_ErrorOutOfStock
case .disallowedStarGift:

View File

@ -233,7 +233,8 @@ final class GiftStoreScreenComponent: Component {
color: ribbonColor
)
let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "# \(presentationStringsFormattedNumber(Int32(uniqueGift.resellStars ?? 0), environment.dateTimeFormat.groupingSeparator))")
//TODO:release
let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "# \(presentationStringsFormattedNumber(Int32(uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })?.amount.value ?? 0), environment.dateTimeFormat.groupingSeparator))")
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(

View File

@ -176,8 +176,8 @@ private final class GiftViewSheetContent: CombinedComponent {
}
}
if let _ = arguments.resellStars {
self.buyFormDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftResale(slug: gift.slug, toPeerId: context.account.peerId), themeParams: nil)
if let _ = arguments.resellAmounts {
self.buyFormDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftResale(slug: gift.slug, toPeerId: context.account.peerId, ton: false), themeParams: nil)
|> deliverOnMainQueue).start(next: { [weak self] paymentForm in
guard let self else {
return
@ -288,11 +288,14 @@ private final class GiftViewSheetContent: CombinedComponent {
}
var minRequiredAmount = StarsAmount(value: 100, nanos: 0)
if let resellStars = self.subject.arguments?.resellStars {
minRequiredAmount = StarsAmount(value: resellStars, nanos: 0)
var canUpgrade = false
if let resellStars = self.subject.arguments?.resellAmounts?.first(where: { $0.currency == .stars }) {
minRequiredAmount = resellStars.amount
} else if let arguments = self.subject.arguments, arguments.canUpgrade && arguments.upgradeStars == nil {
canUpgrade = true
}
if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < minRequiredAmount {
if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < minRequiredAmount || canUpgrade {
self.optionsDisposable = (context.engine.payments.starsTopUpOptions()
|> deliverOnMainQueue).start(next: { [weak self] options in
guard let self else {
@ -813,7 +816,7 @@ private final class GiftViewSheetContent: CombinedComponent {
let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))"
let reference = arguments.reference ?? .slug(slug: gift.slug)
if let resellStars = gift.resellStars, resellStars > 0, !update {
if let resellStars = gift.resellAmounts?.first, resellStars.amount.value > 0, !update {
let alertController = textAlertController(
context: context,
title: presentationData.strings.Gift_View_Resale_Unlist_Title,
@ -832,9 +835,9 @@ private final class GiftViewSheetContent: CombinedComponent {
}
switch self.subject {
case let .profileGift(peerId, currentSubject):
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellStars(nil))))
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts(nil))))
case let .uniqueGift(_, recipientPeerId):
self.subject = .uniqueGift(gift.withResellStars(nil), recipientPeerId)
self.subject = .uniqueGift(gift.withResellAmounts(nil), recipientPeerId)
default:
break
}
@ -873,6 +876,8 @@ private final class GiftViewSheetContent: CombinedComponent {
return
}
let price = CurrencyAmount(amount: StarsAmount(value: price, nanos: 0), currency: .stars)
let _ = ((controller.updateResellStars?(price) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: price))
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak controller] error in
guard let self else {
@ -908,9 +913,9 @@ private final class GiftViewSheetContent: CombinedComponent {
switch self.subject {
case let .profileGift(peerId, currentSubject):
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellStars(price))))
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts([price]))))
case let .uniqueGift(_, recipientPeerId):
self.subject = .uniqueGift(gift.withResellStars(price), recipientPeerId)
self.subject = .uniqueGift(gift.withResellAmounts([price]), recipientPeerId)
default:
break
}
@ -918,7 +923,7 @@ private final class GiftViewSheetContent: CombinedComponent {
var text = presentationData.strings.Gift_View_Resale_List_Success(giftTitle).string
if update {
let starsString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(price))
let starsString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(price.amount.value))
text = presentationData.strings.Gift_View_Resale_Relist_Success(giftTitle, starsString).string
}
@ -1019,7 +1024,7 @@ private final class GiftViewSheetContent: CombinedComponent {
})))
}
if case let .unique(gift) = arguments.gift, let resellStars = gift.resellStars, resellStars > 0 {
if case let .unique(gift) = arguments.gift, let resellAmount = gift.resellAmounts?.first, resellAmount.amount.value > 0 {
if arguments.reference != nil || gift.owner.peerId == context.account.peerId {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ChangePrice, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PriceTag"), color: theme.contextMenu.primaryColor)
@ -1063,7 +1068,7 @@ private final class GiftViewSheetContent: CombinedComponent {
}
}
if let _ = arguments.resellStars, case let .uniqueGift(uniqueGift, recipientPeerId) = subject, let _ = recipientPeerId {
if let _ = arguments.resellAmounts, case let .uniqueGift(uniqueGift, recipientPeerId) = subject, let _ = recipientPeerId {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ViewInProfile, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/ShowIcon"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in
@ -1160,8 +1165,8 @@ private final class GiftViewSheetContent: CombinedComponent {
}
}
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 {
func commitBuy(acceptedPrice: CurrencyAmount? = nil, skipConfirmation: Bool = false) {
guard let resellAmount = self.subject.arguments?.resellAmounts?.first, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, case let .unique(uniqueGift) = self.subject.arguments?.gift else {
return
}
@ -1198,7 +1203,7 @@ private final class GiftViewSheetContent: CombinedComponent {
self.inProgress = true
self.updated()
let buyGiftImpl: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)
let buyGiftImpl: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)
if let buyGift = controller.buyGift {
buyGiftImpl = { slug, peerId, price in
return buyGift(slug, peerId, price)
@ -1215,7 +1220,7 @@ private final class GiftViewSheetContent: CombinedComponent {
}
}
self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId, acceptedPrice ?? resellStars)
self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId, acceptedPrice ?? resellAmount)
|> deliverOnMainQueue).start(
error: { [weak self] error in
guard let self, let controller = self.getController() else {
@ -1227,8 +1232,9 @@ private final class GiftViewSheetContent: CombinedComponent {
switch error {
case let .priceChanged(newPrice):
//TODO:release
let errorTitle = presentationData.strings.Gift_Buy_ErrorPriceChanged_Title
let originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellStars))
let originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellAmount.amount.value))
let newPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(newPrice))
let errorText = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text(originalPriceString, newPriceString).string
@ -1241,7 +1247,8 @@ private final class GiftViewSheetContent: CombinedComponent {
guard let self else {
return
}
self.commitBuy(acceptedPrice: newPrice, skipConfirmation: true)
//TODO:release
self.commitBuy(acceptedPrice: CurrencyAmount(amount: StarsAmount(value: newPrice, nanos: 0), currency: .ton), skipConfirmation: true)
}),
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
})
@ -1395,7 +1402,8 @@ private final class GiftViewSheetContent: CombinedComponent {
return
}
let text: String
let starsString = presentationData.strings.Gift_Buy_Confirm_Text_Stars(Int32(resellStars))
//TODO:release
let starsString = presentationData.strings.Gift_Buy_Confirm_Text_Stars(Int32(resellAmount.amount.value))
if recipientPeerId == self.context.account.peerId {
text = presentationData.strings.Gift_Buy_Confirm_Text(giftTitle, starsString).string
@ -1407,7 +1415,7 @@ private final class GiftViewSheetContent: CombinedComponent {
title: presentationData.strings.Gift_Buy_Confirm_Title,
text: text,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(resellStars)), action: {
TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(resellAmount.amount.value)), action: {
action()
}),
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
@ -2763,8 +2771,8 @@ private final class GiftViewSheetContent: CombinedComponent {
component: PlainButtonComponent(
content: AnyComponent(
HeaderButtonComponent(
title: uniqueGift.resellStars == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist,
iconName: uniqueGift.resellStars == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist"
title: uniqueGift.resellAmounts == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist,
iconName: uniqueGift.resellAmounts == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist"
)
),
effectAlignment: .center,
@ -3128,17 +3136,18 @@ private final class GiftViewSheetContent: CombinedComponent {
originY += table.size.height + 23.0
}
var resellStars: Int64?
var resellStars: CurrencyAmount?
var selling = false
if let uniqueGift {
resellStars = uniqueGift.resellStars
//TODO:release
resellStars = uniqueGift.resellAmounts?.first(where: { $0.currency == .ton })
if let resellStars {
if incoming || ownerPeerId == component.context.account.peerId {
let priceButton = priceButton.update(
component: PlainButtonComponent(
content: AnyComponent(
PriceButtonComponent(price: presentationStringsFormattedNumber(Int32(resellStars), environment.dateTimeFormat.groupingSeparator))
PriceButtonComponent(price: presentationStringsFormattedNumber(Int32(resellStars.amount.value), environment.dateTimeFormat.groupingSeparator))
),
effectAlignment: .center,
action: { [weak state] in
@ -3473,10 +3482,11 @@ private final class GiftViewSheetContent: CombinedComponent {
if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme {
state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: theme.list.itemCheckColors.foregroundColor)!, theme)
}
var upgradeString = strings.Gift_View_BuyFor
upgradeString += " # \(presentationStringsFormattedNumber(Int32(resellStars), environment.dateTimeFormat.groupingSeparator))"
var buyString = strings.Gift_View_BuyFor
//TODO:release
buyString += " # \(presentationStringsFormattedNumber(Int32(resellStars.amount.value), environment.dateTimeFormat.groupingSeparator))"
let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : upgradeString
let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : buyString
let buttonAttributedString = NSMutableAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
if let range = buttonAttributedString.string.range(of: "#"), let starImage = state.cachedStarImage?.0 {
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
@ -3656,7 +3666,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
case upgradePreview([StarGift.UniqueGift.Attribute], String)
case wearPreview(StarGift.UniqueGift)
var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellStars: Int64?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?)? {
var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?)? {
switch self {
case let .message(message):
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
@ -3689,27 +3699,27 @@ public class GiftViewScreen: ViewControllerComponentContainer {
incoming = message.flags.contains(.Incoming)
}
var resellStars: Int64?
var resellAmounts: [CurrencyAmount]?
if case let .unique(uniqueGift) = gift {
resellStars = uniqueGift.resellStars
resellAmounts = uniqueGift.resellAmounts
}
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellStars, canExportDate, nil, canTransferDate, canResaleDate)
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate)
default:
return nil
}
}
case let .uniqueGift(gift, _), let .wearPreview(gift):
return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellStars, nil, nil, nil, nil)
return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil)
case let .profileGift(peerId, gift):
var messageId: EngineMessage.Id?
if case let .message(messageIdValue) = gift.reference {
messageId = messageIdValue
}
var resellStars: Int64?
var resellAmounts: [CurrencyAmount]?
if case let .unique(uniqueGift) = gift.gift {
resellStars = uniqueGift.resellStars
resellAmounts = uniqueGift.resellAmounts
}
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellStars, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate)
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate)
case .soldOutGift:
return nil
case .upgradePreview:
@ -3733,8 +3743,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
fileprivate let convertToStars: (() -> Void)?
fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)?
fileprivate let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
fileprivate let buyGift: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)?
fileprivate let updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)?
fileprivate let buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)?
fileprivate let updateResellStars: ((CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)?
fileprivate let togglePinnedToTop: ((Bool) -> Bool)?
fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)?
@ -3750,8 +3760,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
convertToStars: (() -> Void)? = nil,
transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? = nil,
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil,
buyGift: ((String, EnginePeer.Id, Int64?) -> Signal<Never, BuyStarGiftError>)? = nil,
updateResellStars: ((Int64?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil,
buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)? = nil,
updateResellStars: ((CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil,
togglePinnedToTop: ((Bool) -> Bool)? = nil,
shareStory: ((StarGift.UniqueGift) -> Void)? = nil
) {
@ -3810,7 +3820,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
self.view.disablesInteractiveModalDismiss = true
if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellStars {
if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellAmounts {
if case let .unique(uniqueGift) = arguments.gift, case .peerId(self.context.account.peerId) = uniqueGift.owner {
} else {
self.showBalance = true

View File

@ -3729,7 +3729,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
}
} else if case let .gift(gift) = subject {
isGift = true
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil, resaleStars: nil, canTransferDate: nil, canResaleDate: nil))]
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil))]
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: Namespaces.Message.Cloud, id: -1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
messages = .single([message])
} else {

View File

@ -1257,7 +1257,8 @@ final class AffiliateProgramSetupScreenComponent: Component {
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
} else if let navigationController = controller.navigationController as? NavigationController {
component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(item.peer), subject: nil, keepStack: .always, animated: true, pushController: { [weak navigationController] chatController, animated, completion in

View File

@ -1082,7 +1082,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if case .user = kind {
if isMyProfile || userPeerId != context.account.peerId {
profileGiftsContext = existingProfileGiftsContext ?? ProfileGiftsContext(account: context.account, peerId: userPeerId)
profileGiftsCollectionsContext = existingProfileGiftsCollectionsContext ?? ProfileGiftsCollectionsContext(account: context.account, peerId: userPeerId)
profileGiftsCollectionsContext = existingProfileGiftsCollectionsContext ?? ProfileGiftsCollectionsContext(account: context.account, peerId: userPeerId, allGiftsContext: profileGiftsContext)
} else {
profileGiftsContext = nil
profileGiftsCollectionsContext = nil
@ -1629,7 +1629,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
}
let profileGiftsContext = ProfileGiftsContext(account: context.account, peerId: peerId)
let profileGiftsCollectionsContext = ProfileGiftsCollectionsContext(account: context.account, peerId: peerId)
let profileGiftsCollectionsContext = ProfileGiftsCollectionsContext(account: context.account, peerId: peerId, allGiftsContext: profileGiftsContext)
let personalChannel = peerInfoPersonalOrLinkedChannel(context: context, peerId: peerId, isSettings: false)

View File

@ -1451,7 +1451,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil
payload: nil,
verifyAgeCompletion: nil
)
})
}
@ -11363,7 +11364,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let canReorderEquals = lhs.2 == rhs.2
return filterEquals && sortingEquals && canReorderEquals
})
|> map { [weak self, weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in
|> map { [weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in
var items: [ContextMenuItem] = []
if hasVisibility {
@ -11388,13 +11389,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.default)
//TODO:release
self?.openShareLink(url: "https://t.me/")
})))
// items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
// }, action: { [weak self] _, f in
// f(.default)
// self?.openShareLink(url: "https://t.me/")
// })))
}
if canReorder {
@ -11426,15 +11426,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
items.append(.separator)
}
items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor)
}, action: { [weak giftsContext] _, f in
f(.default)
giftsContext?.updateSorting(sorting == .date ? .value : .date)
})))
if let pane, case .all = pane.currentCollection {
items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor)
}, action: { [weak giftsContext] _, f in
f(.default)
giftsContext?.updateSorting(sorting == .date ? .value : .date)
})))
items.append(.separator)
items.append(.separator)
}
let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in
var updatedFilter = filter

View File

@ -23,17 +23,20 @@ final class AddGiftsScreenComponent: Component {
let context: AccountContext
let peerId: EnginePeer.Id
let collectionId: Int32
let remainingCount: Int32
let profileGifts: ProfileGiftsContext
init(
context: AccountContext,
peerId: EnginePeer.Id,
collectionId: Int32,
remainingCount: Int32,
profileGifts: ProfileGiftsContext
) {
self.context = context
self.peerId = peerId
self.collectionId = collectionId
self.remainingCount = remainingCount
self.profileGifts = profileGifts
}
@ -98,10 +101,10 @@ final class AddGiftsScreenComponent: Component {
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateScrolling(transition: .immediate)
self.updateScrolling(interactive: true, transition: .immediate)
}
private func updateScrolling(transition: ComponentTransition) {
private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) {
guard let environment = self.environment, let giftsListView = self.giftsListView else {
return
}
@ -111,11 +114,17 @@ final class AddGiftsScreenComponent: Component {
var contentSize = CGSize(width: self.scrollView.bounds.width, height: contentHeight)
contentSize.height += environment.safeInsets.bottom
contentSize.height = max(contentSize.height, self.scrollView.bounds.size.height)
contentSize.height += 50.0 + 24.0
transition.setFrame(view: giftsListView, frame: CGRect(origin: CGPoint(), size: contentSize))
if self.scrollView.contentSize != contentSize {
self.scrollView.contentSize = contentSize
}
let bottomContentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height)
if interactive, bottomContentOffset < 200.0 {
self.giftsListView?.loadMore()
}
}
func update(component: AddGiftsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
@ -128,7 +137,13 @@ final class AddGiftsScreenComponent: Component {
if let current = self.giftsListView {
giftsListView = current
} else {
giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId)
giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId, remainingSelectionCount: component.remainingCount)
giftsListView.onContentUpdated = { [weak self] in
guard let self else {
return
}
self.state?.updated(transition: .immediate)
}
giftsListView.selectionUpdated = { [weak self] in
guard let self else {
return
@ -248,6 +263,7 @@ public final class AddGiftsScreen: ViewControllerComponentContainer {
context: AccountContext,
peerId: EnginePeer.Id,
collectionId: Int32,
remainingCount: Int32,
completion: @escaping ([ProfileGiftsContext.State.StarGift]) -> Void
) {
self.context = context
@ -264,10 +280,10 @@ public final class AddGiftsScreen: ViewControllerComponentContainer {
context: context,
peerId: peerId,
collectionId: collectionId,
remainingCount: remainingCount,
profileGifts: self.profileGifts
), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil)
self.title = presentationData.strings.AddGifts_Title
self.navigationPresentation = .modal

View File

@ -36,6 +36,7 @@ final class GiftsListView: UIView {
private let canSelect: Bool
private let ignoreCollection: Int32?
private let remainingSelectionCount: Int32
private var dataDisposable: Disposable?
@ -124,13 +125,14 @@ final class GiftsListView: UIView {
var contextAction: ((ProfileGiftsContext.State.StarGift, UIView, ContextGesture) -> Void)?
var addToCollection: (() -> Void)?
init(context: AccountContext, peerId: PeerId, profileGifts: ProfileGiftsContext, giftsCollections: ProfileGiftsCollectionsContext?, canSelect: Bool, ignoreCollection: Int32? = nil) {
init(context: AccountContext, peerId: PeerId, profileGifts: ProfileGiftsContext, giftsCollections: ProfileGiftsCollectionsContext?, canSelect: Bool, ignoreCollection: Int32? = nil, remainingSelectionCount: Int32 = 0) {
self.context = context
self.peerId = peerId
self.profileGifts = profileGifts
self.giftsCollections = giftsCollections
self.canSelect = canSelect
self.ignoreCollection = ignoreCollection
self.remainingSelectionCount = remainingSelectionCount
if let value = context.currentAppConfiguration.with({ $0 }).data?["stargifts_pinned_to_top_limit"] as? Double {
self.maxPinnedCount = Int(value)
@ -406,6 +408,13 @@ final class GiftsListView: UIView {
}
return self.updateScrolling(interactive: interactive, topInset: topInset, visibleBounds: visibleBounds, transition: transition)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if let topInset = self.topInset, point.y < topInset {
return false
}
return super.point(inside: point, with: event)
}
func updateScrolling(interactive: Bool = false, topInset: CGFloat, visibleBounds: CGRect, transition: ComponentTransition) -> CGFloat {
self.topInset = topInset
@ -474,7 +483,7 @@ final class GiftsListView: UIView {
let peer: GiftItemComponent.Peer?
let subject: GiftItemComponent.Subject
var resellPrice: Int64?
var resellAmount: CurrencyAmount?
switch product.gift {
case let .generic(gift):
@ -489,9 +498,9 @@ final class GiftsListView: UIView {
case let .unique(gift):
subject = .uniqueGift(gift: gift, price: nil)
peer = nil
resellPrice = gift.resellStars
resellAmount = gift.resellAmounts?.first
if let _ = resellPrice {
if let _ = resellAmount {
ribbonText = params.presentationData.strings.PeerInfo_Gifts_Sale
ribbonFont = .larger
ribbonColor = .green
@ -524,6 +533,7 @@ final class GiftsListView: UIView {
itemAlpha = 0.3
}
//TODO:release
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(
@ -534,7 +544,7 @@ final class GiftsListView: UIView {
peer: peer,
subject: subject,
ribbon: ribbonText.flatMap { GiftItemComponent.Ribbon(text: $0, font: ribbonFont, color: ribbonColor, outline: ribbonOutline) },
resellPrice: resellPrice,
resellPrice: resellAmount?.amount.value,
isHidden: !product.savedToProfile,
isSelected: self.selectedItemIds.contains(itemReferenceId),
isPinned: !self.canSelect && product.pinnedToTop,
@ -548,8 +558,10 @@ final class GiftsListView: UIView {
if self.selectedItemIds.contains(itemReferenceId) {
self.selectedItemIds.remove(itemReferenceId)
} else {
self.selectedItemIds.insert(itemReferenceId)
self.selectedItemsMap[itemReferenceId] = product
if self.selectedItemIds.count < self.remainingSelectionCount {
self.selectedItemIds.insert(itemReferenceId)
self.selectedItemsMap[itemReferenceId] = product
}
}
self.selectionUpdated()
self.updateScrolling(transition: .easeInOut(duration: 0.25))

View File

@ -151,6 +151,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
super.init()
self.giftsListView.onContentUpdated = { [weak self] in
guard let self else {
return
}
if let params = self.currentParams {
self.update(size: params.size, topInset: params.topInset, sideInset: params.sideInset, bottomInset: params.bottomInset, deviceMetrics: params.deviceMetrics, visibleHeight: params.visibleHeight, isScrollingLockedAtTop: params.isScrollingLockedAtTop, expandProgress: params.expandProgress, navigationHeight: params.navigationHeight, presentationData: params.presentationData, synchronous: true, transition: .immediate)
}
}
self.addSubnode(self.backgroundNode)
self.addSubnode(self.scrollNode)
@ -184,7 +193,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
self.scrollNode.view.delegate = self
self.scrollNode.view.insertSubview(self.giftsListView, at: 0)
if let tabSelectorView = self.tabSelector.view {
self.scrollNode.view.insertSubview(self.giftsListView, aboveSubview: tabSelectorView)
} else {
self.scrollNode.view.insertSubview(self.giftsListView, at: 0)
}
}
private func item(at point: CGPoint) -> (AnyHashable, ComponentView<Empty>)? {
@ -201,7 +214,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
return
}
let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 20, displayCharacterLimit: true, apply: { [weak self] value in
let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in
guard let self, let value else {
return
}
@ -211,6 +224,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
if let collection {
self.setCurrentCollection(collection: .collection(collection.id))
if let tabSelectorView = self.tabSelector.view as? TabSelectorComponent.View {
tabSelectorView.scrollToEnd()
}
}
})
})
@ -230,6 +247,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self?.setCurrentCollection(collection: .all)
let _ = self?.profileGiftsCollections.deleteCollection(id: id).start()
if let tabSelectorView = self?.tabSelector.view as? TabSelectorComponent.View {
tabSelectorView.scrollToStart()
}
})
]),
ActionSheetItemGroup(items: [
@ -242,7 +263,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
public func addGiftsToCollection(id: Int32) {
let screen = AddGiftsScreen(context: self.context, peerId: self.peerId, collectionId: id, completion: { [weak self] gifts in
var collectionGiftsMaxCount: Int32 = 1000
if let value = self.context.currentAppConfiguration.with({ $0 }).data?["stargifts_collection_gifts_limit"] as? Double {
collectionGiftsMaxCount = Int32(value)
}
var remainingCount = collectionGiftsMaxCount
if let currentCount = self.giftsListView.profileGifts.currentState?.count {
remainingCount = max(0, collectionGiftsMaxCount - currentCount)
}
let screen = AddGiftsScreen(context: self.context, peerId: self.peerId, collectionId: id, remainingCount: remainingCount, completion: { [weak self] gifts in
guard let self else {
return
}
@ -256,7 +285,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
return
}
let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 20, displayCharacterLimit: true, apply: { [weak self] value in
let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in
guard let self, let value else {
return
}
@ -460,20 +489,21 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
f(.default)
self.renameCollection(id: id)
Queue.mainQueue().after(0.15) {
self.renameCollection(id: id)
}
})))
items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_ShareCollection, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] _, f in
guard let self else {
return
}
f(.default)
//TODO:release
let _ = self
})))
// items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_ShareCollection, icon: { theme in
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
// }, action: { [weak self] _, f in
// guard let self else {
// return
// }
// f(.default)
//
// let _ = self
// })))
items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_Reorder, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.actionSheet.primaryTextColor)
@ -494,7 +524,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
f(.default)
self.deleteCollection(id: id)
Queue.mainQueue().after(0.15) {
self.deleteCollection(id: id)
}
})))
let contextController = ContextController(
@ -506,8 +538,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)
self.parentController?.presentInGlobalOverlay(contextController)
}
func updateScrolling(interactive: Bool = false, transition: ComponentTransition) {
if let params = self.currentParams {
@ -515,13 +545,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
var topInset: CGFloat = 60.0
if let collections = self.collections, !collections.isEmpty {
var canEditCollections = false
if self.peerId == self.context.account.peerId || self.canManage {
canEditCollections = true
}
let hasNonEmptyCollections = self.collections?.contains(where: { $0.count > 0 }) ?? false
if let collections = self.collections, !collections.isEmpty && (hasNonEmptyCollections || canEditCollections) {
var tabSelectorItems: [TabSelectorComponent.Item] = []
tabSelectorItems.append(TabSelectorComponent.Item(
id: AnyHashable(GiftCollection.all.rawValue),
title: "All Gifts"
title: params.presentationData.strings.PeerInfo_Gifts_Collections_All
))
var effectiveCollections: [StarGiftCollection] = collections
if let reorderedCollectionIds = self.reorderedCollectionIds {
var collectionMap: [Int32: StarGiftCollection] = [:]
@ -538,6 +574,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
for collection in effectiveCollections {
if !canEditCollections && collection.count == 0 {
continue
}
tabSelectorItems.append(TabSelectorComponent.Item(
id: AnyHashable(GiftCollection.collection(collection.id).rawValue),
content: .component(AnyComponent(
@ -549,27 +588,29 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)
)),
isReorderable: collections.count > 1,
contextAction: { [weak self] sourceNode, gesture in
contextAction: canEditCollections ? { [weak self] sourceNode, gesture in
guard let self else {
return
}
self.openCollectionContextMenu(id: collection.id, sourceNode: sourceNode, gesture: gesture)
}
} : nil
))
}
if canEditCollections {
tabSelectorItems.append(TabSelectorComponent.Item(
id: AnyHashable(GiftCollection.create.rawValue),
content: .component(AnyComponent(
CollectionTabItemComponent(
context: self.context,
icon: .add,
title: params.presentationData.strings.PeerInfo_Gifts_Collections_Add,
theme: params.presentationData.theme
)
)),
isReorderable: false
))
}
tabSelectorItems.append(TabSelectorComponent.Item(
id: AnyHashable(GiftCollection.create.rawValue),
content: .component(AnyComponent(
CollectionTabItemComponent(
context: self.context,
icon: .add,
title: "Add Collection",
theme: params.presentationData.theme
)
)),
isReorderable: false
))
let tabSelectorSize = self.tabSelector.update(
transition: transition,
@ -621,7 +662,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
if let tabSelectorView = self.tabSelector.view {
if tabSelectorView.superview == nil {
tabSelectorView.alpha = 1.0
self.scrollNode.view.addSubview(tabSelectorView)
self.scrollNode.view.insertSubview(tabSelectorView, at: 0)
if !transition.animation.isImmediate {
tabSelectorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
@ -938,10 +979,53 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}, iconPosition: collection.icon == nil ? .left : .right, action: { [weak self] _, f in
f(.default)
guard let self else {
return
}
if isAdded, let giftReference = gift.reference {
let _ = self?.profileGiftsCollections.removeGifts(id: collection.id, gifts: [giftReference]).start()
let _ = self.profileGiftsCollections.removeGifts(id: collection.id, gifts: [giftReference]).start()
} else {
let _ = self?.profileGiftsCollections.addGifts(id: collection.id, gifts: [gift]).start()
let _ = self.profileGiftsCollections.addGifts(id: collection.id, gifts: [gift]).start()
}
var giftFile: TelegramMediaFile?
var giftTitle: String?
switch gift.gift {
case let .generic(gift):
giftFile = gift.file
case let .unique(uniqueGift):
giftTitle = uniqueGift.title + " #\(presentationStringsFormattedNumber(uniqueGift.number, currentParams.presentationData.dateTimeFormat.groupingSeparator))"
for attribute in uniqueGift.attributes {
if case let .model(_, file, _) = attribute {
giftFile = file
}
}
}
if let giftFile {
let text: String
if let giftTitle {
if isAdded {
text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollectionUnique(giftTitle, collection.title).string
} else {
text = currentParams.presentationData.strings.PeerInfo_Gifts_AddedToCollectionUnique(giftTitle, collection.title).string
}
} else {
if isAdded {
text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollection(collection.title).string
} else {
text = currentParams.presentationData.strings.PeerInfo_Gifts_AddedToCollection(collection.title).string
}
}
let undoController = UndoOverlayController(
presentationData: currentParams.presentationData,
content: .sticker(context: self.context, file: giftFile, loop: false, title: nil, text: text, undoText: nil, customAction: nil),
elevatedLayout: true,
action: { _ in return true }
)
self.parentController?.present(undoController, in: .current)
}
})))
}
@ -1213,12 +1297,47 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
}
if case let .collection(id) = self.currentCollection {
if canManage, case let .collection(id) = self.currentCollection {
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_RemoveFromCollection, textColor: .destructive, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/RemoveFromCollection"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in
f(.default)
guard let self else {
return
}
if let reference = gift.reference {
let _ = self?.profileGiftsCollections.removeGifts(id: id, gifts: [reference]).start()
let _ = self.profileGiftsCollections.removeGifts(id: id, gifts: [reference]).start()
}
var giftFile: TelegramMediaFile?
var giftTitle: String?
switch gift.gift {
case let .generic(gift):
giftFile = gift.file
case let .unique(uniqueGift):
giftTitle = uniqueGift.title + " #\(presentationStringsFormattedNumber(uniqueGift.number, currentParams.presentationData.dateTimeFormat.groupingSeparator))"
for attribute in uniqueGift.attributes {
if case let .model(_, file, _) = attribute {
giftFile = file
}
}
}
if let giftFile, let collection = self.collections?.first(where: { $0.id == id }) {
let text: String
if let giftTitle {
text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollectionUnique(giftTitle, collection.title).string
} else {
text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollection(collection.title).string
}
let undoController = UndoOverlayController(
presentationData: currentParams.presentationData,
content: .sticker(context: self.context, file: giftFile, loop: false, title: nil, text: text, undoText: nil, customAction: nil),
elevatedLayout: true,
action: { _ in return true }
)
self.parentController?.present(undoController, in: .current)
}
})))
}
@ -1351,14 +1470,12 @@ private final class CollectionTabItemComponent: Component {
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.title, font: Font.semibold(14.0), textColor: .white))
text: .plain(NSAttributedString(string: component.title, font: Font.semibold(14.0), textColor: component.theme.list.itemSecondaryTextColor))
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: 100.0)
)
let tintColor = component.theme.list.itemSecondaryTextColor
var iconOffset: CGFloat = 0.0
var iconSize = CGSize()
if let icon = component.icon {
@ -1392,7 +1509,7 @@ private final class CollectionTabItemComponent: Component {
transition: .immediate,
component: AnyComponent(BundleIconComponent(
name: "Chat/Input/Media/PanelBadgeAdd",
tintColor: tintColor
tintColor: component.theme.list.itemSecondaryTextColor
)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
@ -1428,8 +1545,6 @@ private final class CollectionTabItemComponent: Component {
self.addSubview(titleView)
}
titleView.frame = titleFrame
transition.setTintColor(layer: titleView.layer, color: tintColor)
}
let size: CGSize

View File

@ -460,7 +460,8 @@ final class UserAppearanceScreenComponent: Component {
],
availability: StarGift.UniqueGift.Availability(issued: 0, total: 0),
giftAddress: nil,
resellStars: nil,
resellAmounts: nil,
resellForTonOnly: false,
releasedBy: nil
)
signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate)

View File

@ -942,8 +942,8 @@ private final class SheetContent: CombinedComponent {
if case let .starGiftResell(giftToMatch, update, _) = self.mode {
if update {
if let resellStars = giftToMatch.resellStars {
self.amount = StarsAmount(value: resellStars, nanos: 0)
if let resellStars = giftToMatch.resellAmounts?.first(where: { $0.currency == .stars }) {
self.amount = resellStars.amount
}
} else {
let _ = (context.engine.payments.cachedStarGifts()

View File

@ -483,6 +483,14 @@ public final class TabSelectorComponent: Component {
}
}
public func scrollToStart() {
self.setContentOffset(.zero, animated: true)
}
public func scrollToEnd() {
self.setContentOffset(CGPoint(x: self.contentSize.width - self.bounds.width, y: 0.0), animated: true)
}
func update(component: TabSelectorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let selectionColorUpdated = component.colors.selection != self.component?.colors.selection

View File

@ -900,7 +900,21 @@ final class AuthorizedApplicationContext {
}
if openAppIfAny, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.rootController.viewControllers.last as? ViewController {
self.context.sharedContext.openWebApp(context: self.context, parentController: parentController, updatedPresentationData: nil, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: true, payload: nil)
self.context.sharedContext.openWebApp(
context: self.context,
parentController: parentController,
updatedPresentationData: nil,
botPeer: peer,
chatPeer: nil,
threadId: nil,
buttonText: "",
url: "",
simple: true,
source: .generic,
skipTermsOfService: true,
payload: nil,
verifyAgeCompletion: nil
)
} else {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: alwaysKeepMessageId || isOutgoingMessage ? messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false) } : nil, activateInput: activateInput ? .text : nil))
}

View File

@ -128,8 +128,7 @@ extension ChatControllerImpl {
}
}
if "".isEmpty {
//TODO:release
if canReplyInChat(self.presentationInterfaceState, accountPeerId: self.context.account.peerId) {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Todo_ReplyToItem, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] c, _ in

View File

@ -27,7 +27,8 @@ func openWebAppImpl(
simple: Bool,
source: ChatOpenWebViewSource,
skipTermsOfService: Bool,
payload: String?
payload: String?,
verifyAgeCompletion: ((Int) -> Void)?
) {
if context.isFrozen {
parentController.push(context.sharedContext.makeAccountFreezeInfoScreen(context: context))
@ -246,7 +247,7 @@ func openWebAppImpl(
navigationController = parentController.effectiveNavigationController
}
return navigationController ?? (context.sharedContext.mainWindow?.viewController as? NavigationController)
})
}, verifyAgeCompletion: verifyAgeCompletion)
controller.navigationPresentation = .flatModal
parentController.push(controller)
@ -351,7 +352,21 @@ public extension ChatControllerImpl {
}
self.chatDisplayNode.dismissInput()
self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: EnginePeer(peer), chatPeer: EnginePeer(peer), threadId: self.chatLocation.threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: false, payload: nil)
self.context.sharedContext.openWebApp(
context: self.context,
parentController: self,
updatedPresentationData: self.updatedPresentationData,
botPeer: EnginePeer(peer),
chatPeer: EnginePeer(peer),
threadId: self.chatLocation.threadId,
buttonText: buttonText,
url: url,
simple: simple,
source: source,
skipTermsOfService: false,
payload: nil,
verifyAgeCompletion: nil
)
}
fileprivate static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void {
@ -616,7 +631,21 @@ public extension ChatControllerImpl {
}
})
} else {
context.sharedContext.openWebApp(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: payload)
context.sharedContext.openWebApp(
context: context,
parentController: parentController,
updatedPresentationData: updatedPresentationData,
botPeer: botPeer,
chatPeer: nil,
threadId: nil,
buttonText: "",
url: "",
simple: true,
source: .generic,
skipTermsOfService: false,
payload: payload,
verifyAgeCompletion: nil
)
}
}
}

View File

@ -8907,7 +8907,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
commit()
})
} else {
self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: botAppStart.payload)
self.context.sharedContext.openWebApp(
context: self.context,
parentController: self,
updatedPresentationData: self.updatedPresentationData,
botPeer: peer,
chatPeer: nil,
threadId: nil,
buttonText: "",
url: "",
simple: true,
source: .generic,
skipTermsOfService: false,
payload: botAppStart.payload,
verifyAgeCompletion: nil
)
commit()
}
})

View File

@ -3882,8 +3882,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return incomingMessagePrivacyScreen(context: context, value: value, exceptions: exceptions, update: update)
}
public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) {
openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload)
public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?, verifyAgeCompletion: ((Int) -> Void)?) {
openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload, verifyAgeCompletion: verifyAgeCompletion)
}
public func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id, mode: AffiliateProgramSetupScreenMode) -> Signal<AffiliateProgramSetupScreenInitialData, NoError> {

View File

@ -31,7 +31,6 @@ import PeerInfoScreen
import PeerInfoStoryGridScreen
import ShareWithPeersScreen
import ChatEmptyNode
//import FaceScanScreen
import UndoUI
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
@ -237,22 +236,6 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
self.accountSettingsController = accountSettingsController
self.rootTabController = tabBarController
self.pushViewController(tabBarController, animated: false)
// Queue.mainQueue().after(1.0) {
// let context = self.context
// let infoScreen = AgeVerificationScreen(context: context, completion: { [weak chatListController] proceed in
// if proceed {
// let scanScreen = FaceScanScreen(context: context, completion: { success in
// let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: "Age check passed!", text: "You can now view this content.", cancel: nil, destructive: false), elevatedLayout: true, action: { _ in return true })
// Queue.mainQueue().after(0.1) {
// chatListController?.present(controller, in: .window(.root))
// }
// })
// chatListController?.push(scanScreen)
// }
// })
// chatListController.push(infoScreen)
// }
}
public func updateRootControllers(showCallsTab: Bool) {

View File

@ -90,6 +90,21 @@ private func updateInfoControllerEntries(theme: PresentationTheme, strings: Pres
return entries
}
private class UpdateInfoController: ItemListController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.view.layer.animatePosition(from: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), to: self.view.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
})
}
func animateOut(completion: (() -> Void)? = nil) {
self.view.layer.animatePosition(from: self.view.layer.position, to: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
completion?()
})
}
}
public func updateInfoController(context: AccountContext, appUpdateInfo: AppUpdateInfo) -> ViewController {
var dismissImpl: (() -> Void)?
var linkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)?
@ -128,16 +143,16 @@ public func updateInfoController(context: AccountContext, appUpdateInfo: AppUpda
actionsDisposable.dispose()
}
let controller = ItemListController(sharedContext: context.sharedContext, state: signal)
controller.navigationPresentation = .modal
let controller = UpdateInfoController(sharedContext: context.sharedContext, state: signal)
linkActionImpl = { [weak controller, weak context] action, itemLink in
if let strongController = controller, let context = context {
context.sharedContext.handleTextLinkAction(context: context, peerId: nil, navigateDisposable: navigateDisposable, controller: strongController, action: action, itemLink: itemLink)
}
}
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)
controller?.presentingViewController?.dismiss(animated: true, completion: nil)
controller?.animateOut(completion: { [weak controller] in
controller?.dismiss()
})
}
return controller
}

View File

@ -102,6 +102,7 @@ class UpdateInfoItemNode: ListViewItemNode {
private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private var linkHighlightingNode: LinkHighlightingNode?
private let maskNode: ASImageNode
private let iconNode: ASImageNode
private let overlayNode: ASImageNode
@ -127,6 +128,9 @@ class UpdateInfoItemNode: ListViewItemNode {
self.bottomStripeNode = ASDisplayNode()
self.bottomStripeNode.isLayerBacked = true
self.maskNode = ASImageNode()
self.maskNode.isUserInteractionEnabled = false
self.iconNode = ASImageNode()
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0))
self.iconNode.isLayerBacked = true
@ -198,6 +202,7 @@ class UpdateInfoItemNode: ListViewItemNode {
let inset: CGFloat
let itemBackgroundColor: UIColor
let itemSeparatorColor: UIColor
let verticalInset: CGFloat = 14.0
switch item.style {
case .plain:
@ -221,10 +226,10 @@ class UpdateInfoItemNode: ListViewItemNode {
switch item.style {
case .plain:
contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + inset)
contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + verticalInset * 2.0)
insets = itemListNeighborsPlainInsets(neighbors)
case .blocks:
contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + inset)
contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + verticalInset * 2.0)
insets = itemListNeighborsGroupedInsets(neighbors, params)
}
@ -289,11 +294,19 @@ class UpdateInfoItemNode: ListViewItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.addSubnode(strongSelf.maskNode)
}
let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
let bottomStripeOffset: CGFloat
@ -305,13 +318,19 @@ class UpdateInfoItemNode: ListViewItemNode {
default:
bottomStripeInset = 0.0
bottomStripeOffset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
let iconFrame = CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: 62.0, height: 62.0))
let iconFrame = CGRect(origin: CGPoint(x: inset, y: verticalInset), size: CGSize(width: 62.0, height: 62.0))
strongSelf.iconNode.frame = iconFrame
strongSelf.overlayNode.frame = iconFrame

View File

@ -653,10 +653,14 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.animateTransitionIn()
})
}
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
decisionHandler(.prompt)
if self.controller?.isVerifyAgeBot == true && type == .camera {
decisionHandler(.grant)
} else {
decisionHandler(.prompt)
}
}
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
@ -1756,6 +1760,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
case "web_app_hide_keyboard":
self.view.window?.endEditing(true)
case "web_app_verify_age":
if let json, self.controller?.isVerifyAgeBot == true {
if let ageValue = json["age"] as? Double {
self.controller?.verifyAgeCompletion?(Int(ageValue))
}
}
default:
break
}
@ -3281,6 +3291,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
public var completion: () -> Void = {}
public var requestSwitchInline: (String, [ReplyMarkupButtonRequestPeerType]?, @escaping () -> Void) -> Void = { _, _, _ in }
public var verifyAgeCompletion: ((Int) -> Void)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, replyToMessageId: MessageId?, threadId: Int64?) {
self.context = context
self.source = params.source
@ -3323,16 +3335,20 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.navigationItem.leftBarButtonItem?.action = #selector(self.cancelPressed)
self.navigationItem.leftBarButtonItem?.target = self
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode)
self.navigationItem.rightBarButtonItem?.action = #selector(self.moreButtonPressed)
self.navigationItem.rightBarButtonItem?.target = self
if !self.isVerifyAgeBot {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode)
self.navigationItem.rightBarButtonItem?.action = #selector(self.moreButtonPressed)
self.navigationItem.rightBarButtonItem?.target = self
}
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
let titleView = WebAppTitleView(context: self.context, theme: self.presentationData.theme)
titleView.title = WebAppTitle(title: params.botName, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: params.botVerified)
self.navigationItem.titleView = titleView
self.titleView = titleView
if !self.isVerifyAgeBot {
let titleView = WebAppTitleView(context: self.context, theme: self.presentationData.theme)
titleView.title = WebAppTitle(title: params.botName, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: params.botVerified)
self.navigationItem.titleView = titleView
self.titleView = titleView
}
self.moreButtonNode.action = { [weak self] _, gesture in
if let strongSelf = self {
@ -3387,6 +3403,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.presentationDataDisposable?.dispose()
}
private var isVerifyAgeBot: Bool {
if let ageBotUsername = self.context.currentAppConfiguration.with({ $0 }).data?["verify_age_bot_username"] as? String {
return self.botAddress == ageBotUsername
}
return false
}
public func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) {
switch self.source {
case .generic, .settings:
@ -3857,7 +3880,8 @@ public func standaloneWebAppController(
willDismiss: @escaping () -> Void = {},
didDismiss: @escaping () -> Void = {},
getNavigationController: @escaping () -> NavigationController? = { return nil },
getSourceRect: (() -> CGRect?)? = nil
getSourceRect: (() -> CGRect?)? = nil,
verifyAgeCompletion: ((Int) -> Void)? = nil
) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, isFullSize: params.fullSize, makeEntityInputView: {
return nil
@ -3868,6 +3892,7 @@ public func standaloneWebAppController(
webAppController.completion = completion
webAppController.getNavigationController = getNavigationController
webAppController.requestSwitchInline = requestSwitchInline
webAppController.verifyAgeCompletion = verifyAgeCompletion
present(webAppController, webAppController.mediaPickerContext)
}
controller.willDismiss = willDismiss

View File

@ -1,5 +1,5 @@
{
"app": "11.14",
"app": "11.13.4",
"xcode": "16.2",
"bazel": "8.2.1:22ff65b05869f6160e5157b1b425a14a62085d71d8baef571f462b8fe5a703a3",
"macos": "15"