Update API

This commit is contained in:
Ilya Laktyushin 2025-01-06 16:00:43 +04:00
parent 8cfec897ee
commit cdc82e4235
54 changed files with 1529 additions and 617 deletions

View File

@ -13641,3 +13641,12 @@ Sorry for the inconvenience.";
"Gift.View.Outgoing.NameHidden" = "Only %@ can see your name.";
"Gift.View.Outgoing.NameAndMessageHidden" = "Only %@ can see your name and message.";
"Conversation.ViewStarGift" = "VIEW COLLECTIBLE";
"ChatList.AddPhoto.Title" = "Add your photo! 📸";
"ChatList.AddPhoto.Text" = "Help your friends spot you easily.";
"Story.ViewGift" = "View Gift";
"Camera.OpenChat" = "Open Chat";

View File

@ -318,6 +318,7 @@ public enum ResolvedUrl {
case boost(peerId: PeerId?, status: ChannelBoostStatus?, myBoostStatus: MyBoostStatus?)
case premiumGiftCode(slug: String)
case premiumMultiGift(reference: String?)
case collectible(gift: StarGift.UniqueGift?)
case messageLink(link: TelegramResolvedMessageLink?)
}
@ -1099,10 +1100,13 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController
func makeStarsIntroScreen(context: AccountContext) -> ViewController
func makeGiftViewScreen(context: AccountContext, message: EngineMessage) -> ViewController
func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: (() -> Void)?) -> ViewController
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: (() -> Void)?) -> ViewController
func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void, requestSelectMessages: ((String, Data, String?) -> Void)?)
func makeShareController(context: AccountContext, subject: ShareControllerSubject, forceExternal: Bool, shareStory: (() -> Void)?, actionCompleted: (() -> Void)?) -> ViewController
func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError>
func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController
@ -1338,28 +1342,3 @@ public struct StickersSearchConfiguration {
}
}
}
public protocol ShareControllerAccountContext: AnyObject {
var accountId: AccountRecordId { get }
var accountPeerId: EnginePeer.Id { get }
var stateManager: AccountStateManager { get }
var engineData: TelegramEngine.EngineData { get }
var animationCache: AnimationCache { get }
var animationRenderer: MultiAnimationRenderer { get }
var contentSettings: ContentSettings { get }
var appConfiguration: AppConfiguration { get }
func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>
}
public protocol ShareControllerEnvironment: AnyObject {
var presentationData: PresentationData { get }
var updatedPresentationData: Signal<PresentationData, NoError> { get }
var isMainApp: Bool { get }
var energyUsageSettings: EnergyUsageSettings { get }
var mediaManager: MediaManager? { get }
func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable
func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id])
}

View File

@ -0,0 +1,55 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import AnimationCache
import MultiAnimationRenderer
public protocol ShareControllerAccountContext: AnyObject {
var accountId: AccountRecordId { get }
var accountPeerId: EnginePeer.Id { get }
var stateManager: AccountStateManager { get }
var engineData: TelegramEngine.EngineData { get }
var animationCache: AnimationCache { get }
var animationRenderer: MultiAnimationRenderer { get }
var contentSettings: ContentSettings { get }
var appConfiguration: AppConfiguration { get }
func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>
}
public protocol ShareControllerEnvironment: AnyObject {
var presentationData: PresentationData { get }
var updatedPresentationData: Signal<PresentationData, NoError> { get }
var isMainApp: Bool { get }
var energyUsageSettings: EnergyUsageSettings { get }
var mediaManager: MediaManager? { get }
func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable
func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id])
}
public enum ShareControllerExternalStatus {
case preparing(Bool)
case progress(Float)
case done
}
public enum ShareControllerError {
case generic
case fileTooBig(Int64)
}
public enum ShareControllerSubject {
case url(String)
case text(String)
case quote(text: String, url: String)
case messages([Message])
case image([ImageRepresentationWithReference])
case media(AnyMediaReference)
case mapMedia(TelegramMediaMap)
case fromExternal(([PeerId], [PeerId: Int64], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
}

View File

@ -6538,7 +6538,13 @@ private final class ChatListLocationContext {
if channel.flags.contains(.requestToJoin) {
actionTitle = presentationData.strings.Group_ApplyToJoin
} else {
actionTitle = presentationData.strings.Channel_JoinChannel
switch channel.info {
case .broadcast:
actionTitle = presentationData.strings.Channel_JoinChannel
case .group:
actionTitle = presentationData.strings.Group_JoinGroup
}
}
toolbar = Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: actionTitle, isEnabled: true))
}

View File

@ -289,9 +289,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
titleString = attributedTitle
textString = NSAttributedString(string: text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case let .setupPhoto(accountPeer):
//TODO:localize
titleString = NSAttributedString(string: "Add your photo! 📸", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
textString = NSAttributedString(string: "Help your friends spot you easily.", font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
titleString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Title, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
textString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
avatarPeer = accountPeer
}

View File

@ -98,7 +98,7 @@ swift_library(
"//submodules/TelegramUI/Components/LottieComponent",
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
"//submodules/ImageTransparency",
"//submodules/GalleryUI",
#"//submodules/GalleryUI",
"//submodules/MediaPlayer:UniversalMediaPlayer",
"//submodules/TelegramUniversalVideoContent",
"//submodules/TelegramUI/Components/CameraButtonComponent",

View File

@ -142,6 +142,8 @@ public class DrawingStickerEntityView: DrawingEntityView {
return image
} else if case .message = self.stickerEntity.content {
return self.animatedImageView?.image
} else if case .gift = self.stickerEntity.content {
return self.animatedImageView?.image
} else {
return nil
}
@ -167,7 +169,7 @@ public class DrawingStickerEntityView: DrawingEntityView {
return file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
case .dualVideoReference:
return CGSize(width: 512.0, height: 512.0)
case let .message(_, size, _, _, _):
case let .message(_, size, _, _, _), let .gift(_, size):
return size
}
}
@ -296,6 +298,43 @@ public class DrawingStickerEntityView: DrawingEntityView {
if let file, let _ = mediaRect {
self.setupWithVideo(file)
}
} else if case let .gift(gift, _) = self.stickerEntity.content {
if let image = self.stickerEntity.renderImage {
self.setupWithImage(image, overlayImage: self.stickerEntity.overlayRenderImage)
}
var file: TelegramMediaFile?
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
file = fileValue
break
}
}
guard let file, let dimensions = file.dimensions else {
return
}
if self.animationNode == nil {
let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.clipsToBounds = true
animationNode.autoplay = false
self.animationNode = animationNode
animationNode.started = { [weak self, weak animationNode] in
self?.imageNode.isHidden = true
if let animationNode = animationNode {
let _ = (animationNode.status
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] status in
self?.started?(status.duration)
})
}
}
self.addSubnode(self.imageNode)
self.addSubnode(animationNode)
}
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: self.context.account.postbox, userLocation: .other, file: file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 256.0, height: 256.0))))
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: stickerPackFileReference(file), resource: file.resource).start())
self.setNeedsLayout()
}
}
@ -418,7 +457,18 @@ public class DrawingStickerEntityView: DrawingEntityView {
if self.isPlaying != isPlaying {
self.isPlaying = isPlaying
if let file = self.file {
var file: TelegramMediaFile?
if let fileValue = self.file {
file = fileValue
} else if case let .gift(gift, _) = self.stickerEntity.content {
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
file = fileValue
break
}
}
}
if let file {
if isPlaying && !self.didSetUpAnimationNode {
self.didSetUpAnimationNode = true
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
@ -596,15 +646,22 @@ public class DrawingStickerEntityView: DrawingEntityView {
let imageSize = self.dimensions.aspectFitted(boundingSize)
let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize)
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
self.imageNode.frame = imageFrame
var animationSize = CGSize(width: imageSize.width, height: imageSize.width)
var animationFrame = imageFrame
if case .gift = self.stickerEntity.content {
animationSize = CGSize(width: animationSize.width * 0.48, height: animationSize.height * 0.48)
animationFrame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: 22.0), size: animationSize)
}
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: animationSize, boundingSize: animationSize, intrinsicInsets: UIEdgeInsets()))()
self.imageNode.frame = animationFrame
if let animationNode = self.animationNode {
if self.isReaction {
animationNode.cornerRadius = floor(imageSize.width * 0.1)
animationNode.cornerRadius = floor(animationSize.width * 0.1)
}
animationNode.frame = imageFrame
animationNode.updateLayout(size: imageSize)
animationNode.frame = animationFrame
animationNode.updateLayout(size: animationSize)
if !self.didApplyVisibility {
self.didApplyVisibility = true
@ -818,6 +875,35 @@ public class DrawingStickerEntityView: DrawingEntityView {
return entities
}
} else if case let .gift(gift, _) = self.stickerEntity.content {
var file: TelegramMediaFile?
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
file = fileValue
break
}
}
if let file, let animationNode = self.animationNode {
let stickerSize = self.bounds.size
let stickerPosition = self.stickerEntity.position
let videoSize = animationNode.frame.size
let scale = self.stickerEntity.scale
let rotation = self.stickerEntity.rotation
let videoPosition = animationNode.position.offsetBy(dx: -stickerSize.width / 2.0, dy: -stickerSize.height / 2.0)
let videoScale = videoSize.width / stickerSize.width
let videoEntity = DrawingStickerEntity(content: .file(.standalone(media: file), .sticker))
videoEntity.referenceDrawingSize = self.stickerEntity.referenceDrawingSize
videoEntity.position = stickerPosition.offsetBy(
dx: (videoPosition.x * cos(rotation) - videoPosition.y * sin(rotation)) * scale,
dy: (videoPosition.y * cos(rotation) + videoPosition.x * sin(rotation)) * scale
)
videoEntity.scale = scale * videoScale
videoEntity.rotation = rotation
return [videoEntity]
}
}
return []
}
@ -1103,6 +1189,9 @@ final class DrawingStickerEntitySelectionView: DrawingEntitySelectionView {
if case .message = entity.content {
cornerRadius *= 2.1
count = 24
} else if case .gift = entity.content {
cornerRadius *= 2.1
count = 24
} else if case .image = entity.content {
count = 24
}

View File

@ -147,7 +147,7 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity {
case let .image(image, _):
self.file = nil
self.imagePromise.set(.single(image))
case .animatedImage, .video, .dualVideoReference, .message:
case .animatedImage, .video, .dualVideoReference, .message, .gift:
self.file = nil
}
}

View File

