Various improvements

This commit is contained in:
Ilya Laktyushin 2025-07-29 17:39:46 +02:00
parent 74cdd1816f
commit 3c87b308f8
20 changed files with 270 additions and 95 deletions

View File

@ -329,8 +329,10 @@ public enum ResolvedUrl {
case collectible(gift: StarGift.UniqueGift?)
case messageLink(link: TelegramResolvedMessageLink?)
case stars
case ton
case shareStory(Int64)
case storyFolder(peerId: PeerId, id: Int64)
case giftCollection(peerId: PeerId, id: Int64)
}
public enum ResolveUrlResult {
@ -669,6 +671,7 @@ public enum PeerInfoControllerMode {
case groupsInCommon
case monoforum(EnginePeer.Id)
case storyAlbum(id: Int64)
case giftCollection(id: Int64)
}
public enum ContactListActionItemInlineIconPosition {

View File

@ -1204,6 +1204,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[555358088] = { return Api.WebPage.parse_webPageEmpty($0) }
dict[1930545681] = { return Api.WebPage.parse_webPageNotModified($0) }
dict[-1328464313] = { return Api.WebPage.parse_webPagePending($0) }
dict[835375875] = { return Api.WebPageAttribute.parse_webPageAttributeStarGiftCollection($0) }
dict[1355547603] = { return Api.WebPageAttribute.parse_webPageAttributeStickerSet($0) }
dict[781501415] = { return Api.WebPageAttribute.parse_webPageAttributeStory($0) }
dict[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($0) }

View File

@ -1,5 +1,6 @@
public extension Api {
indirect enum WebPageAttribute: TypeConstructorDescription {
case webPageAttributeStarGiftCollection(icons: [Api.Document])
case webPageAttributeStickerSet(flags: Int32, stickers: [Api.Document])
case webPageAttributeStory(flags: Int32, peer: Api.Peer, id: Int32, story: Api.StoryItem?)
case webPageAttributeTheme(flags: Int32, documents: [Api.Document]?, settings: Api.ThemeSettings?)
@ -7,6 +8,16 @@ public extension Api {
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .webPageAttributeStarGiftCollection(let icons):
if boxed {
buffer.appendInt32(835375875)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(icons.count))
for item in icons {
item.serialize(buffer, true)
}
break
case .webPageAttributeStickerSet(let flags, let stickers):
if boxed {
buffer.appendInt32(1355547603)
@ -50,6 +61,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .webPageAttributeStarGiftCollection(let icons):
return ("webPageAttributeStarGiftCollection", [("icons", icons as Any)])
case .webPageAttributeStickerSet(let flags, let stickers):
return ("webPageAttributeStickerSet", [("flags", flags as Any), ("stickers", stickers as Any)])
case .webPageAttributeStory(let flags, let peer, let id, let story):
@ -61,6 +74,19 @@ public extension Api {
}
}
public static func parse_webPageAttributeStarGiftCollection(_ reader: BufferReader) -> WebPageAttribute? {
var _1: [Api.Document]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.WebPageAttribute.webPageAttributeStarGiftCollection(icons: _1!)
}
else {
return nil
}
}
public static func parse_webPageAttributeStickerSet(_ reader: BufferReader) -> WebPageAttribute? {
var _1: Int32?
_1 = reader.readInt32()
@ -1328,39 +1354,3 @@ public extension Api.account {
}
}
public extension Api.account {
enum Takeout: TypeConstructorDescription {
case takeout(id: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .takeout(let id):
if boxed {
buffer.appendInt32(1304052993)
}
serializeInt64(id, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .takeout(let id):
return ("takeout", [("id", id as Any)])
}
}
public static func parse_takeout(_ reader: BufferReader) -> Takeout? {
var _1: Int64?
_1 = reader.readInt64()
let _c1 = _1 != nil
if _c1 {
return Api.account.Takeout.takeout(id: _1!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,39 @@
public extension Api.account {
enum Takeout: TypeConstructorDescription {
case takeout(id: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .takeout(let id):
if boxed {
buffer.appendInt32(1304052993)
}
serializeInt64(id, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .takeout(let id):
return ("takeout", [("id", id as Any)])
}
}
public static func parse_takeout(_ reader: BufferReader) -> Takeout? {
var _1: Int64?
_1 = reader.readInt64()
let _c1 = _1 != nil
if _c1 {
return Api.account.Takeout.takeout(id: _1!)
}
else {
return nil
}
}
}
}
public extension Api.account {
enum Themes: TypeConstructorDescription {
case themes(hash: Int64, themes: [Api.Theme])

View File

@ -28,6 +28,10 @@ func telegramMediaWebpageAttributeFromApiWebpageAttribute(_ attribute: Api.WebPa
return .starGift(TelegramMediaWebpageStarGiftAttribute(gift: starGift))
}
return nil
case let .webPageAttributeStarGiftCollection(icons):
var files: [TelegramMediaFile] = []
files = icons.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }
return .giftCollection(TelegramMediaWebpageGiftCollectionAttribute(files: files))
case .webPageAttributeStory:
return nil
}

View File

@ -7,6 +7,7 @@ private enum TelegramMediaWebpageAttributeTypes: Int32 {
case theme
case stickerPack
case starGift
case giftCollection
}
public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
@ -14,33 +15,39 @@ public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
case theme(TelegraMediaWebpageThemeAttribute)
case stickerPack(TelegramMediaWebpageStickerPackAttribute)
case starGift(TelegramMediaWebpageStarGiftAttribute)
case giftCollection(TelegramMediaWebpageGiftCollectionAttribute)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("r", orElse: 0) {
case TelegramMediaWebpageAttributeTypes.theme.rawValue:
self = .theme(decoder.decodeObjectForKey("a", decoder: { TelegraMediaWebpageThemeAttribute(decoder: $0) }) as! TelegraMediaWebpageThemeAttribute)
case TelegramMediaWebpageAttributeTypes.stickerPack.rawValue:
self = .stickerPack(decoder.decodeObjectForKey("a", decoder: { TelegramMediaWebpageStickerPackAttribute(decoder: $0) }) as! TelegramMediaWebpageStickerPackAttribute)
case TelegramMediaWebpageAttributeTypes.starGift.rawValue:
self = .starGift(decoder.decodeObjectForKey("a", decoder: { TelegramMediaWebpageStarGiftAttribute(decoder: $0) }) as! TelegramMediaWebpageStarGiftAttribute)
default:
self = .unsupported
case TelegramMediaWebpageAttributeTypes.theme.rawValue:
self = .theme(decoder.decodeObjectForKey("a", decoder: { TelegraMediaWebpageThemeAttribute(decoder: $0) }) as! TelegraMediaWebpageThemeAttribute)
case TelegramMediaWebpageAttributeTypes.stickerPack.rawValue:
self = .stickerPack(decoder.decodeObjectForKey("a", decoder: { TelegramMediaWebpageStickerPackAttribute(decoder: $0) }) as! TelegramMediaWebpageStickerPackAttribute)
case TelegramMediaWebpageAttributeTypes.starGift.rawValue:
self = .starGift(decoder.decodeObjectForKey("a", decoder: { TelegramMediaWebpageStarGiftAttribute(decoder: $0) }) as! TelegramMediaWebpageStarGiftAttribute)
case TelegramMediaWebpageAttributeTypes.giftCollection.rawValue:
self = .giftCollection(decoder.decodeObjectForKey("a", decoder: { TelegramMediaWebpageGiftCollectionAttribute(decoder: $0) }) as! TelegramMediaWebpageGiftCollectionAttribute)
default:
self = .unsupported
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case .unsupported:
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.unsupported.rawValue, forKey: "r")
case let .theme(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.theme.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case let .stickerPack(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.stickerPack.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case let .starGift(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.starGift.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case .unsupported:
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.unsupported.rawValue, forKey: "r")
case let .theme(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.theme.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case let .stickerPack(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.stickerPack.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case let .starGift(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.starGift.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
case let .giftCollection(attribute):
encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.giftCollection.rawValue, forKey: "r")
encoder.encodeObject(attribute, forKey: "a")
}
}
}
@ -159,6 +166,35 @@ public final class TelegramMediaWebpageStarGiftAttribute: PostboxCoding, Equatab
}
}
public final class TelegramMediaWebpageGiftCollectionAttribute: PostboxCoding, Equatable {
public static func == (lhs: TelegramMediaWebpageGiftCollectionAttribute, rhs: TelegramMediaWebpageGiftCollectionAttribute) -> Bool {
if lhs.files.count != rhs.files.count {
return false
} else {
for i in 0 ..< lhs.files.count {
if !lhs.files[i].isEqual(to: rhs.files[i]) {
return false
}
}
}
return true
}
public let files: [TelegramMediaFile]
public init(files: [TelegramMediaFile]) {
self.files = files
}
public init(decoder: PostboxDecoder) {
self.files = decoder.decodeObjectArrayForKey("files")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.files, forKey: "files")
}
}
public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable {
public let url: String
public let displayUrl: String

View File

@ -1838,11 +1838,11 @@ private final class ProfileGiftsContextImpl {
return true
}
switch gift.gift {
case .generic(let gift):
case let .generic(gift):
if gift.id == id {
return true
}
case .unique(let uniqueGift):
case let .unique(uniqueGift):
if uniqueGift.id == id {
return true
}
@ -1861,11 +1861,11 @@ private final class ProfileGiftsContextImpl {
return true
}
switch gift.gift {
case .generic(let gift):
case let .generic(gift):
if gift.id == id {
return true
}
case .unique(let uniqueGift):
case let .unique(uniqueGift):
if uniqueGift.id == id {
return true
}

View File

@ -11,6 +11,20 @@ public func formatTonAddress(_ address: String) -> String {
return address
}
public func convertStarsToTon(_ amount: StarsAmount, tonUsdRate: Double, starsUsdRate: Double) -> Int64 {
let usdRate = starsUsdRate / 1000.0 / 100.0
let usdValue = Double(amount.value) * usdRate
let tonValue = usdValue / tonUsdRate * 1000000000.0
return Int64(tonValue)
}
public func convertTonToStars(_ amount: StarsAmount, tonUsdRate: Double, starsUsdRate: Double) -> Int64 {
let usdRate = starsUsdRate / 1000.0 / 100.0
let usdValue = Double(amount.value) / 1000000000 * tonUsdRate
let starsValue = usdValue / usdRate
return Int64(starsValue)
}
public func formatTonUsdValue(_ value: Int64, divide: Bool = true, rate: Double = 1.0, dateTimeFormat: PresentationDateTimeFormat) -> String {
let decimalSeparator = dateTimeFormat.decimalSeparator
let normalizedValue: Double = divide ? Double(value) / 1000000000 : Double(value)

View File

@ -476,6 +476,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
entities = nil
case "telegram_call":
actionTitle = item.presentationData.strings.Chat_ViewGroupCall
case "telegram_collection":
//TODO:localize
actionTitle = "VIEW COLLECTION"
default:
break
}
@ -484,6 +487,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
if case let .stickerPack(stickerPack) = attribute, !stickerPack.files.isEmpty {
mediaAndFlags = (stickerPack.files, [.preferMediaInline, .stickerPack])
break
} else if case let .giftCollection(giftCollection) = attribute, !giftCollection.files.isEmpty {
mediaAndFlags = (giftCollection.files, [.preferMediaInline, .stickerPack])
break
}
}

View File

@ -1454,10 +1454,14 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
break
case .stars:
break
case .ton:
break
case .shareStory:
break
case .storyFolder:
break
case .giftCollection:
break
}
}
}))

View File

@ -843,9 +843,9 @@ private final class GiftViewSheetContent: CombinedComponent {
}
switch self.subject {
case let .profileGift(peerId, currentSubject):
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts(nil))))
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts(nil).withResellForTonOnly(false))))
case let .uniqueGift(_, recipientPeerId):
self.subject = .uniqueGift(gift.withResellAmounts(nil), recipientPeerId)
self.subject = .uniqueGift(gift.withResellAmounts(nil).withResellForTonOnly(false), recipientPeerId)
default:
break
}
@ -919,9 +919,9 @@ private final class GiftViewSheetContent: CombinedComponent {
switch self.subject {
case let .profileGift(peerId, currentSubject):
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts([price]))))
self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts([price]).withResellForTonOnly(price.currency == .ton))))
case let .uniqueGift(_, recipientPeerId):
self.subject = .uniqueGift(gift.withResellAmounts([price]), recipientPeerId)
self.subject = .uniqueGift(gift.withResellAmounts([price]).withResellForTonOnly(price.currency == .ton), recipientPeerId)
default:
break
}

