Update API

This commit is contained in:
Ilya Laktyushin 2024-07-01 20:23:26 +04:00
parent 0a08c0908b
commit a28ec1d157
10 changed files with 219 additions and 63 deletions

View File

@ -2554,7 +2554,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
titleLayout = .secondLineWithValue(strings.Attachment_Paid_EditPrice_Stars(Int32(price)))
} else {
title = strings.Attachment_Paid_Create
titleLayout = .singleLine
titleLayout = .twoLinesMax
}
items.append(.action(ContextMenuActionItem(text: title, textLayout: titleLayout, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Paid"), color: theme.contextMenu.primaryColor)

View File

@ -2012,7 +2012,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let peerData = context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.AdsRestricted(id: peerId),
TelegramEngine.EngineData.Item.Peer.CanViewRevenue(id: peerId)
TelegramEngine.EngineData.Item.Peer.CanViewRevenue(id: peerId),
TelegramEngine.EngineData.Item.Peer.CanViewStarsRevenue(id: peerId)
)
let longLoadingSignal: Signal<Bool, NoError> = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue()))
@ -2038,7 +2039,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
)
|> deliverOnMainQueue
|> map { presentationData, state, peer, data, messageView, stories, boostData, boostersState, giftsState, revenueState, revenueTransactions, starsState, starsTransactions, peerData, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
let (adsRestricted, canViewRevenue) = peerData
let (adsRestricted, canViewRevenue, _) = peerData
var isGroup = false
if let peer, case let .channel(channel) = peer, case .group = channel.info {

View File

@ -316,6 +316,7 @@ private enum MediaReferenceRevalidationKey: Hashable {
case notificationSoundList
case customEmoji(fileId: Int64)
case story(peer: PeerReference, id: Int32)
case starsTransaction(transaction: StarsTransactionReference)
}
private final class MediaReferenceRevalidationItemContext {
@ -752,6 +753,30 @@ final class MediaReferenceRevalidationContext {
}
}
func starsTransaction(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, transaction: StarsTransactionReference) -> Signal<StarsContext.State.Transaction, RevalidateMediaReferenceError> {
return self.genericItem(key: .starsTransaction(transaction: transaction), background: background, request: { next, error in
return (_internal_getStarsTransaction(accountPeerId: accountPeerId, postbox: postbox, network: network, transactionReference: transaction)
|> castError(RevalidateMediaReferenceError.self)
|> mapToSignal { result -> Signal<StarsContext.State.Transaction, RevalidateMediaReferenceError> in
if let result {
return .single(result)
} else {
return .fail(.generic)
}
}).start(next: { value in
next(value)
}, error: { _ in
error(.generic)
})
}) |> mapToSignal { next -> Signal<StarsContext.State.Transaction, RevalidateMediaReferenceError> in
if let next = next as? StarsContext.State.Transaction {
return .single(next)
} else {
return .fail(.generic)
}
}
}
func notificationSoundList(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> {
return self.genericItem(key: .notificationSoundList, background: background, request: { next, error in
return (requestNotificationSoundList(network: network, hash: 0)
@ -937,6 +962,16 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n
return .fail(.generic)
}
}
case let .starsTransaction(transaction, _):
return revalidationContext.starsTransaction(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, transaction: transaction)
|> mapToSignal { transaction -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
for transactionMedia in transaction.media {
if let updatedResource = findUpdatedMediaResource(media: transactionMedia, previousMedia: nil, resource: resource) {
return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
}
}
return .fail(.generic)
}
case let .standalone(media):
if let file = media as? TelegramMediaFile {
for attribute in file.attributes {

View File

@ -23,6 +23,7 @@ public struct CachedChannelFlags: OptionSet {
public static let adsRestricted = CachedChannelFlags(rawValue: 1 << 9)
public static let canViewRevenue = CachedChannelFlags(rawValue: 1 << 10)
public static let paidMediaAllowed = CachedChannelFlags(rawValue: 1 << 11)
public static let canViewStarsRevenue = CachedChannelFlags(rawValue: 1 << 12)
}
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {

View File

@ -268,6 +268,7 @@ public enum AnyMediaReference: Equatable {
case attachBot(peer: PeerReference, media: Media)
case customEmoji(media: Media)
case story(peer: PeerReference, id: Int32, media: Media)
case starsTransaction(transaction: StarsTransactionReference, media: Media)
public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool {
switch lhs {
@ -337,6 +338,12 @@ public enum AnyMediaReference: Equatable {
} else {
return false
}
case let .starsTransaction(lhsTransaction, lhsMedia):
if case let .starsTransaction(rhsTransaction, rhsMedia) = rhs, lhsTransaction == rhsTransaction, lhsMedia.isEqual(to: rhsMedia) {
return true
} else {
return false
}
}
}
@ -364,6 +371,8 @@ public enum AnyMediaReference: Equatable {
return nil
case .story:
return nil
case .starsTransaction:
return nil
}
}
@ -413,6 +422,10 @@ public enum AnyMediaReference: Equatable {
if let media = media as? T {
return .story(peer: peer, id: id, media: media)
}
case let .starsTransaction(transaction, media):
if let media = media as? T {
return .starsTransaction(transaction: transaction, media: media)
}
}
return nil
}
@ -441,6 +454,8 @@ public enum AnyMediaReference: Equatable {
return media
case let .story(_, _, media):
return media
case let .starsTransaction(_, media):
return media
}
}
@ -468,6 +483,8 @@ public enum AnyMediaReference: Equatable {
return .customEmoji(media: media)
case let .story(peer, id, _):
return .story(peer: peer, id: id, media: media)
case let .starsTransaction(transaction, _):
return .starsTransaction(transaction: transaction, media: media)
}
}
@ -567,6 +584,7 @@ public enum MediaReference<T: Media> {
case attachBot
case customEmoji
case story
case starsTransaction
}
case standalone(media: T)
@ -580,6 +598,7 @@ public enum MediaReference<T: Media> {
case attachBot(peer: PeerReference, media: T)
case customEmoji(media: T)
case story(peer: PeerReference, id: Int32, media: T)
case starsTransaction(transaction: StarsTransactionReference, media: T)
public init?(decoder: PostboxDecoder) {
guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else {
@ -648,54 +667,65 @@ public enum MediaReference<T: Media> {
}
let id = decoder.decodeInt32ForKey("sid", orElse: 0)
self = .story(peer: peer, id: id, media: media)
case .starsTransaction:
let transaction = decoder.decodeObjectForKey("tr", decoder: { StarsTransactionReference(decoder: $0) }) as! StarsTransactionReference
guard let media = decoder.decodeObjectForKey("m") as? T else {
return nil
}
self = .starsTransaction(transaction: transaction, media: media)
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case let .standalone(media):
encoder.encodeInt32(CodingCase.standalone.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .message(message, media):
encoder.encodeInt32(CodingCase.message.rawValue, forKey: "_r")
encoder.encodeObject(message, forKey: "msg")
encoder.encodeObject(media, forKey: "m")
case let .webPage(webPage, media):
encoder.encodeInt32(CodingCase.webPage.rawValue, forKey: "_r")
encoder.encodeObject(webPage, forKey: "wpg")
encoder.encodeObject(media, forKey: "m")
case let .stickerPack(stickerPack, media):
encoder.encodeInt32(CodingCase.stickerPack.rawValue, forKey: "_r")
encoder.encodeObject(stickerPack, forKey: "spk")
encoder.encodeObject(media, forKey: "m")
case let .savedGif(media):
encoder.encodeInt32(CodingCase.savedGif.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .savedSticker(media):
encoder.encodeInt32(CodingCase.savedSticker.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .recentSticker(media):
encoder.encodeInt32(CodingCase.recentSticker.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .avatarList(peer, media):
encoder.encodeInt32(CodingCase.avatarList.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeObject(media, forKey: "m")
case let .attachBot(peer, media):
encoder.encodeInt32(CodingCase.attachBot.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeObject(media, forKey: "m")
case let .customEmoji(media):
encoder.encodeInt32(CodingCase.customEmoji.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .story(peer, id, media):
encoder.encodeInt32(CodingCase.story.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeInt32(id, forKey: "sid")
encoder.encodeObject(media, forKey: "m")
case let .standalone(media):
encoder.encodeInt32(CodingCase.standalone.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .message(message, media):
encoder.encodeInt32(CodingCase.message.rawValue, forKey: "_r")
encoder.encodeObject(message, forKey: "msg")
encoder.encodeObject(media, forKey: "m")
case let .webPage(webPage, media):
encoder.encodeInt32(CodingCase.webPage.rawValue, forKey: "_r")
encoder.encodeObject(webPage, forKey: "wpg")
encoder.encodeObject(media, forKey: "m")
case let .stickerPack(stickerPack, media):
encoder.encodeInt32(CodingCase.stickerPack.rawValue, forKey: "_r")
encoder.encodeObject(stickerPack, forKey: "spk")
encoder.encodeObject(media, forKey: "m")
case let .savedGif(media):
encoder.encodeInt32(CodingCase.savedGif.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .savedSticker(media):
encoder.encodeInt32(CodingCase.savedSticker.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .recentSticker(media):
encoder.encodeInt32(CodingCase.recentSticker.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .avatarList(peer, media):
encoder.encodeInt32(CodingCase.avatarList.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeObject(media, forKey: "m")
case let .attachBot(peer, media):
encoder.encodeInt32(CodingCase.attachBot.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeObject(media, forKey: "m")
case let .customEmoji(media):
encoder.encodeInt32(CodingCase.customEmoji.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .story(peer, id, media):
encoder.encodeInt32(CodingCase.story.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeInt32(id, forKey: "sid")
encoder.encodeObject(media, forKey: "m")
case let .starsTransaction(transaction, media):
encoder.encodeInt32(CodingCase.starsTransaction.rawValue, forKey: "_r")
encoder.encodeObject(transaction, forKey: "tr")
encoder.encodeObject(media, forKey: "m")
}
}
public var abstract: AnyMediaReference {
switch self {
case let .standalone(media):
@ -720,6 +750,8 @@ public enum MediaReference<T: Media> {
return .customEmoji(media: media)
case let .story(peer, id, media):
return .story(peer: peer, id: id, media: media)
case let .starsTransaction(transaction, media):
return .starsTransaction(transaction: transaction, media: media)
}
}
@ -751,6 +783,8 @@ public enum MediaReference<T: Media> {
return media
case let .story(_, _, media):
return media
case let .starsTransaction(_, media):
return media
}
}

View File

@ -1096,6 +1096,33 @@ public extension TelegramEngine.EngineData.Item {
}
}
public struct CanViewStarsRevenue: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Bool
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .cachedPeerData(peerId: self.id)
}
func extract(view: PostboxView) -> Result {
guard let view = view as? CachedPeerDataView else {
preconditionFailure()
}
if let cachedData = view.cachedPeerData as? CachedChannelData {
return cachedData.flags.contains(.canViewStarsRevenue)
} else {
return false
}
}
}
public struct PaidMediaAllowed: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Bool

View File

@ -763,3 +763,59 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
}
}
}
public struct StarsTransactionReference: PostboxCoding, Hashable, Equatable {
public let peerId: EnginePeer.Id
public let id: String
public let isRefund: Bool
public init(peerId: EnginePeer.Id, id: String, isRefund: Bool) {
self.peerId = peerId
self.id = id
self.isRefund = isRefund
}
public init(decoder: PostboxDecoder) {
self.peerId = EnginePeer.Id(decoder.decodeInt64ForKey("peerId", orElse: 0))
self.id = decoder.decodeStringForKey("id", orElse: "")
self.isRefund = decoder.decodeBoolForKey("refund", orElse: false)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64(self.peerId.toInt64(), forKey: "peerId")
encoder.encodeString(self.id, forKey: "id")
encoder.encodeBool(self.isRefund, forKey: "refund")
}
}
func _internal_getStarsTransaction(accountPeerId: PeerId, postbox: Postbox, network: Network, transactionReference: StarsTransactionReference) -> Signal<StarsContext.State.Transaction?, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(transactionReference.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<StarsContext.State.Transaction?, NoError> in
guard let inputPeer else {
return .single(nil)
}
return network.request(
Api.functions.payments.getStarsTransactionsByID(
peer: inputPeer,
id: [.inputStarsTransaction(flags: transactionReference.isRefund ? (1 << 0) : 0, id: transactionReference.id)]
)
)
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.payments.StarsStatus?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<StarsContext.State.Transaction?, NoError> in
return postbox.transaction { transaction -> StarsContext.State.Transaction? in
guard let result, case let .starsStatus(_, _, transactions, _, chats, users) = result, let matchingTransaction = transactions.first else {
return nil
}
let peers = AccumulatedPeers(chats: chats, users: users)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: peers)
return StarsContext.State.Transaction(apiTransaction: matchingTransaction, peerId: transactionReference.peerId, transaction: transaction)
}
}
}
}

View File

@ -592,7 +592,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
if (flags2 & Int32(1 << 14)) != 0 {
channelFlags.insert(.paidMediaAllowed)
}
if (flags2 & Int32(1 << 15)) != 0 {
channelFlags.insert(.canViewStarsRevenue)
}
let sendAsPeerId = defaultSendAs?.peerId
let linkedDiscussionPeerId: PeerId?

View File

@ -248,7 +248,7 @@ public final class StarsImageComponent: Component {
public enum Subject: Equatable {
case none
case photo(TelegramMediaWebFile)
case media([Media])
case media([AnyMediaReference])
case extendedMedia([TelegramExtendedMedia])
case transactionPeer(StarsContext.State.Transaction.Peer)
@ -267,7 +267,7 @@ public final class StarsImageComponent: Component {
return false
}
case let .media(lhsMedia):
if case let .media(rhsMedia) = rhs, areMediaArraysEqual(lhsMedia, rhsMedia) {
if case let .media(rhsMedia) = rhs, areMediaArraysEqual(lhsMedia.map { $0.media }, rhsMedia.map { $0.media }) {
return true
} else {
return false
@ -388,7 +388,7 @@ public final class StarsImageComponent: Component {
guard let component = self.component, let containerNode = self.containerNode else {
return nil
}
if case let .media(media) = component.subject, media.first?.id == transitionMedia.id {
if case let .media(media) = component.subject, media.first?.media.id == transitionMedia.id {
return (containerNode, containerNode.bounds, { [weak containerNode] in
return (containerNode?.view.snapshotContentTree(unhide: true), nil)
})
@ -477,25 +477,25 @@ public final class StarsImageComponent: Component {
containerNode.view.addSubview(imageNode.view)
self.imageNode = imageNode
}
if let image = media.first as? TelegramMediaImage {
if let imageDimensions = largestImageRepresentation(image.representations)?.dimensions {
if let imageReference = media.first?.concrete(TelegramMediaImage.self) {
if let imageDimensions = largestImageRepresentation(imageReference.media.representations)?.dimensions {
dimensions = imageDimensions.cgSize.aspectFilled(imageSize)
}
if isFirstTime {
imageNode.setSignal(chatMessagePhotoThumbnail(account: component.context.account, userLocation: .other, photoReference: .standalone(media: image), onlyFullSize: false, blurred: false))
imageNode.setSignal(chatMessagePhotoThumbnail(account: component.context.account, userLocation: .other, photoReference: imageReference, onlyFullSize: false, blurred: false))
}
} else if let file = media.first as? TelegramMediaFile {
if let videoDimensions = file.dimensions {
} else if let fileReference = media.first?.concrete(TelegramMediaFile.self) {
if let videoDimensions = fileReference.media.dimensions {
dimensions = videoDimensions.cgSize.aspectFilled(imageSize)
}
if isFirstTime {
imageNode.setSignal(mediaGridMessageVideo(postbox: component.context.account.postbox, userLocation: .other, videoReference: .standalone(media: file), useLargeThumbnail: true, autoFetchFullSizeThumbnail: true))
imageNode.setSignal(mediaGridMessageVideo(postbox: component.context.account.postbox, userLocation: .other, videoReference: fileReference, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true))
}
}
imageNode.frame = imageFrame
imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 16.0), imageSize: dimensions, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor))()
if let firstMedia = media.first, self.hiddenMedia.contains(where: { $0.id == firstMedia.id }) {
if let firstMedia = media.first?.media, self.hiddenMedia.contains(where: { $0.id == firstMedia.id }) {
containerNode.isHidden = true
} else {
containerNode.isHidden = false
@ -520,19 +520,19 @@ public final class StarsImageComponent: Component {
self.imageFrameNode = imageFrameNode
}
if let image = media[1] as? TelegramMediaImage {
if let imageDimensions = largestImageRepresentation(image.representations)?.dimensions {
if let imageReference = media[1].concrete(TelegramMediaImage.self) {
if let imageDimensions = largestImageRepresentation(imageReference.media.representations)?.dimensions {
secondDimensions = imageDimensions.cgSize.aspectFilled(imageSize)
}
if isFirstTime {
secondImageNode.setSignal(chatMessagePhotoThumbnail(account: component.context.account, userLocation: .other, photoReference: .standalone(media: image), onlyFullSize: false, blurred: false))
secondImageNode.setSignal(chatMessagePhotoThumbnail(account: component.context.account, userLocation: .other, photoReference: imageReference, onlyFullSize: false, blurred: false))
}
} else if let file = media[1] as? TelegramMediaFile {
if let videoDimensions = file.dimensions {
} else if let fileReference = media[1].concrete(TelegramMediaFile.self) {
if let videoDimensions = fileReference.media.dimensions {
secondDimensions = videoDimensions.cgSize.aspectFilled(imageSize)
}
if isFirstTime {
secondImageNode.setSignal(mediaGridMessageVideo(postbox: component.context.account.postbox, userLocation: .other, videoReference: .standalone(media: file), useLargeThumbnail: true, autoFetchFullSizeThumbnail: true))
secondImageNode.setSignal(mediaGridMessageVideo(postbox: component.context.account.postbox, userLocation: .other, videoReference: fileReference, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true))
}
}

View File

@ -183,7 +183,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let messageId: EngineMessage.Id?
let toPeer: EnginePeer?
let transactionPeer: StarsContext.State.Transaction.Peer?
let media: [Media]
let media: [AnyMediaReference]
let photo: TelegramMediaWebFile?
let isRefund: Bool
@ -264,7 +264,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
toPeer = nil
}
transactionPeer = transaction.peer
media = transaction.media
media = transaction.media.map { AnyMediaReference.starsTransaction(transaction: StarsTransactionReference(peerId: parentPeer.id, id: transaction.id, isRefund: transaction.flags.contains(.isRefund)), media: $0) }
photo = transaction.photo
isRefund = transaction.flags.contains(.isRefund)
case let .receipt(receipt):
@ -331,7 +331,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
diameter: 90.0,
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
action: !media.isEmpty ? { transitionNode, addToTransitionSurface in
component.openMedia(media, transitionNode, addToTransitionSurface)
component.openMedia(media.map { $0.media }, transitionNode, addToTransitionSurface)
} : nil
),
availableSize: CGSize(width: context.availableSize.width, height: 200.0),