@ -43,7 +43,9 @@ swift_library(
"//submodules/Components/MultilineTextComponent",
"//submodules/Components/BundleIconComponent",
"//submodules/TelegramUI/Components/LottieComponent",
#"//submodules/TelegramUI/Components/MessageInputPanelComponent",
"//submodules/TelegramUI/Components/MessageInputPanelComponent",
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
"//submodules/ChatPresentationInterfaceState",
],
visibility = [
"//visibility:public",

View File

@ -40,17 +40,6 @@ public enum ShareControllerPreferredAction {
case custom(action: ShareControllerAction)
}
public enum ShareControllerExternalStatus {
case preparing(Bool)
case progress(Float)
case done
}
public enum ShareControllerError {
case generic
case fileTooBig(Int64)
}
public struct ShareControllerSegmentedValue {
let title: String
let subject: ShareControllerSubject
@ -65,17 +54,6 @@ public struct ShareControllerSegmentedValue {
}
}
public enum ShareControllerSubject {
case url(String)
case text(String)
case quote(text: String, url: String)
case messages([Message])
case image([ImageRepresentationWithReference])
case media(AnyMediaReference)
case mapMedia(TelegramMediaMap)
case fromExternal(([PeerId], [PeerId: Int64], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
}
private enum ExternalShareItem {
case text(String)
case url(URL)

View File

@ -18,7 +18,6 @@ swift_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/ShareController:ShareController",
"//submodules/StickerResources:StickerResources",
"//submodules/AlertUI:AlertUI",
"//submodules/PresentationDataUtils:PresentationDataUtils",

View File

@ -7,7 +7,6 @@ import TelegramCore
import SwiftSignalKit
import TelegramUIPreferences
import AccountContext
import ShareController
import StickerResources
import AlertUI
import PresentationDataUtils
@ -116,15 +115,19 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
}
if let stickerPackContentsValue = strongSelf.stickerPackContentsValue, case let .result(info, _, _) = stickerPackContentsValue, !info.shortName.isEmpty {
let shareController = ShareController(context: strongSelf.context, subject: .url("https://t.me/addstickers/\(info.shortName)"), externalShare: true)
let parentNavigationController = strongSelf.parentNavigationController
shareController.actionCompleted = { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
let shareController = strongSelf.context.sharedContext.makeShareController(
context: strongSelf.context,
subject: .url("https://t.me/addstickers/\(info.shortName)"),
forceExternal: true,
shareStory: nil,
actionCompleted: { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
}
)
strongSelf.present(shareController, in: .window(.root))
strongSelf.dismiss()
}

View File

@ -13,7 +13,6 @@ import ShimmerEffect
import ContextUI
import MoreButtonNode
import UndoUI
import ShareController
import TextFormat
import PremiumUI
import OverlayStatusController
@ -1133,13 +1132,18 @@ private final class StickerPackContainer: ASDisplayNode {
if let strongSelf = self {
let parentNavigationController = strongSelf.controller?.parentNavigationController
let shareController = ShareController(context: strongSelf.context, subject: shareSubject)
shareController.actionCompleted = { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
let shareController = strongSelf.context.sharedContext.makeShareController(
context: strongSelf.context,
subject: shareSubject,
forceExternal: false,
shareStory: nil,
actionCompleted: { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
}
)
strongSelf.controller?.present(shareController, in: .window(.root))
}
})))

View File

@ -534,6 +534,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1300094593] = { return Api.MediaArea.parse_inputMediaAreaVenue($0) }
dict[1996756655] = { return Api.MediaArea.parse_mediaAreaChannelPost($0) }
dict[-891992787] = { return Api.MediaArea.parse_mediaAreaGeoPoint($0) }
dict[1468491885] = { return Api.MediaArea.parse_mediaAreaStarGift($0) }
dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) }
dict[926421125] = { return Api.MediaArea.parse_mediaAreaUrl($0) }
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
@ -909,7 +910,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1301522832] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
dict[46953416] = { return Api.StarGift.parse_starGift($0) }
dict[1779697613] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[880997154] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[-1809377438] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
dict[-1070837941] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }
@ -1156,6 +1157,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1355547603] = { return Api.WebPageAttribute.parse_webPageAttributeStickerSet($0) }
dict[781501415] = { return Api.WebPageAttribute.parse_webPageAttributeStory($0) }
dict[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($0) }
dict[-814781000] = { return Api.WebPageAttribute.parse_webPageAttributeUniqueStarGift($0) }
dict[211046684] = { return Api.WebViewMessageSent.parse_webViewMessageSent($0) }
dict[1294139288] = { return Api.WebViewResult.parse_webViewResultUrl($0) }
dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) }
@ -1361,6 +1363,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[870003448] = { return Api.messages.TranslatedText.parse_translateResult($0) }
dict[1218005070] = { return Api.messages.VotesList.parse_votesList($0) }
dict[-44166467] = { return Api.messages.WebPage.parse_webPage($0) }
dict[-1254192351] = { return Api.messages.WebPagePreview.parse_webPagePreview($0) }
dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) }
dict[675942550] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) }
dict[-1730811363] = { return Api.payments.ConnectedStarRefBots.parse_connectedStarRefBots($0) }
@ -1383,6 +1386,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[497778871] = { return Api.payments.StarsRevenueWithdrawalUrl.parse_starsRevenueWithdrawalUrl($0) }
dict[1822222573] = { return Api.payments.StarsStatus.parse_starsStatus($0) }
dict[-1261053863] = { return Api.payments.SuggestedStarRefBots.parse_suggestedStarRefBots($0) }
dict[-895289845] = { return Api.payments.UniqueStarGift.parse_uniqueStarGift($0) }
dict[1801827607] = { return Api.payments.UserStarGifts.parse_userStarGifts($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[541839704] = { return Api.phone.ExportedGroupCallInvite.parse_exportedGroupCallInvite($0) }
@ -2440,6 +2444,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.WebPage:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.WebPagePreview:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.BankCardData:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.CheckedGiftCode:
@ -2472,6 +2478,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.payments.SuggestedStarRefBots:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.UniqueStarGift:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.UserStarGifts:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.ValidatedRequestedInfo:

View File

@ -868,6 +868,7 @@ public extension Api {
case inputMediaAreaVenue(coordinates: Api.MediaAreaCoordinates, queryId: Int64, resultId: String)
case mediaAreaChannelPost(coordinates: Api.MediaAreaCoordinates, channelId: Int64, msgId: Int32)
case mediaAreaGeoPoint(flags: Int32, coordinates: Api.MediaAreaCoordinates, geo: Api.GeoPoint, address: Api.GeoPointAddress?)
case mediaAreaStarGift(coordinates: Api.MediaAreaCoordinates, slug: String)
case mediaAreaSuggestedReaction(flags: Int32, coordinates: Api.MediaAreaCoordinates, reaction: Api.Reaction)
case mediaAreaUrl(coordinates: Api.MediaAreaCoordinates, url: String)
case mediaAreaVenue(coordinates: Api.MediaAreaCoordinates, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
@ -908,6 +909,13 @@ public extension Api {
geo.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {address!.serialize(buffer, true)}
break
case .mediaAreaStarGift(let coordinates, let slug):
if boxed {
buffer.appendInt32(1468491885)
}
coordinates.serialize(buffer, true)
serializeString(slug, buffer: buffer, boxed: false)
break
case .mediaAreaSuggestedReaction(let flags, let coordinates, let reaction):
if boxed {
buffer.appendInt32(340088945)
@ -957,6 +965,8 @@ public extension Api {
return ("mediaAreaChannelPost", [("coordinates", coordinates as Any), ("channelId", channelId as Any), ("msgId", msgId as Any)])
case .mediaAreaGeoPoint(let flags, let coordinates, let geo, let address):
return ("mediaAreaGeoPoint", [("flags", flags as Any), ("coordinates", coordinates as Any), ("geo", geo as Any), ("address", address as Any)])
case .mediaAreaStarGift(let coordinates, let slug):
return ("mediaAreaStarGift", [("coordinates", coordinates as Any), ("slug", slug as Any)])
case .mediaAreaSuggestedReaction(let flags, let coordinates, let reaction):
return ("mediaAreaSuggestedReaction", [("flags", flags as Any), ("coordinates", coordinates as Any), ("reaction", reaction as Any)])
case .mediaAreaUrl(let coordinates, let url):
@ -1053,6 +1063,22 @@ public extension Api {
return nil
}
}
public static func parse_mediaAreaStarGift(_ reader: BufferReader) -> MediaArea? {
var _1: Api.MediaAreaCoordinates?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.MediaAreaCoordinates
}
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MediaArea.mediaAreaStarGift(coordinates: _1!, slug: _2!)
}
else {
return nil
}
}
public static func parse_mediaAreaSuggestedReaction(_ reader: BufferReader) -> MediaArea? {
var _1: Int32?
_1 = reader.readInt32()

View File

@ -575,7 +575,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?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?)
case starGiftUnique(id: Int64, title: String, num: Int32, ownerId: Int64, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32)
case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Int64?, ownerName: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -594,14 +594,17 @@ public extension Api {
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(lastSaleDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(upgradeStars!, buffer: buffer, boxed: false)}
break
case .starGiftUnique(let id, let title, let num, let ownerId, let attributes, let availabilityIssued, let availabilityTotal):
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let attributes, let availabilityIssued, let availabilityTotal):
if boxed {
buffer.appendInt32(1779697613)
buffer.appendInt32(880997154)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(slug, buffer: buffer, boxed: false)
serializeInt32(num, buffer: buffer, boxed: false)
serializeInt64(ownerId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(ownerId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(ownerName!, buffer: buffer, boxed: false)}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(attributes.count))
for item in attributes {
@ -617,8 +620,8 @@ public extension Api {
switch self {
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars):
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), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any)])
case .starGiftUnique(let id, let title, let num, let ownerId, let attributes, let availabilityIssued, let availabilityTotal):
return ("starGiftUnique", [("id", id as Any), ("title", title as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any)])
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let attributes, let availabilityIssued, let availabilityTotal):
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), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any)])
}
}
@ -663,31 +666,40 @@ public extension Api {
}
}
public static func parse_starGiftUnique(_ reader: BufferReader) -> StarGift? {
var _1: Int64?
_1 = reader.readInt64()
var _2: String?
_2 = parseString(reader)
var _3: Int32?
_3 = reader.readInt32()
var _4: Int64?
_4 = reader.readInt64()
var _5: [Api.StarGiftAttribute]?
var _1: Int32?
_1 = reader.readInt32()
var _2: Int64?
_2 = reader.readInt64()
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
var _5: Int32?
_5 = reader.readInt32()
var _6: Int64?
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt64() }
var _7: String?
if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) }
var _8: [Api.StarGiftAttribute]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
}
var _6: Int32?
_6 = reader.readInt32()
var _7: Int32?
_7 = reader.readInt32()
var _9: Int32?
_9 = reader.readInt32()
var _10: Int32?
_10 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.StarGift.starGiftUnique(id: _1!, title: _2!, num: _3!, ownerId: _4!, attributes: _5!, availabilityIssued: _6!, availabilityTotal: _7!)
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
let _c8 = _8 != nil
let _c9 = _9 != nil
let _c10 = _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, attributes: _8!, availabilityIssued: _9!, availabilityTotal: _10!)
}
else {
return nil

View File

@ -207,6 +207,7 @@ public extension Api {
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?)
case webPageAttributeUniqueStarGift(gift: Api.StarGift)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -242,6 +243,12 @@ public extension Api {
}}
if Int(flags) & Int(1 << 1) != 0 {settings!.serialize(buffer, true)}
break
case .webPageAttributeUniqueStarGift(let gift):
if boxed {
buffer.appendInt32(-814781000)
}
gift.serialize(buffer, true)
break
}
}
@ -253,6 +260,8 @@ public extension Api {
return ("webPageAttributeStory", [("flags", flags as Any), ("peer", peer as Any), ("id", id as Any), ("story", story as Any)])
case .webPageAttributeTheme(let flags, let documents, let settings):
return ("webPageAttributeTheme", [("flags", flags as Any), ("documents", documents as Any), ("settings", settings as Any)])
case .webPageAttributeUniqueStarGift(let gift):
return ("webPageAttributeUniqueStarGift", [("gift", gift as Any)])
}
}
@ -317,6 +326,19 @@ public extension Api {
return nil
}
}
public static func parse_webPageAttributeUniqueStarGift(_ reader: BufferReader) -> WebPageAttribute? {
var _1: Api.StarGift?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.StarGift
}
let _c1 = _1 != nil
if _c1 {
return Api.WebPageAttribute.webPageAttributeUniqueStarGift(gift: _1!)
}
else {
return nil
}
}
}
}

View File