View File

@ -528,6 +528,7 @@ private final class PeerInfoPendingPane {
chatLocationContextHolder: Atomic<ChatLocationContextHolder?>,
sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?,
initialStoryFolderId: Int64?,
initialGiftCollectionId: Int64?,
key: PeerInfoPaneKey,
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
parentController: ViewController?,
@ -577,7 +578,7 @@ private final class PeerInfoPendingPane {
}
}
}
paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, profileGiftsCollections: data.profileGiftsCollectionsContext!, profileGifts: data.profileGiftsContext!, canManage: canManage, canGift: canGift)
paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, profileGiftsCollections: data.profileGiftsCollectionsContext!, profileGifts: data.profileGiftsContext!, canManage: canManage, canGift: canGift, initialGiftCollectionId: initialGiftCollectionId)
case .stories, .storyArchive, .botPreview:
var canManage = false
if let peer = data.peer {
@ -730,6 +731,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
private var pendingPanes: [PeerInfoPaneKey: PeerInfoPendingPane] = [:]
private var shouldFadeIn = false
private var initialStoryFolderId: Int64?
private var initialGiftCollectionId: Int64?
private var transitionFraction: CGFloat = 0.0
@ -755,7 +757,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
private let initialPaneKey: PeerInfoPaneKey?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?) {
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?, initialGiftCollectionId: Int64?) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
@ -765,6 +767,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
self.isMediaOnly = isMediaOnly
self.initialPaneKey = initialPaneKey
self.initialStoryFolderId = initialStoryFolderId
self.initialGiftCollectionId = initialGiftCollectionId
self.additionalBackgroundNode = ASDisplayNode()
@ -1103,12 +1106,19 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
if self.pendingPanes[key] == nil, let data {
var leftScope = false
var initialStoryFolderId: Int64?
var initialGiftCollectionId: Int64?
if case .stories = key {
if let initialStoryFolderIdValue = self.initialStoryFolderId {
self.initialStoryFolderId = nil
initialStoryFolderId = initialStoryFolderIdValue
}
}
if case .gifts = key {
if let initialGiftCollectionIdValue = self.initialGiftCollectionId {
self.initialGiftCollectionId = nil
initialGiftCollectionId = initialGiftCollectionIdValue
}
}
let pane = PeerInfoPendingPane(
context: self.context,
updatedPresentationData: self.updatedPresentationData,
@ -1128,6 +1138,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
chatLocationContextHolder: self.chatLocationContextHolder,
sharedMediaFromForumTopic: self.sharedMediaFromForumTopic,
initialStoryFolderId: initialStoryFolderId,
initialGiftCollectionId: initialGiftCollectionId,
key: key,
hasBecomeReady: { [weak self] key in
let apply: () -> Void = {

View File

@ -2908,6 +2908,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
private let chatLocation: ChatLocation
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
private let switchToStoryFolder: Int64?
private let switchToGiftCollection: Int64?
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
let isSettings: Bool
@ -3032,7 +3033,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
private var didSetReady = false
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToStoryFolder: Int64?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToStoryFolder: Int64?, switchToGiftCollection: Int64?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
self.controller = controller
self.context = context
self.peerId = peerId
@ -3049,6 +3050,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.isMediaOnly = context.account.peerId == peerId && !isSettings && !isMyProfile
self.initialExpandPanes = initialPaneKey != nil
self.switchToStoryFolder = switchToStoryFolder
self.switchToGiftCollection = switchToGiftCollection
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
self.scrollNode = ASScrollNode()
@ -3060,7 +3062,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
forumTopicThreadId = message.threadId
}
self.headerNode = PeerInfoHeaderNode(context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, isMyProfile: isMyProfile, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder, initialGiftCollectionId: switchToGiftCollection)
super.init()
@ -11391,12 +11393,16 @@ 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)
// self?.openShareLink(url: "https://t.me/")
// })))
if let addressName = data.peer?.addressName, !addressName.isEmpty {
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)
if let pane, case let .collection(id) = pane.currentCollection {
self?.openShareLink(url: "https://t.me/\(addressName)/c/\(id)")
}
})))
}
}
if canReorder {
@ -13103,6 +13109,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
private let switchToGifts: Bool
private let switchToGroupsInCommon: Bool
private let switchToStoryFolder: Int64?
private let switchToGiftCollection: Int64?
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
let chatLocation: ChatLocation
private let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
@ -13179,7 +13186,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
switchToRecommendedChannels: Bool = false,
switchToGifts: Bool = false,
switchToGroupsInCommon: Bool = false,
switchToStoryFolder: Int64? = nil
switchToStoryFolder: Int64? = nil,
switchToGiftCollection: Int64? = nil
) {
self.context = context
self.updatedPresentationData = updatedPresentationData
@ -13198,6 +13206,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
self.switchToGifts = switchToGifts
self.switchToGroupsInCommon = switchToGroupsInCommon
self.switchToStoryFolder = switchToStoryFolder
self.switchToGiftCollection = switchToGiftCollection
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
if let forumTopicThread = forumTopicThread {
@ -13565,8 +13574,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
initialPaneKey = .groupsInCommon
} else if self.switchToStoryFolder != nil {
initialPaneKey = .stories
} else if self .switchToGiftCollection != nil {
initialPaneKey = .gifts
}
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToStoryFolder: self.switchToStoryFolder, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToStoryFolder: self.switchToStoryFolder, switchToGiftCollection: self.switchToGiftCollection, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())

View File

@ -80,6 +80,8 @@ final class GiftsListView: UIView {
private var selectedItemsMap: [AnyHashable: ProfileGiftsContext.State.StarGift] = [:]
var selectionUpdated: () -> Void = { }
var displayUnpinScreen: ((ProfileGiftsContext.State.StarGift, (() -> Void)?) -> Void)?
var selectedItems: [ProfileGiftsContext.State.StarGift] {
var gifts: [ProfileGiftsContext.State.StarGift] = []
var existingIds = Set<AnyHashable>()
@ -645,9 +647,9 @@ final class GiftsListView: UIView {
}
if let reference = product.reference {
if pinnedToTop && self.pinnedReferences.count >= self.maxPinnedCount {
// self.displayUnpinScreen(gift: product, completion: {
self.displayUnpinScreen?(product, {
dismissImpl?()
// })
})
return false
}
self.profileGifts.updateStarGiftPinnedToTop(reference: reference, pinnedToTop: pinnedToTop)

View File

@ -70,6 +70,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private let profileGifts: ProfileGiftsContext
private let canManage: Bool
private let canGift: Bool
private let initialGiftCollectionId: Int64?
private var resultsAreEmpty = false
private let chatControllerInteraction: ChatControllerInteraction
@ -130,7 +132,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private let collectionsMaxCount: Int
public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, profileGiftsCollections: ProfileGiftsCollectionsContext, profileGifts: ProfileGiftsContext, canManage: Bool, canGift: Bool) {
public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, profileGiftsCollections: ProfileGiftsCollectionsContext, profileGifts: ProfileGiftsContext, canManage: Bool, canGift: Bool, initialGiftCollectionId: Int64?) {
self.context = context
self.peerId = peerId
self.chatControllerInteraction = chatControllerInteraction
@ -138,6 +140,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.profileGifts = profileGifts
self.canManage = canManage
self.canGift = canGift
self.initialGiftCollectionId = initialGiftCollectionId
if let value = context.currentAppConfiguration.with({ $0 }).data?["stargifts_collections_limit"] as? Double {
self.collectionsMaxCount = Int(value)
@ -150,6 +153,12 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.giftsListView = GiftsListView(context: context, peerId: peerId, profileGifts: profileGifts, giftsCollections: profileGiftsCollections, canSelect: false)
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.scrollNode)
self.statusPromise.set(self.giftsListView.status)
self.ready.set(self.giftsListView.isReady)
self.giftsListView.onContentUpdated = { [weak self] in
guard let self else {
@ -159,19 +168,18 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
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)
self.statusPromise.set(self.giftsListView.status)
self.ready.set(self.giftsListView.isReady)
self.giftsListView.contextAction = { [weak self] gift, view, gesture in
guard let self else {
return
}
self.contextAction(gift: gift, view: view, gesture: gesture)
}
self.giftsListView.displayUnpinScreen = { [weak self] gift, completion in
guard let self else {
return
}
self.displayUnpinScreen(gift: gift, completion: completion)
}
self.collectionsDisposable = (profileGiftsCollections.state
|> deliverOnMainQueue).start(next: { [weak self] state in
@ -181,6 +189,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.collections = state.collections
self.updateScrolling(transition: .easeInOut(duration: 0.2))
})
if let initialGiftCollectionId {
self.setCurrentCollection(collection: .collection(Int32(initialGiftCollectionId)))
}
}
deinit {
@ -435,13 +447,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
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.giftsListView.parentController = self.parentController
self.giftsListView.displayUnpinScreen = { [weak self] gift, completion in
guard let self else {
return
}
self.displayUnpinScreen(gift: gift, completion: completion)
}
self.giftsListView.contextAction = { [weak self] gift, view, gesture in
guard let self else {
return
}
self.contextAction(gift: gift, view: view, gesture: gesture)
}
self.giftsListView.parentController = self.parentController
self.giftsListView.frame = previousGiftsListView.frame
self.scrollNode.view.insertSubview(self.giftsListView, aboveSubview: previousGiftsListView)
@ -494,6 +512,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
})))
// 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