@ -744,6 +744,54 @@ public extension Api.messages {
}
}
public extension Api.messages {
indirect enum WebPagePreview: TypeConstructorDescription {
case webPagePreview(media: Api.MessageMedia, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .webPagePreview(let media, let users):
if boxed {
buffer.appendInt32(-1254192351)
}
media.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .webPagePreview(let media, let users):
return ("webPagePreview", [("media", media as Any), ("users", users as Any)])
}
}
public static func parse_webPagePreview(_ reader: BufferReader) -> WebPagePreview? {
var _1: Api.MessageMedia?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.MessageMedia
}
var _2: [Api.User]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.messages.WebPagePreview.webPagePreview(media: _1!, users: _2!)
}
else {
return nil
}
}
}
}
public extension Api.payments {
enum BankCardData: TypeConstructorDescription {
case bankCardData(title: String, openUrls: [Api.BankCardOpenUrl])
@ -1538,45 +1586,3 @@ public extension Api.payments {
}
}
public extension Api.payments {
enum StarGiftUpgradePreview: TypeConstructorDescription {
case starGiftUpgradePreview(sampleAttributes: [Api.StarGiftAttribute])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .starGiftUpgradePreview(let sampleAttributes):
if boxed {
buffer.appendInt32(377215243)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(sampleAttributes.count))
for item in sampleAttributes {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .starGiftUpgradePreview(let sampleAttributes):
return ("starGiftUpgradePreview", [("sampleAttributes", sampleAttributes as Any)])
}
}
public static func parse_starGiftUpgradePreview(_ reader: BufferReader) -> StarGiftUpgradePreview? {
var _1: [Api.StarGiftAttribute]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.payments.StarGiftUpgradePreview.starGiftUpgradePreview(sampleAttributes: _1!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,45 @@
public extension Api.payments {
enum StarGiftUpgradePreview: TypeConstructorDescription {
case starGiftUpgradePreview(sampleAttributes: [Api.StarGiftAttribute])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .starGiftUpgradePreview(let sampleAttributes):
if boxed {
buffer.appendInt32(377215243)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(sampleAttributes.count))
for item in sampleAttributes {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .starGiftUpgradePreview(let sampleAttributes):
return ("starGiftUpgradePreview", [("sampleAttributes", sampleAttributes as Any)])
}
}
public static func parse_starGiftUpgradePreview(_ reader: BufferReader) -> StarGiftUpgradePreview? {
var _1: [Api.StarGiftAttribute]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.payments.StarGiftUpgradePreview.starGiftUpgradePreview(sampleAttributes: _1!)
}
else {
return nil
}
}
}
}
public extension Api.payments {
enum StarGifts: TypeConstructorDescription {
case starGifts(hash: Int32, gifts: [Api.StarGift])
@ -334,6 +376,54 @@ public extension Api.payments {
}
}
public extension Api.payments {
enum UniqueStarGift: TypeConstructorDescription {
case uniqueStarGift(gift: Api.StarGift, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .uniqueStarGift(let gift, let users):
if boxed {
buffer.appendInt32(-895289845)
}
gift.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .uniqueStarGift(let gift, let users):
return ("uniqueStarGift", [("gift", gift as Any), ("users", users as Any)])
}
}
public static func parse_uniqueStarGift(_ reader: BufferReader) -> UniqueStarGift? {
var _1: Api.StarGift?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.StarGift
}
var _2: [Api.User]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.payments.UniqueStarGift.uniqueStarGift(gift: _1!, users: _2!)
}
else {
return nil
}
}
}
}
public extension Api.payments {
enum UserStarGifts: TypeConstructorDescription {
case userStarGifts(flags: Int32, count: Int32, gifts: [Api.UserStarGift], nextOffset: String?, users: [Api.User])
@ -1776,121 +1866,3 @@ public extension Api.stats {
}
}
public extension Api.stats {
enum PublicForwards: TypeConstructorDescription {
case publicForwards(flags: Int32, count: Int32, forwards: [Api.PublicForward], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users):
if boxed {
buffer.appendInt32(-1828487648)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(forwards.count))
for item in forwards {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users):
return ("publicForwards", [("flags", flags as Any), ("count", count as Any), ("forwards", forwards as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
}
}
public static func parse_publicForwards(_ reader: BufferReader) -> PublicForwards? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.PublicForward]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PublicForward.self)
}
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: [Api.Chat]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _6: [Api.User]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.stats.PublicForwards.publicForwards(flags: _1!, count: _2!, forwards: _3!, nextOffset: _4, chats: _5!, users: _6!)
}
else {
return nil
}
}
}
}
public extension Api.stats {
enum StoryStats: TypeConstructorDescription {
case storyStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyStats(let viewsGraph, let reactionsByEmotionGraph):
if boxed {
buffer.appendInt32(1355613820)
}
viewsGraph.serialize(buffer, true)
reactionsByEmotionGraph.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyStats(let viewsGraph, let reactionsByEmotionGraph):
return ("storyStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)])
}
}
public static func parse_storyStats(_ reader: BufferReader) -> StoryStats? {
var _1: Api.StatsGraph?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _2: Api.StatsGraph?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.stats.StoryStats.storyStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,121 @@
public extension Api.stats {
enum PublicForwards: TypeConstructorDescription {
case publicForwards(flags: Int32, count: Int32, forwards: [Api.PublicForward], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users):
if boxed {
buffer.appendInt32(-1828487648)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(forwards.count))
for item in forwards {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .publicForwards(let flags, let count, let forwards, let nextOffset, let chats, let users):
return ("publicForwards", [("flags", flags as Any), ("count", count as Any), ("forwards", forwards as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
}
}
public static func parse_publicForwards(_ reader: BufferReader) -> PublicForwards? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.PublicForward]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PublicForward.self)
}
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: [Api.Chat]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _6: [Api.User]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.stats.PublicForwards.publicForwards(flags: _1!, count: _2!, forwards: _3!, nextOffset: _4, chats: _5!, users: _6!)
}
else {
return nil
}
}
}
}
public extension Api.stats {
enum StoryStats: TypeConstructorDescription {
case storyStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyStats(let viewsGraph, let reactionsByEmotionGraph):
if boxed {
buffer.appendInt32(1355613820)
}
viewsGraph.serialize(buffer, true)
reactionsByEmotionGraph.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyStats(let viewsGraph, let reactionsByEmotionGraph):
return ("storyStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)])
}
}
public static func parse_storyStats(_ reader: BufferReader) -> StoryStats? {
var _1: Api.StatsGraph?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _2: Api.StatsGraph?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.stats.StoryStats.storyStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!)
}
else {
return nil
}
}
}
}
public extension Api.stickers {
enum SuggestedShortName: TypeConstructorDescription {
case suggestedShortName(shortName: String)
@ -1276,57 +1394,3 @@ public extension Api.upload {
}
}
public extension Api.upload {
enum WebFile: TypeConstructorDescription {
case webFile(size: Int32, mimeType: String, fileType: Api.storage.FileType, mtime: Int32, bytes: Buffer)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .webFile(let size, let mimeType, let fileType, let mtime, let bytes):
if boxed {
buffer.appendInt32(568808380)
}
serializeInt32(size, buffer: buffer, boxed: false)
serializeString(mimeType, buffer: buffer, boxed: false)
fileType.serialize(buffer, true)
serializeInt32(mtime, buffer: buffer, boxed: false)
serializeBytes(bytes, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .webFile(let size, let mimeType, let fileType, let mtime, let bytes):
return ("webFile", [("size", size as Any), ("mimeType", mimeType as Any), ("fileType", fileType as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)])
}
}
public static func parse_webFile(_ reader: BufferReader) -> WebFile? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Api.storage.FileType?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.storage.FileType
}
var _4: Int32?
_4 = reader.readInt32()
var _5: Buffer?
_5 = parseBytes(reader)
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.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,57 @@
public extension Api.upload {
enum WebFile: TypeConstructorDescription {
case webFile(size: Int32, mimeType: String, fileType: Api.storage.FileType, mtime: Int32, bytes: Buffer)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .webFile(let size, let mimeType, let fileType, let mtime, let bytes):
if boxed {
buffer.appendInt32(568808380)
}
serializeInt32(size, buffer: buffer, boxed: false)
serializeString(mimeType, buffer: buffer, boxed: false)
fileType.serialize(buffer, true)
serializeInt32(mtime, buffer: buffer, boxed: false)
serializeBytes(bytes, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .webFile(let size, let mimeType, let fileType, let mtime, let bytes):
return ("webFile", [("size", size as Any), ("mimeType", mimeType as Any), ("fileType", fileType as Any), ("mtime", mtime as Any), ("bytes", bytes as Any)])
}
}
public static func parse_webFile(_ reader: BufferReader) -> WebFile? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Api.storage.FileType?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.storage.FileType
}
var _4: Int32?
_4 = reader.readInt32()
var _5: Buffer?
_5 = parseBytes(reader)
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.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!)
}
else {
return nil
}
}
}
}
public extension Api.users {
enum UserFull: TypeConstructorDescription {
case userFull(fullUser: Api.UserFull, chats: [Api.Chat], users: [Api.User])

View File

@ -2383,12 +2383,11 @@ public extension Api.functions.bots {
}
}
public extension Api.functions.bots {
static func getBotRecommendations(flags: Int32, bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.users.Users>) {
static func getBotRecommendations(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.users.Users>) {
let buffer = Buffer()
buffer.appendInt32(676707937)
serializeInt32(flags, buffer: buffer, boxed: false)
buffer.appendInt32(-1581840363)
bot.serialize(buffer, true)
return (FunctionDescription(name: "bots.getBotRecommendations", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.Users? in
return (FunctionDescription(name: "bots.getBotRecommendations", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.Users? in
let reader = BufferReader(buffer)
var result: Api.users.Users?
if let signature = reader.readInt32() {
@ -7063,9 +7062,9 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func getWebPagePreview(flags: Int32, message: String, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
static func getWebPagePreview(flags: Int32, message: String, entities: [Api.MessageEntity]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.WebPagePreview>) {
let buffer = Buffer()
buffer.appendInt32(-1956073268)
buffer.appendInt32(1460498287)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
@ -7073,11 +7072,11 @@ public extension Api.functions.messages {
for item in entities! {
item.serialize(buffer, true)
}}
return (FunctionDescription(name: "messages.getWebPagePreview", parameters: [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
return (FunctionDescription(name: "messages.getWebPagePreview", parameters: [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.WebPagePreview? in
let reader = BufferReader(buffer)
var result: Api.MessageMedia?
var result: Api.messages.WebPagePreview?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.MessageMedia
result = Api.parse(reader, signature: signature) as? Api.messages.WebPagePreview
}
return result
})
@ -9479,6 +9478,21 @@ public extension Api.functions.payments {
})
}
}
public extension Api.functions.payments {
static func getUniqueStarGift(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.UniqueStarGift>) {
let buffer = Buffer()
buffer.appendInt32(-1583919758)
serializeString(slug, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.getUniqueStarGift", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.UniqueStarGift? in
let reader = BufferReader(buffer)
var result: Api.payments.UniqueStarGift?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.payments.UniqueStarGift
}
return result
})
}
}
public extension Api.functions.payments {
static func getUserStarGift(msgId: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.UserStarGifts>) {
let buffer = Buffer()

View File

@ -542,6 +542,8 @@ func mediaAreaFromApiMediaArea(_ mediaArea: Api.MediaArea) -> MediaArea? {
return .channelMessage(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), messageId: EngineMessage.Id(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: messageId))
case let .mediaAreaWeather(coordinates, emoji, temperatureC, color):
return .weather(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), emoji: emoji, temperature: temperatureC, color: color)
case let .mediaAreaStarGift(coordinates, slug):
return .starGift(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), slug: slug)
}
}
@ -596,6 +598,8 @@ func apiMediaAreasFromMediaAreas(_ mediaAreas: [MediaArea], transaction: Transac
apiMediaAreas.append(.mediaAreaUrl(coordinates: inputCoordinates, url: url))
case let .weather(_, emoji, temperature, color):
apiMediaAreas.append(.mediaAreaWeather(coordinates: inputCoordinates, emoji: emoji, temperatureC: temperature, color: color))
case let .starGift(_, slug):
apiMediaAreas.append(.mediaAreaStarGift(coordinates: inputCoordinates, slug: slug))
}
}
return apiMediaAreas

View File

@ -23,6 +23,11 @@ func telegramMediaWebpageAttributeFromApiWebpageAttribute(_ attribute: Api.WebPa
var files: [TelegramMediaFile] = []
files = stickers.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }
return .stickerPack(TelegramMediaWebpageStickerPackAttribute(flags: flags, files: files))
case let .webPageAttributeUniqueStarGift(gift):
if let starGift = StarGift(apiStarGift: gift) {
return .starGift(TelegramMediaWebpageStarGiftAttribute(gift: starGift))
}
return nil
case .webPageAttributeStory:
return nil
}

View File

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

View File

@ -4,12 +4,14 @@ private enum TelegramMediaWebpageAttributeTypes: Int32 {
case unsupported
case theme
case stickerPack
case starGift
}
public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
case unsupported
case theme(TelegraMediaWebpageThemeAttribute)
case stickerPack(TelegramMediaWebpageStickerPackAttribute)
case starGift(TelegramMediaWebpageStarGiftAttribute)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("r", orElse: 0) {
@ -17,6 +19,8 @@ public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
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
}
@ -32,6 +36,9 @@ public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
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")
}
}
}
@ -127,6 +134,29 @@ public final class TelegramMediaWebpageStickerPackAttribute: PostboxCoding, Equa
}
}
public final class TelegramMediaWebpageStarGiftAttribute: PostboxCoding, Equatable {
public static func == (lhs: TelegramMediaWebpageStarGiftAttribute, rhs: TelegramMediaWebpageStarGiftAttribute) -> Bool {
if lhs.gift != rhs.gift {
return false
}
return true
}
public let gift: StarGift
public init(gift: StarGift) {
self.gift = gift
}
public init(decoder: PostboxDecoder) {
self.gift = decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.gift, forKey: "gift")
}
}
public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable {
public let url: String
public let displayUrl: String

View File

@ -152,6 +152,7 @@ public enum MediaArea: Codable, Equatable {
case channelMessage(coordinates: Coordinates, messageId: EngineMessage.Id)
case link(coordinates: Coordinates, url: String)
case weather(coordinates: Coordinates, emoji: String, temperature: Double, color: Int32)
case starGift(coordinates: Coordinates, slug: String)
public struct ReactionFlags: OptionSet {
public var rawValue: Int32
@ -174,6 +175,7 @@ public enum MediaArea: Codable, Equatable {
case channelMessage
case link
case weather
case starGift
}
public enum DecodingError: Error {
@ -210,6 +212,10 @@ public enum MediaArea: Codable, Equatable {
let temperature = try container.decode(Double.self, forKey: .temperature)
let color = try container.decodeIfPresent(Int32.self, forKey: .color) ?? 0
self = .weather(coordinates: coordinates, emoji: emoji, temperature: temperature, color: color)
case .starGift:
let coordinates = try container.decode(MediaArea.Coordinates.self, forKey: .coordinates)
let slug = try container.decode(String.self, forKey: .value)
self = .starGift(coordinates: coordinates, slug: slug)
}
}
@ -240,6 +246,10 @@ public enum MediaArea: Codable, Equatable {
try container.encode(emoji, forKey: .value)
try container.encode(temperature, forKey: .temperature)
try container.encode(color, forKey: .color)
case let .starGift(coordinates, slug):
try container.encode(MediaAreaType.starGift.rawValue, forKey: .type)
try container.encode(coordinates, forKey: .coordinates)
try container.encode(slug, forKey: .value)
}
}
}
@ -257,6 +267,8 @@ public extension MediaArea {
return coordinates
case let .weather(coordinates, _, _, _):
return coordinates
case let .starGift(coordinates, _):
return coordinates
}
}
}