View File

@ -627,20 +627,14 @@ private final class SheetContent: CombinedComponent {
}
if state.currency == .stars {
if let amount = state.amount, let tonUsdRate = withdrawConfiguration.tonUsdRate, let usdWithdrawRate = withdrawConfiguration.usdWithdrawRate {
let usdRate = usdWithdrawRate / 1000.0 / 100.0
let usdValue = Double(amount.value) * usdRate
let tonValue = usdValue / tonUsdRate * 1000000000.0
state.amount = StarsAmount(value: max(min(Int64(tonValue), resaleConfiguration.starGiftResaleMaxTonAmount), resaleConfiguration.starGiftResaleMinTonAmount), nanos: 0)
state.amount = StarsAmount(value: max(min(convertStarsToTon(amount, tonUsdRate: tonUsdRate, starsUsdRate: usdWithdrawRate), resaleConfiguration.starGiftResaleMaxTonAmount), resaleConfiguration.starGiftResaleMinTonAmount), nanos: 0)
} else {
state.amount = StarsAmount(value: 0, nanos: 0)
}
state.currency = .ton
} else {
if let amount = state.amount, let tonUsdRate = withdrawConfiguration.tonUsdRate, let usdWithdrawRate = withdrawConfiguration.usdWithdrawRate {
let usdRate = usdWithdrawRate / 1000.0 / 100.0
let usdValue = Double(amount.value) / 1000000000 * tonUsdRate
let starsValue = usdValue / usdRate
state.amount = StarsAmount(value: max(min(Int64(starsValue), resaleConfiguration.starGiftResaleMaxStarsAmount), resaleConfiguration.starGiftResaleMinStarsAmount), nanos: 0)
state.amount = StarsAmount(value: max(min(convertTonToStars(amount, tonUsdRate: tonUsdRate, starsUsdRate: usdWithdrawRate), resaleConfiguration.starGiftResaleMaxStarsAmount), resaleConfiguration.starGiftResaleMinStarsAmount), nanos: 0)
} else {
state.amount = StarsAmount(value: 0, nanos: 0)
}

View File

@ -883,6 +883,15 @@ func openResolvedUrlImpl(
navigationController.pushViewController(controller, animated: true)
}
}
case .ton:
dismissInput()
if let tonContext = context.tonContext {
let controller = context.sharedContext.makeStarsTransactionsScreen(context: context, starsContext: tonContext)
controller.navigationPresentation = .modal
if let navigationController {
navigationController.pushViewController(controller, animated: true)
}
}
case let .joinVoiceChat(peerId, invite):
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue).start(next: { peer in
@ -1519,5 +1528,26 @@ func openResolvedUrlImpl(
}
navigationController?.pushViewController(controller)
}
case let .giftCollection(peerId, id):
Task { @MainActor [weak navigationController] in
guard let peer = await context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
).get() else {
return
}
guard let controller = context.sharedContext.makePeerInfoController(
context: context,
updatedPresentationData: updatedPresentationData,
peer: peer._asPeer(),
mode: .giftCollection(id: id),
avatarInitiallyExpanded: false,
fromChat: false,
requestsContext: nil
) else {
return
}
navigationController?.pushViewController(controller)
}
}
}