View File

@ -204,7 +204,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
case id
case title
case number
case slug
case ownerPeerId
case ownerName
case attributes
case availability
}
@ -428,6 +430,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
encoder.encodeInt32(self.total, forKey: CodingKeys.total.rawValue)
}
}
public enum Owner: Equatable {
case peerId(EnginePeer.Id)
case name(String)
}
public enum DecodingError: Error {
case generic
@ -436,15 +443,17 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
public let id: Int64
public let title: String
public let number: Int32
public let ownerPeerId: EnginePeer.Id
public let slug: String
public let owner: Owner
public let attributes: [Attribute]
public let availability: Availability
public init(id: Int64, title: String, number: Int32, ownerPeerId: EnginePeer.Id, attributes: [Attribute], availability: Availability) {
public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability) {
self.id = id
self.title = title
self.number = number
self.ownerPeerId = ownerPeerId
self.slug = slug
self.owner = owner
self.attributes = attributes
self.availability = availability
}
@ -454,7 +463,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.id = try container.decode(Int64.self, forKey: .id)
self.title = try container.decode(String.self, forKey: .title)
self.number = try container.decode(Int32.self, forKey: .number)
self.ownerPeerId = EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(try container.decode(Int64.self, forKey: .ownerPeerId)))
self.slug = try container.decodeIfPresent(String.self, forKey: .slug) ?? ""
if let ownerId = try container.decodeIfPresent(Int64.self, forKey: .ownerPeerId) {
self.owner = .peerId(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)))
} else if let ownerName = try container.decodeIfPresent(String.self, forKey: .ownerName) {
self.owner = .name(ownerName)
} else {
self.owner = .name("Unknown")
}
self.attributes = try container.decode([UniqueGift.Attribute].self, forKey: .attributes)
self.availability = try container.decode(UniqueGift.Availability.self, forKey: .availability)
}
@ -463,7 +479,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.id = decoder.decodeInt64ForKey(CodingKeys.id.rawValue, orElse: 0)
self.title = decoder.decodeStringForKey(CodingKeys.title.rawValue, orElse: "")
self.number = decoder.decodeInt32ForKey(CodingKeys.number.rawValue, orElse: 0)
self.ownerPeerId = EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(decoder.decodeInt64ForKey(CodingKeys.ownerPeerId.rawValue, orElse: 0)))
self.slug = decoder.decodeStringForKey(CodingKeys.slug.rawValue, orElse: "")
if let ownerId = decoder.decodeOptionalInt64ForKey(CodingKeys.ownerPeerId.rawValue) {
self.owner = .peerId(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)))
} else if let ownerName = decoder.decodeOptionalStringForKey(CodingKeys.ownerName.rawValue) {
self.owner = .name(ownerName)
} else {
self.owner = .name("Unknown")
}
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
}
@ -473,7 +496,13 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
try container.encode(self.id, forKey: .id)
try container.encode(self.title, forKey: .title)
try container.encode(self.number, forKey: .number)
try container.encode(self.ownerPeerId.id._internalGetInt64Value(), forKey: .ownerPeerId)
try container.encode(self.slug, forKey: .slug)
switch self.owner {
case let .peerId(peerId):
try container.encode(peerId.id._internalGetInt64Value(), forKey: .ownerPeerId)
case let .name(name):
try container.encode(name, forKey: .ownerName)
}
try container.encode(self.attributes, forKey: .attributes)
try container.encode(self.availability, forKey: .availability)
}
@ -482,7 +511,13 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
encoder.encodeInt64(self.id, forKey: CodingKeys.id.rawValue)
encoder.encodeString(self.title, forKey: CodingKeys.title.rawValue)
encoder.encodeInt32(self.number, forKey: CodingKeys.number.rawValue)
encoder.encodeInt64(self.ownerPeerId.id._internalGetInt64Value(), forKey: CodingKeys.ownerPeerId.rawValue)
encoder.encodeString(self.slug, forKey: CodingKeys.slug.rawValue)
switch self.owner {
case let .peerId(peerId):
encoder.encodeInt64(peerId.id._internalGetInt64Value(), forKey: CodingKeys.ownerPeerId.rawValue)
case let .name(name):
encoder.encodeString(name, forKey: CodingKeys.ownerName.rawValue)
}
encoder.encodeObjectArray(self.attributes, forKey: CodingKeys.attributes.rawValue)
encoder.encodeObject(self.availability, forKey: CodingKeys.availability.rawValue)
}
@ -570,8 +605,16 @@ extension StarGift {
return nil
}
self = .generic(StarGift.Gift(id: id, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars))
case let .starGiftUnique(id, title, num, ownerId, attributes, availabilityIssued, availabilityTotal):
self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, ownerPeerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)), attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal)))
case let .starGiftUnique(_, id, title, slug, num, ownerId, ownerName, attributes, availabilityIssued, availabilityTotal):
let owner: StarGift.UniqueGift.Owner
if let ownerId {
owner = .peerId(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)))
} else if let ownerName {
owner = .name(ownerName)
} 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)))
}
}
}
@ -1311,3 +1354,29 @@ extension StarGift.UniqueGift.Attribute {
}
}
}
func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal<StarGift.UniqueGift?, NoError> {
return account.network.request(Api.functions.payments.getUniqueStarGift(slug: slug))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.payments.UniqueStarGift?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<StarGift.UniqueGift?, NoError> in
if let result = result {
switch result {
case let .uniqueStarGift(gift, users):
return account.postbox.transaction { transaction in
let parsedPeers = AccumulatedPeers(users: users)
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers)
guard case let .unique(uniqueGift) = StarGift(apiStarGift: gift) else {
return nil
}
return uniqueGift
}
}
} else {
return .single(nil)
}
}
}

View File

@ -132,5 +132,9 @@ public extension TelegramEngine {
public func starGiftUpgradePreview(giftId: Int64) -> Signal<[StarGift.UniqueGift.Attribute], NoError> {
return _internal_starGiftUpgradePreview(account: self.account, giftId: giftId)
}
public func getUniqueStarGift(slug: String) -> Signal<StarGift.UniqueGift?, NoError> {
return _internal_getUniqueStarGift(account: self.account, slug: slug)
}
}
}

View File

@ -155,8 +155,8 @@ public func webpagePreviewWithProgress(account: Account, urls: [String], webpage
}
return account.network.requestWithAdditionalInfo(Api.functions.messages.getWebPagePreview(flags: 0, message: urls.joined(separator: " "), entities: nil), info: .progress)
|> `catch` { _ -> Signal<NetworkRequestResult<Api.MessageMedia>, NoError> in
return .single(.result(.messageMediaEmpty))
|> `catch` { _ -> Signal<NetworkRequestResult<Api.messages.WebPagePreview>, NoError> in
return .single(.result(.webPagePreview(media: .messageMediaEmpty, users: [])))
}
|> mapToSignal { result -> Signal<WebpagePreviewWithProgressResult, NoError> in
switch result {
@ -169,36 +169,44 @@ public func webpagePreviewWithProgress(account: Account, urls: [String], webpage
return .complete()
}
case let .result(result):
if let preCachedResources = result.preCachedResources {
if case let .webPagePreview(result, _) = result, let preCachedResources = result.preCachedResources {
for (resource, data) in preCachedResources {
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
}
}
switch result {
case let .messageMediaWebPage(flags, webpage):
let _ = flags
if let media = telegramMediaWebpageFromApiWebpage(webpage), let url = media.content.url {
if case .Loaded = media.content {
return .single(.result(WebpagePreviewResult.Result(webpage: media, sourceUrl: url)))
} else {
return .single(.result(WebpagePreviewResult.Result(webpage: media, sourceUrl: url)))
|> then(
account.stateManager.updatedWebpage(media.webpageId)
|> take(1)
|> map { next -> WebpagePreviewWithProgressResult in
if let url = next.content.url {
return .result(WebpagePreviewResult.Result(webpage: next, sourceUrl: url))
} else {
return .result(nil)
}
case let .webPagePreview(media, users):
switch media {
case let .messageMediaWebPage(_, webpage):
return account.postbox.transaction { transaction -> Signal<WebpagePreviewWithProgressResult, NoError> in
let peers = AccumulatedPeers(users: users)
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: peers)
if let media = telegramMediaWebpageFromApiWebpage(webpage), let url = media.content.url {
if case .Loaded = media.content {
return .single(.result(WebpagePreviewResult.Result(webpage: media, sourceUrl: url)))
} else {
return .single(.result(WebpagePreviewResult.Result(webpage: media, sourceUrl: url)))
|> then(
account.stateManager.updatedWebpage(media.webpageId)
|> take(1)
|> map { next -> WebpagePreviewWithProgressResult in
if let url = next.content.url {
return .result(WebpagePreviewResult.Result(webpage: next, sourceUrl: url))
} else {
return .result(nil)
}
}
)
}
)
} else {
return .single(.result(nil))
}
}
} else {
|> switchToLatest
default:
return .single(.result(nil))
}
default:
return .single(.result(nil))
}
}
}

View File

@ -189,8 +189,7 @@ final class CameraCodeResultComponent: Component {
view.frame = CGRect(origin: CGPoint(x: 54.0, y: 9.0), size: titleSize)
}
//TODO:localize
let subtitleString = NSMutableAttributedString(string: "Open Chat >", font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
let subtitleString = NSMutableAttributedString(string: "\(presentationData.strings.Camera_OpenChat) >", font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
if let range = subtitleString.string.range(of: ">"), let arrowImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") {
subtitleString.addAttribute(.attachment, value: arrowImage, range: NSRange(range, in: subtitleString.string))
subtitleString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: subtitleString.string))

View File

@ -366,7 +366,6 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var buttonTitle = item.presentationData.strings.Notification_PremiumGift_View
var buttonIcon: String?
var ribbonTitle = ""
var hasServiceMessage = true
var textSpacing: CGFloat = 0.0
var isStarGift = false
@ -381,6 +380,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var uniquePatternColor: UIColor?
var uniquePatternFile: TelegramMediaFile?
let isStoryEntity = item.message.id.id == -1
var hasServiceMessage = !isStoryEntity
for media in item.message.media {
if let action = media as? TelegramMediaAction {
switch action.action {
@ -560,10 +562,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} else {
authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
}
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
text = "**\(uniqueGift.title) #\(uniqueGift.number)**"
ribbonTitle = item.presentationData.strings.Notification_StarGift_Gift
buttonTitle = item.presentationData.strings.Notification_StarGift_View
title = isStoryEntity ? uniqueGift.title : item.presentationData.strings.Notification_StarGift_Title(authorName).string
text = isStoryEntity ? "**Collectible #\(uniqueGift.number)**" : "**\(uniqueGift.title) #\(uniqueGift.number)**"
ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift
buttonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_View
modelTitle = item.presentationData.strings.Notification_StarGift_Model
backdropTitle = item.presentationData.strings.Notification_StarGift_Backdrop
symbolTitle = item.presentationData.strings.Notification_StarGift_Symbol
@ -697,6 +699,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
if !buttonTitle.isEmpty {
giftSize.height += 48.0
} else if isStoryEntity {
giftSize.height += 12.0
}
var labelRects = labelLayout.linesRects()
@ -754,7 +758,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
}
let overlayColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
let overlayColor = item.presentationData.theme.theme.overallDarkAppearance && uniquePatternFile == nil ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - giftSize.width) / 2.0), y: hasServiceMessage ? labelLayout.size.height + 12.0 : 0.0), size: giftSize)
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
@ -767,11 +771,12 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
let animationFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY - 16.0 + iconOffset), size: iconSize)
strongSelf.animationNode.frame = animationFrame
strongSelf.animationNode.isHidden = isStoryEntity
strongSelf.buttonNode.isHidden = buttonTitle.isEmpty
strongSelf.buttonTitleNode.isHidden = buttonTitle.isEmpty
if strongSelf.item == nil {
if strongSelf.item == nil && !isStoryEntity {
strongSelf.animationNode.started = { [weak self] in
if let strongSelf = self {
let current = CACurrentMediaTime()
@ -883,63 +888,78 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.dustNode = nil
}
var middleX = mediaBackgroundFrame.width / 2.0
if let (modelValueLayout, _) = modelValueLayoutAndApply, let (backdropValueLayout, _) = backdropValueLayoutAndApply, let (symbolValueLayout, _) = symbolValueLayoutAndApply {
let maxWidth = max(modelValueLayout.size.width, max(backdropValueLayout.size.width, symbolValueLayout.size.width))
middleX = min(mediaBackgroundFrame.width - maxWidth - 16.0, middleX)
let attributeSpacing: CGFloat = 6.0
let attributeVerticalSpacing: CGFloat = 22.0
var attributeMidpoints: [CGFloat] = []
func appendAttributeMidpoint(titleLayout: TextNodeLayout?, valueLayout: TextNodeLayout?) {
if let titleLayout, let valueLayout {
let totalWidth = titleLayout.size.width + attributeSpacing + valueLayout.size.width
let titleOffset = titleLayout.size.width + attributeSpacing / 2.0
let midpoint = (mediaBackgroundFrame.width - totalWidth) / 2.0 + titleOffset
attributeMidpoints.append(midpoint)
}
}
let titleMaxX: CGFloat = mediaBackgroundFrame.minX + middleX - 2.0
let valueMinX: CGFloat = mediaBackgroundFrame.minX + middleX + 3.0
appendAttributeMidpoint(titleLayout: modelTitleLayoutAndApply?.0, valueLayout: modelValueLayoutAndApply?.0)
appendAttributeMidpoint(titleLayout: backdropTitleLayoutAndApply?.0, valueLayout: backdropValueLayoutAndApply?.0)
appendAttributeMidpoint(titleLayout: symbolTitleLayoutAndApply?.0, valueLayout: symbolValueLayoutAndApply?.0)
let middleX = attributeMidpoints.isEmpty ? mediaBackgroundFrame.width / 2.0 : attributeMidpoints.reduce(0, +) / CGFloat(attributeMidpoints.count)
let titleMaxX: CGFloat = mediaBackgroundFrame.minX + middleX - attributeSpacing / 2.0
let valueMinX: CGFloat = mediaBackgroundFrame.minX + middleX + attributeSpacing / 2.0
if let (modelTitleLayout, modelTitleApply) = modelTitleLayoutAndApply {
if strongSelf.modelTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.modelTitleTextNode)
func positionAttributeNodes(
titleTextNode: TextNode,
valueTextNode: TextNode,
titleLayoutAndApply: (TextNodeLayout, () -> TextNode)?,
valueLayoutAndApply: (TextNodeLayout, () -> TextNode)?,
yOffset: CGFloat
) {
if let (titleLayout, titleApply) = titleLayoutAndApply {
if titleTextNode.supernode == nil {
strongSelf.addSubnode(titleTextNode)
}
let _ = titleApply()
titleTextNode.frame = CGRect(
origin: CGPoint(x: titleMaxX - titleLayout.size.width, y: clippingTextFrame.maxY + yOffset),
size: titleLayout.size
)
}
if let (valueLayout, valueApply) = valueLayoutAndApply {
if valueTextNode.supernode == nil {
strongSelf.addSubnode(valueTextNode)
}
let _ = valueApply()
valueTextNode.frame = CGRect(
origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + yOffset),
size: valueLayout.size
)
}
let _ = modelTitleApply()
strongSelf.modelTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - modelTitleLayout.size.width, y: clippingTextFrame.maxY + 10.0), size: modelTitleLayout.size)
}
if let (modelValueLayout, modelValueApply) = modelValueLayoutAndApply {
if strongSelf.modelValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.modelValueTextNode)
}
let _ = modelValueApply()
strongSelf.modelValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 10.0), size: modelValueLayout.size)
}
if let (backdropTitleLayout, backdropTitleApply) = backdropTitleLayoutAndApply {
if strongSelf.backdropTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.backdropTitleTextNode)
}
let _ = backdropTitleApply()
strongSelf.backdropTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - backdropTitleLayout.size.width, y: clippingTextFrame.maxY + 32.0), size: backdropTitleLayout.size)
}
if let (backdropValueLayout, backdropValueApply) = backdropValueLayoutAndApply {
if strongSelf.backdropValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.backdropValueTextNode)
}
let _ = backdropValueApply()
strongSelf.backdropValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 32.0), size: backdropValueLayout.size)
}
if let (symbolTitleLayout, symbolTitleApply) = symbolTitleLayoutAndApply {
if strongSelf.symbolTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.symbolTitleTextNode)
}
let _ = symbolTitleApply()
strongSelf.symbolTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - symbolTitleLayout.size.width, y: clippingTextFrame.maxY + 54.0), size: symbolTitleLayout.size)
}
if let (symbolValueLayout, symbolValueApply) = symbolValueLayoutAndApply {
if strongSelf.symbolValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.symbolValueTextNode)
}
let _ = symbolValueApply()
strongSelf.symbolValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 54.0), size: symbolValueLayout.size)
}
positionAttributeNodes(
titleTextNode: strongSelf.modelTitleTextNode,
valueTextNode: strongSelf.modelValueTextNode,
titleLayoutAndApply: modelTitleLayoutAndApply,
valueLayoutAndApply: modelValueLayoutAndApply,
yOffset: 10.0
)
positionAttributeNodes(
titleTextNode: strongSelf.backdropTitleTextNode,
valueTextNode: strongSelf.backdropValueTextNode,
titleLayoutAndApply: backdropTitleLayoutAndApply,
valueLayoutAndApply: backdropValueLayoutAndApply,
yOffset: 10.0 + attributeVerticalSpacing
)
positionAttributeNodes(
titleTextNode: strongSelf.symbolTitleTextNode,
valueTextNode: strongSelf.symbolValueTextNode,
titleLayoutAndApply: symbolTitleLayoutAndApply,
valueLayoutAndApply: symbolValueLayoutAndApply,
yOffset: 10.0 + attributeVerticalSpacing * 2
)
var buttonSize = CGSize(width: buttonTitleLayout.size.width + 38.0, height: 34.0)
var buttonOriginY = clippingTextFrame.maxY + 10.0
if modelTitleLayoutAndApply != nil {

View File

@ -457,6 +457,8 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
}
}
actionTitle = isEmoji ? item.presentationData.strings.Conversation_ViewEmojis : item.presentationData.strings.Conversation_ViewStickers
case "telegram_nft":
actionTitle = item.presentationData.strings.Conversation_ViewStarGift
default:
break
}