View File

@ -1031,6 +1031,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
} else {
if parsedUrl.host == "stars" {
handleResolvedUrl(.stars)
} else if parsedUrl.host == "ton" {
handleResolvedUrl(.ton)
} else if parsedUrl.host == "importStickers" {
handleResolvedUrl(.importStickers)
} else if parsedUrl.host == "settings" {

View File

@ -3965,6 +3965,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
var switchToGifts = false
var switchToGroupsInCommon = false
var switchToStoryFolder: Int64?
var switchToGiftCollection: Int64?
switch mode {
case let .nearbyPeer(distance):
@ -3992,10 +3993,12 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
sharedMediaFromForumTopic = (peerId, peer.id.toInt64())
case let .storyAlbum(id):
switchToStoryFolder = id
case let .giftCollection(id):
switchToGiftCollection = id
default:
break
}
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGifts: switchToGifts, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGifts: switchToGifts, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToGiftCollection: switchToGiftCollection)
} else if peer is TelegramSecretChat {
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
}

View File

@ -82,6 +82,7 @@ public enum ParsedInternalPeerUrlParameter {
case profile
case referrer(String)
case storyFolder(Int64)
case giftCollection(Int64)
}
public enum ParsedInternalUrl {
@ -603,6 +604,12 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou
} else {
return nil
}
} else if pathComponents.count >= 3 && pathComponents[1] == "c" {
if let collectionId = Int64(pathComponents[2]) {
return .peer(.name(pathComponents[0]), .giftCollection(collectionId))
} else {
return nil
}
} else if pathComponents.count == 4 && pathComponents[0] == "c" {
if let channelId = Int64(pathComponents[1]), let threadId = Int32(pathComponents[2]), let messageId = Int32(pathComponents[3]), channelId > 0 {
var timecode: Double?
@ -946,6 +953,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
case let .storyFolder(folderId):
return .single(.result(.storyFolder(peerId: peer.id, id: folderId)))
case let .giftCollection(collectionId):
return .single(.result(.giftCollection(peerId: peer.id, id: collectionId)))
}
} else {
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))