View File

@ -1381,6 +1381,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
break
case .premiumMultiGift:
break
case .collectible:
break
case .messageLink:
break
}

View File

@ -431,10 +431,9 @@ public final class GiftItemComponent: Component {
}
price = priceValue
case .uniqueGift:
//TODO:localize
buttonColor = UIColor.white
starsColor = UIColor.white
price = "Unique"
price = ""
}
let buttonSize = self.button.update(

View File

@ -42,6 +42,7 @@ swift_library(
"//submodules/ConfettiEffect",
"//submodules/TooltipUI",
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
"//submodules/MoreButtonNode",
],
visibility = [
"//visibility:public",

View File

@ -0,0 +1,143 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import ComponentFlow
import BundleIconComponent
import MultilineTextComponent
import MoreButtonNode
import AccountContext
import TelegramPresentationData
final class ButtonsComponent: Component {
let theme: PresentationTheme
let isOverlay: Bool
let showMoreButton: Bool
let closePressed: () -> Void
let morePressed: (ASDisplayNode, ContextGesture?) -> Void
init(
theme: PresentationTheme,
isOverlay: Bool,
showMoreButton: Bool,
closePressed: @escaping () -> Void,
morePressed: @escaping (ASDisplayNode, ContextGesture?) -> Void
) {
self.theme = theme
self.isOverlay = isOverlay
self.showMoreButton = showMoreButton
self.closePressed = closePressed
self.morePressed = morePressed
}
static func ==(lhs: ButtonsComponent, rhs: ButtonsComponent) -> Bool {
return lhs.theme === rhs.theme && lhs.isOverlay == rhs.isOverlay && lhs.showMoreButton == rhs.showMoreButton
}
final class View: UIView {
private let backgroundView = UIView()
private let closeButton = HighlightTrackingButton()
private let closeIcon = UIImageView()
private let moreNode = MoreButtonNode(theme: defaultPresentationTheme, size: CGSize(width: 36.0, height: 36.0), encircled: false)
private var component: ButtonsComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundView.clipsToBounds = true
self.addSubview(self.backgroundView)
self.closeIcon.image = generateCloseButtonImage()
self.moreNode.updateColor(.white, transition: .immediate)
self.backgroundView.addSubview(self.moreNode.view)
self.backgroundView.addSubview(self.closeButton)
self.backgroundView.addSubview(self.closeIcon)
self.closeButton.highligthedChanged = { [weak self] highlighted in
guard let self else {
return
}
if highlighted {
self.closeIcon.layer.removeAnimation(forKey: "opacity")
self.closeIcon.alpha = 0.6
} else {
self.closeIcon.alpha = 1.0
self.closeIcon.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
}
}
self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func closePressed() {
guard let component = self.component else {
return
}
component.closePressed()
}
func update(component: ButtonsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.component = component
self.state = state
let backgroundSize = CGSize(width: component.showMoreButton ? 70.0 : 30.0, height: 30.0)
self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0
let backgroundColor: UIColor = component.isOverlay ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x808084, alpha: 0.1)
let foregroundColor: UIColor = component.isOverlay ? .white : component.theme.actionSheet.inputClearButtonColor
transition.setBackgroundColor(view: self.backgroundView, color: backgroundColor)
transition.setTintColor(view: self.closeIcon, color: foregroundColor)
let backgroundFrame = CGRect(origin: .zero, size: backgroundSize)
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
transition.setFrame(view: self.moreNode.view, frame: CGRect(origin: CGPoint(x: -7.0, y: -4.0), size: CGSize(width: 36.0, height: 36.0)))
transition.setAlpha(view: self.moreNode.view, alpha: component.showMoreButton ? 1.0 : 0.0)
self.moreNode.action = { [weak self] node, gesture in
guard let self, let component = self.component else {
return
}
component.morePressed(node, gesture)
}
let closeFrame = CGRect(origin: CGPoint(x: backgroundSize.width - 30.0 - (component.showMoreButton ? 3.0 : 0.0), y: 0.0), size: CGSize(width: 30.0, height: 30.0))
transition.setFrame(view: self.closeIcon, frame: closeFrame)
transition.setFrame(view: self.closeButton, frame: closeFrame)
return backgroundSize
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
private func generateCloseButtonImage() -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setStrokeColor(UIColor.white.cgColor)
context.move(to: CGPoint(x: 10.0, y: 10.0))
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
context.strokePath()
context.move(to: CGPoint(x: 20.0, y: 10.0))
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
context.strokePath()
})?.withRenderingMode(.alwaysTemplate)
}

View File

@ -29,6 +29,7 @@ import CheckComponent
import TooltipUI
import GiftAnimationComponent
import LottieComponent
import ContextUI
private let modelButtonTag = GenericComponentViewTag()
private let backdropButtonTag = GenericComponentViewTag()
@ -50,6 +51,7 @@ private final class GiftViewSheetContent: CombinedComponent {
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
let showAttributeInfo: (Any, Float) -> Void
let viewUpgraded: (EngineMessage.Id) -> Void
let openMore: (ASDisplayNode, ContextGesture?) -> Void
let getController: () -> ViewController?
init(
@ -66,6 +68,7 @@ private final class GiftViewSheetContent: CombinedComponent {
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
showAttributeInfo: @escaping (Any, Float) -> Void,
viewUpgraded: @escaping (EngineMessage.Id) -> Void,
openMore: @escaping (ASDisplayNode, ContextGesture?) -> Void,
getController: @escaping () -> ViewController?
) {
self.context = context
@ -81,6 +84,7 @@ private final class GiftViewSheetContent: CombinedComponent {
self.upgradeGift = upgradeGift
self.showAttributeInfo = showAttributeInfo
self.viewUpgraded = viewUpgraded
self.openMore = openMore
self.getController = getController
}
@ -108,8 +112,6 @@ private final class GiftViewSheetContent: CombinedComponent {
var cachedCircleImage: UIImage?
var cachedStarImage: (UIImage, PresentationTheme)?
var cachedCloseImage: (UIImage, PresentationTheme)?
var cachedOverlayCloseImage: UIImage?
var cachedChevronImage: (UIImage, PresentationTheme)?
var cachedSmallChevronImage: (UIImage, PresentationTheme)?
@ -158,12 +160,17 @@ private final class GiftViewSheetContent: CombinedComponent {
self.keepOriginalInfo = true
}
var peerIds: [EnginePeer.Id] = [arguments.peerId, context.account.peerId]
var peerIds: [EnginePeer.Id] = [context.account.peerId]
if let peerId = arguments.peerId {
peerIds.append(peerId)
}
if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) {
peerIds.append(fromPeerId)
}
if case let .unique(gift) = arguments.gift {
peerIds.append(gift.ownerPeerId)
if case let .peerId(peerId) = gift.owner {
peerIds.append(peerId)
}
for attribute in gift.attributes {
if case let .originalInfo(senderPeerId, recipientPeerId, _, _, _) = attribute {
if let senderPeerId {
@ -272,10 +279,10 @@ private final class GiftViewSheetContent: CombinedComponent {
}
func commitUpgrade() {
guard let arguments = self.subject.arguments, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
return
}
let peerId = arguments.peerId
let proceed: (Int64?) -> Void = { formId in
self.inProgress = true
self.updated()
@ -332,7 +339,7 @@ private final class GiftViewSheetContent: CombinedComponent {
}
static var body: Body {
let closeButton = Child(Button.self)
let buttons = Child(ButtonsComponent.self)
let animation = Child(GiftCompositionComponent.self)
let title = Child(MultilineTextComponent.self)
let description = Child(MultilineTextComponent.self)
@ -436,22 +443,7 @@ private final class GiftViewSheetContent: CombinedComponent {
convertStars = nil
titleString = ""
}
let closeImage: UIImage
let closeOverlayImage: UIImage
if let (image, theme) = state.cachedCloseImage, theme === environment.theme {
closeImage = image
} else {
closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)!
state.cachedCloseImage = (closeImage, theme)
}
if let image = state.cachedOverlayCloseImage {
closeOverlayImage = image
} else {
closeOverlayImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: .white)!
state.cachedOverlayCloseImage = closeOverlayImage
}
var showUpgradePreview = false
if state.inUpgradePreview, let _ = state.sampleGiftAttributes {
showUpgradePreview = true
@ -460,10 +452,12 @@ private final class GiftViewSheetContent: CombinedComponent {
}
let cancel = component.cancel
let closeButton = closeButton.update(
component: Button(
content: AnyComponent(Image(image: showUpgradePreview || uniqueGift != nil ? closeOverlayImage : closeImage)),
action: { [weak state] in
let buttons = buttons.update(
component: ButtonsComponent(
theme: theme,
isOverlay: showUpgradePreview || uniqueGift != nil,
showMoreButton: uniqueGift != nil,
closePressed: { [weak state] in
guard let state else {
return
}
@ -473,10 +467,13 @@ private final class GiftViewSheetContent: CombinedComponent {
} else {
cancel(true)
}
},
morePressed: { node, gesture in
component.openMore(node, gesture)
}
),
availableSize: CGSize(width: 30.0, height: 30.0),
transition: .immediate
transition: context.transition
)
var originY: CGFloat = 0.0
@ -888,70 +885,81 @@ private final class GiftViewSheetContent: CombinedComponent {
if !soldOut {
if let uniqueGift {
if let peer = state.peerMap[uniqueGift.ownerPeerId] {
let ownerComponent: AnyComponent<Empty>
if let _ = subject.arguments?.transferStars {
ownerComponent = AnyComponent(
HStack([
AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(Button(
content: AnyComponent(
PeerCellComponent(
switch uniqueGift.owner {
case let .peerId(peerId):
if let peer = state.peerMap[peerId] {
let ownerComponent: AnyComponent<Empty>
if let _ = subject.arguments?.transferStars {
ownerComponent = AnyComponent(
HStack([
AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(Button(
content: AnyComponent(
PeerCellComponent(
context: component.context,
theme: theme,
strings: strings,
peer: peer
)
),
action: {
component.openPeer(peer)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
))
),
AnyComponentWithIdentity(
id: AnyHashable(1),
component: AnyComponent(Button(
content: AnyComponent(ButtonContentComponent(
context: component.context,
theme: theme,
strings: strings,
peer: peer
)
),
action: {
component.openPeer(peer)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
))
text: strings.Gift_Unique_Transfer,
color: theme.list.itemAccentColor
)),
action: {
component.transferGift()
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
))
)
], spacing: 4.0)
)
} else {
ownerComponent = AnyComponent(Button(
content: AnyComponent(
PeerCellComponent(
context: component.context,
theme: theme,
strings: strings,
peer: peer
)
),
AnyComponentWithIdentity(
id: AnyHashable(1),
component: AnyComponent(Button(
content: AnyComponent(ButtonContentComponent(
context: component.context,
text: strings.Gift_Unique_Transfer,
color: theme.list.itemAccentColor
)),
action: {
component.transferGift()
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
))
)
], spacing: 4.0)
)
} else {
ownerComponent = AnyComponent(Button(
content: AnyComponent(
PeerCellComponent(
context: component.context,
theme: theme,
strings: strings,
peer: peer
)
),
action: {
component.openPeer(peer)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
action: {
component.openPeer(peer)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
}
))
}
tableItems.append(.init(
id: "owner",
title: strings.Gift_Unique_Owner,
component: ownerComponent
))
}
case let .name(name):
tableItems.append(.init(
id: "owner",
id: "anon_owner",
title: strings.Gift_Unique_Owner,
component: ownerComponent
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)))
)
))
}
} else if let peerId = subject.arguments?.fromPeerId, let peer = state.peerMap[peerId] {
@ -1593,8 +1601,8 @@ private final class GiftViewSheetContent: CombinedComponent {
originY += buttonChild.size.height
originY += 7.0
context.add(closeButton
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
context.add(buttons
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - 16.0 - buttons.size.width / 2.0, y: 28.0))
)
let contentSize = CGSize(width: context.availableSize.width, height: originY + 5.0 + environment.safeInsets.bottom)
@ -1618,6 +1626,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
let transferGift: () -> Void
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
let viewUpgraded: (EngineMessage.Id) -> Void
let openMore: (ASDisplayNode, ContextGesture?) -> Void
let showAttributeInfo: (Any, Float) -> Void
init(
@ -1632,6 +1641,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
transferGift: @escaping () -> Void,
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
viewUpgraded: @escaping (EngineMessage.Id) -> Void,
openMore: @escaping (ASDisplayNode, ContextGesture?) -> Void,
showAttributeInfo: @escaping (Any, Float) -> Void
) {
self.context = context
@ -1645,6 +1655,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
self.transferGift = transferGift
self.upgradeGift = upgradeGift
self.viewUpgraded = viewUpgraded
self.openMore = openMore
self.showAttributeInfo = showAttributeInfo
}
@ -1695,6 +1706,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
upgradeGift: context.component.upgradeGift,
showAttributeInfo: context.component.showAttributeInfo,
viewUpgraded: context.component.viewUpgraded,
openMore: context.component.openMore,
getController: controller
)),
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
@ -1764,11 +1776,12 @@ private final class GiftViewSheetComponent: CombinedComponent {
public class GiftViewScreen: ViewControllerComponentContainer {
public enum Subject: Equatable {
case message(EngineMessage)
case uniqueGift(StarGift.UniqueGift)
case profileGift(EnginePeer.Id, ProfileGiftsContext.State.StarGift)
case soldOutGift(StarGift.Gift)
case upgradePreview([StarGift.UniqueGift.Attribute], String)
var arguments: (peerId: EnginePeer.Id, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, canExportDate: Int32?, upgradeMessageId: Int32?)? {
var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, canExportDate: Int32?, upgradeMessageId: Int32?)? {
switch self {
case let .message(message):
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
@ -1793,6 +1806,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
return nil
}
}
case let .uniqueGift(gift):
return (nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, false, false, false, nil, nil, nil, nil)
case let .profileGift(peerId, gift):
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, gift.messageId, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, gift.canExportDate, nil)
case .soldOutGift:
@ -1817,7 +1832,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
updateSavedToProfile: ((EngineMessage.Id, Bool) -> Void)? = nil,
convertToStars: (() -> Void)? = nil,
transferGift: ((Bool, EnginePeer.Id) -> Void)? = nil,
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil,
shareStory: (() -> Void)? = nil
) {
self.context = context
self.subject = subject
@ -1831,6 +1847,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
var transferGiftImpl: (() -> Void)?
var showAttributeInfoImpl: ((Any, Float) -> Void)?
var upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
var openMoreImpl: ((ASDisplayNode, ContextGesture?) -> Void)?
var viewUpgradedImpl: ((EngineMessage.Id) -> Void)?
super.init(
@ -1865,6 +1882,9 @@ public class GiftViewScreen: ViewControllerComponentContainer {
viewUpgraded: { messageId in
viewUpgradedImpl?(messageId)
},
openMore: { node, gesture in
openMoreImpl?(node, gesture)
},
showAttributeInfo: { tag, rarity in
showAttributeInfoImpl?(tag, rarity)
}
@ -2151,6 +2171,66 @@ public class GiftViewScreen: ViewControllerComponentContainer {
})
self.present(controller, in: .current)
}
openMoreImpl = { [weak self] node, gesture in
guard let self, let arguments = self.subject.arguments, case let .unique(gift) = arguments.gift else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
let link = "https://t.me/nft/\(gift.slug)"
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: "Copy Link", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
guard let self else {
return
}
UIPasteboard.general.string = link
self.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
})))
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
guard let self else {
return
}
let shareController = context.sharedContext.makeShareController(
context: context,
subject: .url(link),
forceExternal: false,
shareStory: shareStory,
actionCompleted: { [weak self] in
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
}
)
self.present(shareController, in: .window(.root))
})))
if let _ = arguments.transferStars {
items.append(.action(ContextMenuActionItem(text: "Transfer", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in
c?.dismiss(completion: nil)
transferGiftImpl?()
})))
}
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: self, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.presentInGlobalOverlay(contextController)
}
}
required public init(coder aDecoder: NSCoder) {
@ -2298,6 +2378,7 @@ private final class TableComponent: CombinedComponent {
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
let secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
var leftColumnWidth: CGFloat = 0.0
@ -2388,7 +2469,7 @@ private final class TableComponent: CombinedComponent {
if hasLastBackground {
let lastRowHeight = rowHeights[i - 1] ?? 0
let lastBackground = lastBackground.update(
component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor),
component: Rectangle(color: secondaryBackgroundColor),
availableSize: CGSize(width: context.availableSize.width, height: lastRowHeight),
transition: context.transition
)
@ -2399,7 +2480,7 @@ private final class TableComponent: CombinedComponent {
}
let leftColumnBackground = leftColumnBackground.update(
component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor),
component: Rectangle(color: secondaryBackgroundColor),
availableSize: CGSize(width: leftColumnWidth, height: innerTotalHeight),
transition: context.transition
)
@ -2597,27 +2678,6 @@ private final class PeerCellComponent: Component {
}
}
private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(backgroundColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setStrokeColor(foregroundColor.cgColor)
context.move(to: CGPoint(x: 10.0, y: 10.0))
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
context.strokePath()
context.move(to: CGPoint(x: 20.0, y: 10.0))
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
context.strokePath()
})
}
private final class ButtonContentComponent: Component {
let context: AccountContext
let text: String
@ -2914,3 +2974,17 @@ private final class ParagraphComponent: CombinedComponent {
}
}
}
private final class GiftViewContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ASDisplayNode
init(controller: ViewController, sourceNode: ASDisplayNode) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -180,6 +180,11 @@ public enum CodableDrawingEntity: Equatable {
coordinates: coordinates,
messageId: messageId
)
} else if case let .gift(gift, _) = entity.content {
return .starGift(
coordinates: coordinates,
slug: gift.slug
)
} else {
return nil
}

View File

@ -39,6 +39,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
case video(TelegramMediaFile)
case dualVideoReference(Bool)
case message([MessageId], CGSize, TelegramMediaFile?, CGRect?, CGFloat?)
case gift(StarGift.UniqueGift, CGSize)
public static func == (lhs: Content, rhs: Content) -> Bool {
switch lhs {
@ -78,6 +79,12 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
} else {
return false
}
case let .gift(lhsGift, lhsSize):
if case let .gift(rhsGift, rhsSize) = rhs {
return lhsGift == rhsGift && lhsSize == rhsSize
} else {
return false
}
}
}
}
@ -98,6 +105,9 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
case messageSize
case messageMediaRect
case messageMediaCornerRadius
case gift
case referenceDrawingSize
case position
case scale
@ -120,6 +130,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
self.scale = max(0.59, min(1.77, self.scale))
} else if case .message = self.content {
self.scale = max(2.5, self.scale)
} else if case .gift = self.content {
self.scale = max(2.5, self.scale)
}
}
}
@ -164,6 +176,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
dimensions = CGSize(width: 512.0, height: 512.0)
case let .message(_, size, _, _, _):
dimensions = size
case let .gift(_, size):
dimensions = size
}
let boundingSize = CGSize(width: size, height: size)
@ -191,7 +205,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
return true
case .dualVideoReference:
return true
case .message:
case .message, .gift:
return !(self.renderSubEntities ?? []).isEmpty
}
}
@ -202,7 +216,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
return imageType == .rectangle
case .video:
return true
case .message:
case .message, .gift:
return true
default:
return false
@ -232,7 +246,10 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(UUID.self, forKey: .uuid)
if let messageIds = try container.decodeIfPresent([MessageId].self, forKey: .messageIds) {
if let gift = try container.decodeIfPresent(StarGift.UniqueGift.self, forKey: .gift) {
let size = try container.decodeIfPresent(CGSize.self, forKey: .messageSize) ?? .zero
self.content = .gift(gift, size)
} else if let messageIds = try container.decodeIfPresent([MessageId].self, forKey: .messageIds) {
let size = try container.decodeIfPresent(CGSize.self, forKey: .messageSize) ?? .zero
let file = try container.decodeIfPresent(TelegramMediaFile.self, forKey: .messageFile)
let mediaRect = try container.decodeIfPresent(CGRect.self, forKey: .messageMediaRect)
@ -343,6 +360,9 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
try container.encodeIfPresent(file, forKey: .messageFile)
try container.encodeIfPresent(mediaRect, forKey: .messageMediaRect)
try container.encodeIfPresent(mediaCornerRadius, forKey: .messageMediaCornerRadius)
case let .gift(gift, size):
try container.encode(gift, forKey: .gift)
try container.encode(size, forKey: .messageSize)
}
try container.encode(self.referenceDrawingSize, forKey: .referenceDrawingSize)
try container.encode(self.position, forKey: .position)

View File

@ -87,17 +87,26 @@ public final class DrawingMessageRenderer {
private let isNight: Bool
private let isOverlay: Bool
private let isLink: Bool
private let isGift: Bool
private let messagesContainerNode: ASDisplayNode
private var avatarHeaderNode: ListViewItemHeaderNode?
private var messageNodes: [ListViewItemNode]?
init(context: AccountContext, messages: [Message], isNight: Bool = false, isOverlay: Bool = false, isLink: Bool = false) {
init(
context: AccountContext,
messages: [Message],
isNight: Bool = false,
isOverlay: Bool = false,
isLink: Bool = false,
isGift: Bool = false
) {
self.context = context
self.messages = messages
self.isNight = isNight
self.isOverlay = isOverlay
self.isLink = isLink
self.isGift = isGift
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true
@ -198,7 +207,13 @@ public final class DrawingMessageRenderer {
let avatarHeaderItem: ListViewItemHeader?
if let author = self.messages.first?.author {
avatarHeaderItem = self.context.sharedContext.makeChatMessageAvatarHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, peer: self.messages.first!.peers[author.id]!, message: self.messages.first!, theme: theme, strings: presentationData.strings, wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder)
let avatarPeer: Peer
if let peer = self.messages.first!.peers[author.id] {
avatarPeer = peer
} else {
avatarPeer = author
}
avatarHeaderItem = self.context.sharedContext.makeChatMessageAvatarHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, peer: avatarPeer, message: self.messages.first!, theme: theme, strings: presentationData.strings, wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder)
} else {
avatarHeaderItem = nil
}
@ -208,6 +223,8 @@ public final class DrawingMessageRenderer {
var leftInset: CGFloat = 37.0
if self.isLink {
leftInset = -6.0
} else if self.isGift {
leftInset = -50.0
}
let containerWidth = layout.size.width - inset * 2.0
let params = ListViewItemLayoutParams(width: containerWidth, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
@ -329,13 +346,19 @@ public final class DrawingMessageRenderer {
private let nightContainerNode: ContainerNode
private let overlayContainerNode: ContainerNode
public init(context: AccountContext, messages: [Message], parentView: UIView, isLink: Bool = false) {
public init(
context: AccountContext,
messages: [Message],
parentView: UIView,
isLink: Bool = false,
isGift: Bool = false
) {
self.context = context
self.messages = messages
self.dayContainerNode = ContainerNode(context: context, messages: messages, isLink: isLink)
self.nightContainerNode = ContainerNode(context: context, messages: messages, isNight: true, isLink: isLink)
self.overlayContainerNode = ContainerNode(context: context, messages: messages, isOverlay: true, isLink: isLink)
self.dayContainerNode = ContainerNode(context: context, messages: messages, isLink: isLink, isGift: isGift)
self.nightContainerNode = ContainerNode(context: context, messages: messages, isNight: true, isLink: isLink, isGift: isGift)
self.overlayContainerNode = ContainerNode(context: context, messages: messages, isOverlay: true, isLink: isLink, isGift: isGift)
parentView.addSubview(self.dayContainerNode.view)
parentView.addSubview(self.nightContainerNode.view)

View File

@ -168,6 +168,7 @@ public final class MediaEditor {
case asset(PHAsset)
case draft(MediaEditorDraft)
case message(MessageId)
case gift(StarGift.UniqueGift)
case sticker(TelegramMediaFile)
var dimensions: PixelDimensions {
@ -178,7 +179,7 @@ public final class MediaEditor {
return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight))
case let .draft(draft):
return draft.dimensions
case .message, .sticker, .videoCollage:
case .message, .gift, .sticker, .videoCollage:
return PixelDimensions(width: 1080, height: 1920)
}
}
@ -817,7 +818,7 @@ public final class MediaEditor {
player = self.makePlayer(asset: asset)
}
}
return getChatWallpaperImage(context: self.context, messageId: messageId)
return getChatWallpaperImage(context: self.context, peerId: messageId.peerId)
|> map { _, image, nightImage in
return TextureSourceResult(
image: image,
@ -828,6 +829,17 @@ public final class MediaEditor {
)
}
}
case .gift:
textureSource = getChatWallpaperImage(context: self.context, peerId: self.context.account.peerId)
|> map { _, image, nightImage in
return TextureSourceResult(
image: image,
nightImage: nightImage,
player: nil,
playerIsReference: true,
gradientColors: GradientColors(top: .black, bottom: .black)
)
}
case let .sticker(file):
let entity = MediaEditorComposerStickerEntity(
postbox: self.context.account.postbox,

View File

@ -94,7 +94,7 @@ func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, enti
content = .video(file)
case .dualVideoReference:
return []
case .message:
case .message, .gift:
if let renderImage = entity.renderImage, let image = CIImage(image: renderImage, options: [.colorSpace: colorSpace]) {
var entities: [MediaEditorComposerEntity] = []
entities.append(MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: entity.baseSize, mirrored: false))

View File

@ -135,7 +135,7 @@ func getTextureImage(device: MTLDevice, texture: MTLTexture, mirror: Bool = fals
return UIImage(cgImage: cgImage)
}
public func getChatWallpaperImage(context: AccountContext, messageId: EngineMessage.Id) -> Signal<(CGSize, UIImage?, UIImage?), NoError> {
public func getChatWallpaperImage(context: AccountContext, peerId: EnginePeer.Id) -> Signal<(CGSize, UIImage?, UIImage?), NoError> {
let themeSettings = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|> map { sharedData -> PresentationThemeSettings in
let themeSettings: PresentationThemeSettings
@ -148,7 +148,7 @@ public func getChatWallpaperImage(context: AccountContext, messageId: EngineMess
}
let peerWallpaper = context.account.postbox.transaction { transaction -> TelegramWallpaper? in
return (transaction.getPeerCachedData(peerId: messageId.peerId) as? CachedChannelData)?.wallpaper
return (transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData)?.wallpaper
}
return combineLatest(themeSettings, peerWallpaper)

View File

@ -203,7 +203,7 @@ extension MediaEditorScreenImpl {
} else if let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) {
innerSaveDraft(media: .image(image: image, dimensions: draft.dimensions))
}
case .message:
case .message, .gift:
if let pixel = generateSingleColorImage(size: CGSize(width: 1, height: 1), color: .black) {
innerSaveDraft(media: .image(image: pixel, dimensions: PixelDimensions(width: 1080, height: 1920)))
}

View File

@ -433,7 +433,6 @@ final class MediaEditorScreenComponent: Component {
}
},
dismissTextInput: {
},
insertText: { [weak self] text in
if let self {
@ -1661,7 +1660,17 @@ final class MediaEditorScreenComponent: Component {
var topButtonOffsetX: CGFloat = 0.0
var topButtonOffsetY: CGFloat = 0.0
if let subject = controller.node.subject, case .message = subject {
var hasDayNightSelection = false
if let subject = controller.node.subject {
switch subject {
case .message, .gift:
hasDayNightSelection = true
default:
break
}
}
if hasDayNightSelection {
let isNightTheme = mediaEditor?.values.nightTheme == true
let dayNightContentComponent: AnyComponentWithIdentity<Empty>
@ -3145,6 +3154,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
isSavingAvailable = true
case .message:
isSavingAvailable = true
case .gift:
isSavingAvailable = true
default:
isSavingAvailable = false
}
@ -3237,8 +3248,13 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
mediaEditor.seek(initialVideoPosition, andPlay: true)
}
}
if case .message = subject, self.context.sharedContext.currentPresentationData.with({$0}).autoNightModeTriggered {
mediaEditor.setNightTheme(true)
if self.context.sharedContext.currentPresentationData.with({$0}).autoNightModeTriggered {
switch subject {
case .message, .gift:
mediaEditor.setNightTheme(true)
default:
break
}
}
mediaEditor.valuesUpdated = { [weak self] values in
if let self, let controller = self.controller, values.gradientColors != nil, controller.previousSavedValues != values {
@ -3284,28 +3300,33 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
self.stickerMaskDrawingView?.clearWithEmptyColor()
}
if case .message = effectiveSubject {
} else {
switch effectiveSubject {
case .message, .gift:
break
default:
self.readyValue.set(.single(true))
}
if case let .image(_, _, additionalImage, position) = effectiveSubject, let additionalImage {
let image = generateImage(CGSize(width: additionalImage.size.width, height: additionalImage.size.width), contextGenerator: { size, context in
let bounds = CGRect(origin: .zero, size: size)
context.clear(bounds)
context.addEllipse(in: bounds)
context.clip()
if let cgImage = additionalImage.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(x: (size.width - additionalImage.size.width) / 2.0, y: (size.height - additionalImage.size.height) / 2.0), size: additionalImage.size))
}
}, scale: 1.0)
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage, .dualPhoto))
imageEntity.referenceDrawingSize = storyDimensions
imageEntity.scale = 1.625
imageEntity.position = position.getPosition(storyDimensions)
self.entitiesView.add(imageEntity, announce: false)
} else if case let .video(_, _, mirror, additionalVideoPath, _, _, _, changes, position) = effectiveSubject {
switch effectiveSubject {
case let .image(_, _, additionalImage, position):
if let additionalImage {
let image = generateImage(CGSize(width: additionalImage.size.width, height: additionalImage.size.width), contextGenerator: { size, context in
let bounds = CGRect(origin: .zero, size: size)
context.clear(bounds)
context.addEllipse(in: bounds)
context.clip()
if let cgImage = additionalImage.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(x: (size.width - additionalImage.size.width) / 2.0, y: (size.height - additionalImage.size.height) / 2.0), size: additionalImage.size))
}
}, scale: 1.0)
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage, .dualPhoto))
imageEntity.referenceDrawingSize = storyDimensions
imageEntity.scale = 1.625
imageEntity.position = position.getPosition(storyDimensions)
self.entitiesView.add(imageEntity, announce: false)
}
case let .video(_, _, mirror, additionalVideoPath, _, _, _, changes, position):
mediaEditor.setVideoIsMirrored(mirror)
if let additionalVideoPath {
let videoEntity = DrawingStickerEntity(content: .dualVideoReference(false))
@ -3324,24 +3345,41 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
}
}
}
} else if case let .videoCollage(items) = effectiveSubject {
case let .videoCollage(items):
mediaEditor.setupCollage(items.map { $0.editorItem })
} else if case let .message(messageIds) = effectiveSubject {
case let .sticker(_, emoji):
controller.stickerSelectedEmoji = emoji
case .message, .gift:
var isGift = false
let messages: Signal<[Message], NoError>
if case let .message(messageIds) = effectiveSubject {
messages = self.context.engine.data.get(
EngineDataMap(messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init(id:)))
)
|> map { result in
var messages: [Message] = []
for id in messageIds {
if let maybeMessage = result[id], let message = maybeMessage {
messages.append(message._asMessage())
}
}
return messages
}
} else if case let .gift(gift) = effectiveSubject {
isGift = true
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false))]
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 {
fatalError()
}
let isNightTheme = mediaEditor.values.nightTheme
let _ = ((self.context.engine.data.get(
EngineDataMap(messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init(id:)))
))
|> deliverOnMainQueue).start(next: { [weak self] result in
let _ = (messages
|> deliverOnMainQueue).start(next: { [weak self] messages in
guard let self else {
return
}
var messages: [Message] = []
for id in messageIds {
if let maybeMessage = result[id], let message = maybeMessage {
messages.append(message._asMessage())
}
}
var messageFile: TelegramMediaFile?
if let maybeFile = messages.first?.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile, maybeFile.isVideo, let _ = self.context.account.postbox.mediaBox.completedResourcePath(maybeFile.resource, pathExtension: nil) {
messageFile = maybeFile
@ -3350,7 +3388,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
messageFile = nil
}
let renderer = DrawingMessageRenderer(context: self.context, messages: messages, parentView: self.view)
let renderer = DrawingMessageRenderer(context: self.context, messages: messages, parentView: self.view, isGift: isGift)
renderer.render(completion: { result in
if case .draft = subject, let existingEntityView = self.entitiesView.getView(where: { entityView in
if let stickerEntityView = entityView as? DrawingStickerEntityView, case .message = (stickerEntityView.entity as! DrawingStickerEntity).content {
@ -3366,12 +3404,25 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
messageEntity.overlayRenderImage = result.overlayImage
existingEntityView.update(animated: false)
} else {
let messageEntity = DrawingStickerEntity(content: .message(messageIds, result.size, messageFile, result.mediaFrame?.rect, result.mediaFrame?.cornerRadius))
var content: DrawingStickerEntity.Content
var position: CGPoint
switch effectiveSubject {
case let .message(messageIds):
content = .message(messageIds, result.size, messageFile, result.mediaFrame?.rect, result.mediaFrame?.cornerRadius)
position = CGPoint(x: storyDimensions.width / 2.0 - 54.0, y: storyDimensions.height / 2.0)
case let .gift(gift):
content = .gift(gift, result.size)
position = CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
default:
fatalError()
}
let messageEntity = DrawingStickerEntity(content: content)
messageEntity.renderImage = result.dayImage
messageEntity.secondaryRenderImage = result.nightImage
messageEntity.overlayRenderImage = result.overlayImage
messageEntity.referenceDrawingSize = storyDimensions
messageEntity.position = CGPoint(x: storyDimensions.width / 2.0 - 54.0, y: storyDimensions.height / 2.0)
messageEntity.position = position
let fraction = max(result.size.width, result.size.height) / 353.0
messageEntity.scale = min(6.0, 3.3 * fraction)
@ -3386,10 +3437,10 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
self.readyValue.set(.single(true))
})
})
} else if case let .sticker(_, emoji) = effectiveSubject {
controller.stickerSelectedEmoji = emoji
default:
break
}
self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in
if let self, let colors {
let gradientImage = generateGradientImage(size: CGSize(width: 5.0, height: 640.0), colors: colors.array, locations: [0.0, 1.0])
@ -3749,10 +3800,12 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
}
private var canEnhance: Bool {
if case .message = self.subject {
switch self.subject {
case .message, .gift:
return false
default:
return true
}
return true
}
private var enhanceInitialTranslation: Float?
@ -3856,8 +3909,13 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
guard !self.isCollageTimelineOpen else {
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, case .message = subject, !self.entitiesView.hasSelection {
return
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
switch subject {
case .message, .gift:
return
default:
break
}
}
let currentTimestamp = CACurrentMediaTime()
if let previousPanTimestamp = self.previousPanTimestamp, currentTimestamp - previousPanTimestamp < 0.016, case .changed = gestureRecognizer.state {
@ -3871,8 +3929,13 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
guard !self.isCollageTimelineOpen else {
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, case .message = subject, !self.entitiesView.hasSelection {
return
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
switch subject {
case .message, .gift:
return
default:
break
}
}
let currentTimestamp = CACurrentMediaTime()
if let previousPinchTimestamp = self.previousPinchTimestamp, currentTimestamp - previousPinchTimestamp < 0.016, case .changed = gestureRecognizer.state {
@ -3886,8 +3949,13 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
guard !self.isCollageTimelineOpen else {
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, case .message = subject, !self.entitiesView.hasSelection {
return
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
switch subject {
case .message, .gift:
return
default:
break
}
}
let currentTimestamp = CACurrentMediaTime()
if let previousRotateTimestamp = self.previousRotateTimestamp, currentTimestamp - previousRotateTimestamp < 0.016, case .changed = gestureRecognizer.state {
@ -4072,7 +4140,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
var animateIn = false
if let subject {
switch subject {
case .empty, .message, .sticker, .image:
case .empty, .message, .gift, .sticker, .image:
animateIn = true
default:
break
@ -5924,6 +5992,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
case asset(PHAsset)
case draft(MediaEditorDraft, Int64?)
case message([MessageId])
case gift(StarGift.UniqueGift)
case sticker(TelegramMediaFile, [String])
var dimensions: PixelDimensions {
@ -5936,7 +6005,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight))
case let .draft(draft, _):
return draft.dimensions
case .message, .sticker, .videoCollage:
case .message, .gift, .sticker, .videoCollage:
return PixelDimensions(width: 1080, height: 1920)
}
}
@ -5960,6 +6029,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return .draft(draft)
case let .message(messageIds):
return .message(messageIds.first!)
case let .gift(gift):
return .gift(gift)
case let .sticker(sticker, _):
return .sticker(sticker)
}
@ -5985,6 +6056,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return draft.isVideo
case .message:
return false
case .gift:
return false
case .sticker:
return false
}
@ -6189,9 +6262,12 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
if self.isEditingStory {
needsAudioSession = true
}
if case .message = subject {
switch subject {
case .message, .gift:
needsAudioSession = true
checkPostingAvailability = true
default:
break
}
if needsAudioSession {
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .record(speaker: false, video: true, withOthers: true), activate: { _ in
@ -7195,9 +7271,16 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
firstFrame = .single((UIImage(), nil))
}
}
case let .message(messages):
case .message, .gift:
let peerId: EnginePeer.Id
if case let .message(messageIds) = subject {
peerId = messageIds.first!.peerId
} else {
peerId = self.context.account.peerId
}
let isNightTheme = mediaEditor.values.nightTheme
let wallpaper = getChatWallpaperImage(context: self.context, messageId: messages.first!)
let wallpaper = getChatWallpaperImage(context: self.context, peerId: peerId)
|> map { _, image, nightImage -> UIImage? in
if isNightTheme {
return nightImage ?? image
@ -8055,7 +8138,18 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
}
case let .message(messages):
let isNightTheme = mediaEditor.values.nightTheme
exportSubject = getChatWallpaperImage(context: self.context, messageId: messages.first!)
exportSubject = getChatWallpaperImage(context: self.context, peerId: messages.first!.peerId)
|> mapToSignal { _, image, nightImage -> Signal<MediaEditorVideoExport.Subject, NoError> in
if isNightTheme {
let effectiveImage = nightImage ?? image
return effectiveImage.flatMap({ .single(.image(image: $0)) }) ?? .complete()
} else {
return image.flatMap({ .single(.image(image: $0)) }) ?? .complete()
}
}
case .gift:
let isNightTheme = mediaEditor.values.nightTheme
exportSubject = getChatWallpaperImage(context: self.context, peerId: self.context.account.peerId)
|> mapToSignal { _, image, nightImage -> Signal<MediaEditorVideoExport.Subject, NoError> in
if isNightTheme {
let effectiveImage = nightImage ?? image

View File

@ -3342,14 +3342,14 @@ final class StoryItemSetContainerSendMessage {
useGesturePosition = true
let action = { [weak self, weak view, weak controller] in
let _ = ((context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: true))
|> mapToSignal { result -> Signal<Message?, GetMessagesError> in
|> mapToSignal { result -> Signal<Message?, GetMessagesError> in
if case let .result(messages) = result {
return .single(messages.first)
} else {
return .complete()
}
})
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak view, weak controller] message in
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak view, weak controller] message in
guard let self, let view else {
return
}
@ -3365,7 +3365,7 @@ final class StoryItemSetContainerSendMessage {
switch error {
case .privateChannel:
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak view] peer in
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak view] peer in
guard let self, let view else {
return
}
@ -3416,6 +3416,31 @@ final class StoryItemSetContainerSendMessage {
}))
case .weather:
return
case let .starGift(_, slug):
useGesturePosition = true
let action = {
let _ = openUserGeneratedUrl(context: component.context, peerId: nil, url: "https://t.me/nft/\(slug)", concealed: false, skipUrlAuth: false, skipConcealedAlert: false, forceDark: true, present: { [weak controller] c in
controller?.present(c, in: .window(.root))
}, openResolved: { [weak self, weak view] resolved in
guard let self, let view else {
return
}
self.openResolved(view: view, result: resolved, forceExternal: false, concealed: false)
}, alertDisplayUpdated: { [weak self, weak view] alertController in
guard let self, let view else {
return
}
self.statusController = alertController
view.updateIsProgressPaused()
})
}
if immediate {
action()
return
}
actions.append(ContextMenuAction(content: .textWithIcon(title: updatedPresentationData.initial.strings.Story_ViewGift, icon: generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: .white)), action: {
action()
}))
}
self.selectedMediaArea = mediaArea

View File

@ -27,10 +27,21 @@ import StoryContainerScreen
import SaveToCameraRoll
import MediaEditorScreen
enum StorySharingSubject {
case messages([Message])
case gift(StarGift.UniqueGift)
}
extension ChatControllerImpl {
func openStorySharing(messages: [Message]) {
func openStorySharing(subject: StorySharingSubject) {
let context = self.context
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError> = .single(.message(messages.map { $0.id }))
let editorSubject: Signal<MediaEditorScreenImpl.Subject?, NoError>
switch subject {
case let .messages(messages):
editorSubject = .single(.message(messages.map { $0.id }))
case let .gift(gift):
editorSubject = .single(.gift(gift))
}
let externalState = MediaEditorTransitionOutExternalState(
storyTarget: nil,
@ -42,7 +53,7 @@ extension ChatControllerImpl {
let controller = MediaEditorScreenImpl(
context: context,
mode: .storyEditor,
subject: subject,
subject: editorSubject,
transitionIn: nil,
transitionOut: { _, _ in
return nil

View File

@ -1198,7 +1198,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.push(controller)
return true
case .starGift, .starGiftUnique:
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message))
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message), shareStory: { [weak self] in
if let self, case let .starGiftUnique(gift, _, _, _, _, _, _) = action.action, case let .unique(uniqueGift) = gift {
Queue.mainQueue().after(0.15) {
self.openStorySharing(subject: .gift(uniqueGift))
}
}
})
strongSelf.push(controller)
return true
case .giftStars:

View File

@ -134,7 +134,7 @@ extension ChatControllerImpl {
return
}
Queue.mainQueue().after(0.15) {
self.openStorySharing(messages: messages)
self.openStorySharing(subject: .messages(messages))
}
}
}

View File

@ -1211,6 +1211,13 @@ func openResolvedUrlImpl(
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
})
case let .collectible(gift):
if let gift {
let controller = context.sharedContext.makeGiftViewScreen(context: context, gift: gift, shareStory: nil)
navigationController?.pushViewController(controller)
} else {
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
case let .messageLink(link):
if let link {
if let navigationController = navigationController {

View File

@ -655,6 +655,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
convertedUrl = "https://t.me/addtheme/\(parameter)"
}
}
} else if parsedUrl.host == "nft" {
if let components = URLComponents(string: "/?" + query) {
var slug: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "slug" {
slug = value
}
}
}
}
if let slug {
convertedUrl = "https://t.me/nft/\(slug)"
}
}
} else if parsedUrl.host == "privatepost" {
if let components = URLComponents(string: "/?" + query) {
var channelId: Int64?

View File

@ -76,6 +76,7 @@ import StarsIntroScreen
import ContentReportScreen
import AffiliateProgramSetupScreen
import GalleryUI
import ShareController
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -2922,8 +2923,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StarsIntroScreen(context: context)
}
public func makeGiftViewScreen(context: AccountContext, message: EngineMessage) -> ViewController {
return GiftViewScreen(context: context, subject: .message(message))
public func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: (() -> Void)?) -> ViewController {
return GiftViewScreen(context: context, subject: .message(message), shareStory: shareStory)
}
public func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: (() -> Void)?) -> ViewController {
return GiftViewScreen(context: context, subject: .uniqueGift(gift), shareStory: shareStory)
}
public func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void, requestSelectMessages: ((String, Data, String?) -> Void)?) {
@ -2935,6 +2940,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
})
}
public func makeShareController(context: AccountContext, subject: ShareControllerSubject, forceExternal: Bool, shareStory: (() -> Void)?, actionCompleted: (() -> Void)?) -> ViewController {
let controller = ShareController(context: context, subject: subject, externalShare: forceExternal)
controller.shareStory = shareStory
controller.actionCompleted = actionCompleted
return controller
}
public func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError> {
return MiniAppListScreen.initialData(context: context)
}

View File

@ -105,6 +105,7 @@ public enum ParsedInternalUrl {
case chatFolder(slug: String)
case premiumGiftCode(slug: String)
case messageLink(slug: String)
case collectible(slug: String)
case externalUrl(url: String)
}
@ -523,6 +524,8 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou
return .wallpaper(parameter)
} else if pathComponents[0] == "addtheme" {
return .theme(pathComponents[1])
} else if pathComponents[0] == "nft" {
return .collectible(slug: pathComponents[1])
} else if pathComponents[0] == "addlist" || pathComponents[0] == "folder" || pathComponents[0] == "list" {
return .chatFolder(slug: pathComponents[1])
} else if pathComponents[0] == "boost", pathComponents.count == 2 {
@ -1086,6 +1089,11 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
}
case let .premiumGiftCode(slug):
return .single(.result(.premiumGiftCode(slug: slug)))
case let .collectible(slug):
return .single(.progress) |> then(context.engine.payments.getUniqueStarGift(slug: slug)
|> map { gift -> ResolveInternalUrlResult in
return .result(.collectible(gift: gift))
})
case let .messageLink(slug):
return .single(.progress)
|> then(context.engine.peers.resolveMessageLink(slug: slug)