Bot apps implementation

This commit is contained in:
Ilya Laktyushin 2023-02-28 23:25:35 +04:00
parent 4c6dd1e16d
commit 53d1cb856a
45 changed files with 1553 additions and 394 deletions

View File

@ -8939,5 +8939,16 @@ Sorry for the inconvenience.";
"ChatList.ClearSavedMessagesConfirmation" = "Are you sure you want to delete all your saved messages?";
"Conversation.Translation.Settings" = "Settings";
"StickerPacksSettings.TrendingStickers" = "Trending Stickers";
"StickerPacksSettings.MyStickers" = "MY STICKERS";
"StickerPacksSettings.DynamicOrder" = "Dynamic Pack Order";
"StickerPacksSettings.DynamicOrderInfo" = "Recently used sticker packs will be displayed above the older ones.";
"StickerPacksSettings.Emoji" = "Emoji";
"StickerPacksSettings.DynamicOrderOff" = "Dynamic Order Off";
"StickerPacksSettings.DynamicOrderOffInfo" = "Sticker packs will no longer be automatically rearranged every time you use a sticker.";
"GroupPermission.NotAvailableInDiscussionGroups" = "This permission is not available in discussion groups.";
"GroupPermission.NotAvailableInGeoGroups" = "This permission is not available in location-based groups.";

View File

@ -443,6 +443,7 @@ public final class NavigateToChatControllerParams {
public let subject: ChatControllerSubject?
public let botStart: ChatControllerInitialBotStart?
public let attachBotStart: ChatControllerInitialAttachBotStart?
public let botAppStart: ChatControllerInitialBotAppStart?
public let updateTextInputState: ChatTextInputState?
public let activateInput: ChatControllerActivateInput?
public let keepStack: NavigateToChatKeepStack
@ -463,7 +464,7 @@ public final class NavigateToChatControllerParams {
public let setupController: (ChatController) -> Void
public let completion: (ChatController) -> Void
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) {
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) {
self.navigationController = navigationController
self.chatController = chatController
self.chatLocationContextHolder = chatLocationContextHolder
@ -472,6 +473,7 @@ public final class NavigateToChatControllerParams {
self.subject = subject
self.botStart = botStart
self.attachBotStart = attachBotStart
self.botAppStart = botAppStart
self.updateTextInputState = updateTextInputState
self.activateInput = activateInput
self.keepStack = keepStack

View File

@ -208,12 +208,25 @@ public struct ChatControllerInitialAttachBotStart {
}
}
public struct ChatControllerInitialBotAppStart {
public let botApp: BotApp
public let payload: String?
public let justInstalled: Bool
public init(botApp: BotApp, payload: String?, justInstalled: Bool) {
self.botApp = botApp
self.payload = payload
self.justInstalled = justInstalled
}
}
public enum ChatControllerInteractionNavigateToPeer {
case `default`
case chat(textInputState: ChatTextInputState?, subject: ChatControllerSubject?, peekData: ChatPeekTimeout?)
case info
case withBotStartPayload(ChatControllerInitialBotStart)
case withAttachBot(ChatControllerInitialAttachBotStart)
case withBotApp(ChatControllerInitialBotAppStart)
}
public struct ChatInterfaceForwardOptionsState: Codable, Equatable {

View File

@ -59,6 +59,13 @@ public enum ChatTranslationDisplayType {
case translated
}
public enum ChatOpenWebViewSource: Equatable {
case generic
case menu
case inline(bot: EnginePeer)
case webApp(botApp: BotApp)
}
public final class ChatPanelInterfaceInteraction {
public let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
public let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
@ -148,7 +155,7 @@ public final class ChatPanelInterfaceInteraction {
public let openSendAsPeer: (ASDisplayNode, ContextGesture?) -> Void
public let presentChatRequestAdminInfo: () -> Void
public let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void
public let openWebView: (String, String, Bool, Bool) -> Void
public let openWebView: (String, String, Bool, ChatOpenWebViewSource) -> Void
public let updateShowWebView: ((Bool) -> Bool) -> Void
public let insertText: (NSAttributedString) -> Void
public let backwardsDeleteText: () -> Void
@ -250,7 +257,7 @@ public final class ChatPanelInterfaceInteraction {
openSendAsPeer: @escaping (ASDisplayNode, ContextGesture?) -> Void,
presentChatRequestAdminInfo: @escaping () -> Void,
displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void,
openWebView: @escaping (String, String, Bool, Bool) -> Void,
openWebView: @escaping (String, String, Bool, ChatOpenWebViewSource) -> Void,
updateShowWebView: @escaping ((Bool) -> Bool) -> Void,
insertText: @escaping (NSAttributedString) -> Void,
backwardsDeleteText: @escaping () -> Void,

View File

@ -2471,13 +2471,19 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
var speedValue: String = strongSelf.presentationData.strings.PlaybackSpeed_Normal
var speedIconText: String = "1x"
var didSetSpeedValue = false
for (text, iconText, speed) in strongSelf.speedList(strings: strongSelf.presentationData.strings) {
if abs(speed - status.baseRate) < 0.01 {
speedValue = text
speedIconText = iconText
didSetSpeedValue = true
break
}
}
if !didSetSpeedValue && status.baseRate != 1.0 {
speedValue = String(format: "%.1fx", status.baseRate)
speedIconText = speedValue
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PlaybackSpeed_Title, textLayout: .secondLineWithValue(speedValue), icon: { theme in
return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor)
@ -2625,7 +2631,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
}
strongSelf.updatePlaybackRate(newValue)
if finished {
dismiss()
//dismiss()
}
}), true))

View File

@ -411,7 +411,7 @@ public struct LegacyAssetPickerEnqueueMessage {
public var isFile: Bool
}
public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any]) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
return Signal { subscriber in
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
var messages: [LegacyAssetPickerEnqueueMessage] = []

View File

@ -109,6 +109,7 @@ swift_library(
"//submodules/TelegramUI/Components/ChatTimerScreen",
"//submodules/AnimatedAvatarSetNode",
"//submodules/TelegramUI/Components/StorageUsageScreen",
"//submodules/FeaturedStickersScreen:FeaturedStickersScreen",
],
visibility = [
"//visibility:public",

View File

@ -11,10 +11,17 @@ import EmojiStatusComponent
import ComponentFlow
import AccountContext
public enum ItemListReactionArrowStyle {
case arrow
case none
}
public class ItemListReactionItem: ListViewItem, ItemListItem {
let context: AccountContext
let presentationData: ItemListPresentationData
let icon: UIImage?
let title: String
let arrowStyle: ItemListReactionArrowStyle
let reaction: MessageReaction.Reaction
let availableReactions: AvailableReactions?
public let sectionId: ItemListSectionId
@ -22,10 +29,12 @@ public class ItemListReactionItem: ListViewItem, ItemListItem {
let action: (() -> Void)?
public let tag: ItemListItemTag?
public init(context: AccountContext, presentationData: ItemListPresentationData, title: String, reaction: MessageReaction.Reaction, availableReactions: AvailableReactions?, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, tag: ItemListItemTag? = nil) {
public init(context: AccountContext, presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, arrowStyle: ItemListReactionArrowStyle = .none, reaction: MessageReaction.Reaction, availableReactions: AvailableReactions?, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context
self.presentationData = presentationData
self.icon = icon
self.title = title
self.arrowStyle = arrowStyle
self.reaction = reaction
self.availableReactions = availableReactions
self.sectionId = sectionId
@ -84,7 +93,9 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
let iconNode: ASImageNode
let titleNode: TextNode
let arrowNode: ASImageNode
let iconView: ComponentHostView<Empty>
private let activateArea: AccessibilityAreaNode
@ -119,11 +130,20 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
self.bottomStripeNode = ASDisplayNode()
self.bottomStripeNode.isLayerBacked = true
self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true
self.iconNode.displaysAsynchronously = false
self.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false
self.iconView = ComponentHostView<Empty>()
self.arrowNode = ASImageNode()
self.arrowNode.displayWithoutProcessing = true
self.arrowNode.displaysAsynchronously = false
self.arrowNode.isLayerBacked = true
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true
@ -133,6 +153,7 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
self.addSubnode(self.titleNode)
self.view.addSubview(self.iconView)
self.addSubnode(self.arrowNode)
self.addSubnode(self.activateArea)
}
@ -157,10 +178,28 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
let itemBackgroundColor: UIColor
let itemSeparatorColor: UIColor
let leftInset = 16.0 + params.leftInset
var updatedTheme: PresentationTheme?
var updateArrowImage: UIImage?
if currentItem?.presentationData.theme !== item.presentationData.theme {
updatedTheme = item.presentationData.theme
updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
}
var updateIcon = false
if currentItem?.icon != item.icon {
updateIcon = true
}
var leftInset = 16.0 + params.leftInset
if item.icon != nil {
leftInset += 43.0
}
var additionalTextRightInset: CGFloat = 0.0
additionalTextRightInset += 44.0
if item.arrowStyle == .arrow {
additionalTextRightInset += 24.0
}
let titleColor: UIColor = item.presentationData.theme.list.itemPrimaryTextColor
@ -197,11 +236,30 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.activateArea.accessibilityTraits = []
if currentItem?.presentationData.theme !== item.presentationData.theme {
if let icon = item.icon {
if strongSelf.iconNode.supernode == nil {
strongSelf.addSubnode(strongSelf.iconNode)
}
if updateIcon {
strongSelf.iconNode.image = icon
}
let iconY = floor((layout.contentSize.height - icon.size.height) / 2.0)
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size)
} else if strongSelf.iconNode.supernode != nil {
strongSelf.iconNode.image = nil
strongSelf.iconNode.removeFromSupernode()
}
if let _ = updatedTheme {
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
}
if let updateArrowImage = updateArrowImage {
strongSelf.arrowNode.image = updateArrowImage
}
let _ = titleApply()
@ -282,6 +340,17 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
animationContent = .customEmoji(fileId: fileId)
}
var rightInset: CGFloat = 0.0
if let arrowImage = strongSelf.arrowNode.image, item.arrowStyle == .arrow {
let arrowRightOffset: CGFloat = 7.0
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - arrowRightOffset - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
rightInset += arrowRightOffset + arrowImage.size.width
strongSelf.arrowNode.isHidden = false
} else {
strongSelf.arrowNode.isHidden = true
}
if let animationContent = animationContent {
let iconBoundingSize = CGSize(width: 28.0, height: 28.0)
let iconOffsetX: CGFloat = 0.0
@ -299,12 +368,9 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode {
containerSize: iconBoundingSize
)
strongSelf.iconView.isUserInteractionEnabled = false
strongSelf.iconView.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - iconSize.width + iconOffsetX, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
strongSelf.iconView.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - iconSize.width + iconOffsetX - rightInset, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
}
/*if let arrowImage = strongSelf.arrowNode.image {
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
}*/
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel))
}
})

View File

@ -17,6 +17,7 @@ import UndoUI
import ShareController
import WebPBinding
import ReactionImageComponent
import FeaturedStickersScreen
private final class InstalledStickerPacksControllerArguments {
let context: AccountContext
@ -34,10 +35,11 @@ private final class InstalledStickerPacksControllerArguments {
let toggleAnimatedStickers: (Bool) -> Void
let toggleSuggestAnimatedEmoji: (Bool) -> Void
let togglePackSelected: (ItemCollectionId) -> Void
let expandTrendingPacks: () -> Void
let toggleLargeEmoji: (Bool) -> Void
let toggleDynamicPackOrder: (Bool) -> Void
let addPack: (StickerPackCollectionInfo) -> Void
init(context: AccountContext, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openEmoji: @escaping () -> Void, openQuickReaction: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void, toggleSuggestAnimatedEmoji: @escaping (Bool) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void, expandTrendingPacks: @escaping () -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void) {
init(context: AccountContext, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openEmoji: @escaping () -> Void, openQuickReaction: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void, toggleSuggestAnimatedEmoji: @escaping (Bool) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, toggleDynamicPackOrder: @escaping (Bool) -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void) {
self.context = context
self.openStickerPack = openStickerPack
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
@ -52,14 +54,16 @@ private final class InstalledStickerPacksControllerArguments {
self.toggleAnimatedStickers = toggleAnimatedStickers
self.toggleSuggestAnimatedEmoji = toggleSuggestAnimatedEmoji
self.togglePackSelected = togglePackSelected
self.expandTrendingPacks = expandTrendingPacks
self.toggleLargeEmoji = toggleLargeEmoji
self.toggleDynamicPackOrder = toggleDynamicPackOrder
self.addPack = addPack
}
}
private enum InstalledStickerPacksSection: Int32 {
case service
case trending
case settings
case categories
case order
case stickers
}
@ -78,33 +82,34 @@ public enum InstalledStickerPacksEntryTag: ItemListItemTag {
private enum InstalledStickerPacksEntryId: Hashable {
case index(Int32)
case trendingPack(ItemCollectionId)
case pack(ItemCollectionId)
}
private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
case suggestOptions(PresentationTheme, String, String)
case largeEmoji(PresentationTheme, String, Bool)
case animatedStickers(PresentationTheme, String, Bool)
case animatedStickersInfo(PresentationTheme, String)
case trending(PresentationTheme, String, Int32)
case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?)
case masks(PresentationTheme, String)
case emoji(PresentationTheme, String)
case emoji(PresentationTheme, String, Int32)
case quickReaction(String, MessageReaction.Reaction, AvailableReactions)
case animatedStickers(PresentationTheme, String, Bool)
case animatedStickersInfo(PresentationTheme, String)
case packOrder(PresentationTheme, String, Bool)
case packOrderInfo(PresentationTheme, String)
case suggestAnimatedEmoji(String, Bool)
case trendingPacksTitle(PresentationTheme, String)
case trendingPack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, Bool)
case trendingExpand(PresentationTheme, String)
case packsTitle(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?)
case packsInfo(PresentationTheme, String)
var section: ItemListSectionId {
switch self {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji:
return InstalledStickerPacksSection.service.rawValue
case .trendingPacksTitle, .trendingPack, .trendingExpand:
return InstalledStickerPacksSection.trending.rawValue
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji:
return InstalledStickerPacksSection.settings.rawValue
case .trending, .masks, .emoji, .quickReaction, .archived:
return InstalledStickerPacksSection.categories.rawValue
case .packOrder, .packOrderInfo:
return InstalledStickerPacksSection.order.rawValue
case .packsTitle, .pack, .packsInfo:
return InstalledStickerPacksSection.stickers.rawValue
}
@ -114,34 +119,34 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
switch self {
case .suggestOptions:
return .index(0)
case .trending:
case .largeEmoji:
return .index(1)
case .archived:
return .index(2)
case .masks:
return .index(3)
case .emoji:
return .index(4)
case .quickReaction:
return .index(5)
case .animatedStickers:
return .index(6)
return .index(2)
case .animatedStickersInfo:
return .index(3)
case .trending:
return .index(4)
case .archived:
return .index(5)
case .emoji:
return .index(6)
case .masks:
return .index(7)
case .suggestAnimatedEmoji:
case .quickReaction:
return .index(8)
case .trendingPacksTitle:
case .suggestAnimatedEmoji:
return .index(9)
case let .trendingPack(_, _, _, info, _, _, _, _, _):
return .trendingPack(info.id)
case .trendingExpand:
case .packOrder:
return .index(10)
case .packsTitle:
case .packOrderInfo:
return .index(11)
case .packsTitle:
return .index(12)
case let .pack(_, _, _, info, _, _, _, _, _, _):
return .pack(info.id)
case .packsInfo:
return .index(12)
return .index(13)
}
}
@ -153,6 +158,12 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else {
return false
}
case let .largeEmoji(lhsTheme, lhsText, lhsValue):
if case let .largeEmoji(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .trending(lhsTheme, lhsText, lhsCount):
if case let .trending(rhsTheme, rhsText, rhsCount) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsCount == rhsCount {
return true
@ -165,8 +176,8 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else {
return false
}
case let .emoji(lhsTheme, lhsCount):
if case let .emoji(rhsTheme, rhsCount) = rhs, lhsTheme === rhsTheme, lhsCount == rhsCount {
case let .emoji(lhsTheme, lhsText, lhsCount):
if case let .emoji(rhsTheme, rhsText, rhsCount) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsCount == rhsCount {
return true
} else {
return false
@ -195,63 +206,30 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else {
return false
}
case let .packOrder(lhsTheme, lhsText, lhsValue):
if case let .packOrder(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .packOrderInfo(lhsTheme, lhsText):
if case let .packOrderInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .suggestAnimatedEmoji(lhsText, lhsValue):
if case let .suggestAnimatedEmoji(rhsText, rhsValue) = rhs, lhsValue == rhsValue, lhsText == rhsText {
return true
} else {
return false
}
case let .trendingPacksTitle(lhsTheme, lhsText):
if case let .trendingPacksTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .packsTitle(lhsTheme, lhsText):
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .trendingPack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsUnread, lhsInstalled):
if case let .trendingPack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsUnread, rhsInstalled) = rhs {
if lhsIndex != rhsIndex {
return false
}
if lhsTheme !== rhsTheme {
return false
}
if lhsStrings !== rhsStrings {
return false
}
if lhsInfo != rhsInfo {
return false
}
if lhsTopItem != rhsTopItem {
return false
}
if lhsCount != rhsCount {
return false
}
if lhsAnimatedStickers != rhsAnimatedStickers {
return false
}
if lhsUnread != rhsUnread {
return false
}
if lhsInstalled != rhsInstalled {
return false
}
return true
} else {
return false
}
case let .trendingExpand(lhsTheme, lhsText):
if case let .trendingExpand(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing, lhsSelected):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing, rhsSelected) = rhs {
if lhsIndex != rhsIndex {
@ -301,93 +279,91 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
switch lhs {
case .suggestOptions:
switch rhs {
case .suggestOptions:
case .suggestOptions:
return false
default:
return true
}
case .trending:
case .largeEmoji:
switch rhs {
case .suggestOptions, .trending:
return false
default:
return true
}
case .archived:
switch rhs {
case .suggestOptions, .trending, .archived:
return false
default:
return true
}
case .masks:
switch rhs {
case .suggestOptions, .trending, .archived, .masks:
return false
default:
return true
}
case .emoji:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji:
return false
default:
return true
}
case .quickReaction:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji, .quickReaction:
case .suggestOptions, .largeEmoji:
return false
default:
return true
}
case .animatedStickers:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji, .quickReaction, .animatedStickers:
case .suggestOptions, .largeEmoji, .animatedStickers:
return false
default:
return true
}
case .animatedStickersInfo:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji, .quickReaction, .animatedStickers, .animatedStickersInfo:
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo:
return false
default:
return true
}
case .trending:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending:
return false
default:
return true
}
case .archived:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived:
return false
default:
return true
}
case .masks:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks:
return false
default:
return true
}
case .emoji:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji:
return false
default:
return true
}
case .quickReaction:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction:
return false
default:
return true
}
case .packOrder:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder:
return false
default:
return true
}
case .packOrderInfo:
switch rhs {
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder, .packOrderInfo:
return false
default:
return true
}
case .suggestAnimatedEmoji:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji, .quickReaction, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji:
return false
default:
return true
}
case .trendingPacksTitle:
switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle:
return false
default:
return true
}
case let .trendingPack(lhsIndex, _, _, _, _, _, _, _, _):
switch rhs {
case let .trendingPack(rhsIndex, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
case .trendingExpand, .packsTitle, .pack, .packsInfo:
return true
default:
return false
}
case .trendingExpand:
switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle, .trendingPack, .trendingExpand:
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder, .packOrderInfo, .suggestAnimatedEmoji:
return false
default:
return true
}
case .packsTitle:
switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle, .trendingPack, .trendingExpand, .packsTitle:
case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .masks, .emoji, .quickReaction, .archived, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .packsTitle:
return false
default:
return true
@ -418,25 +394,9 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openSuggestOptions()
}, tag: InstalledStickerPacksEntryTag.suggestOptions)
case let .trending(theme, text, count):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: count == 0 ? "" : "\(count)", labelStyle: .badge(theme.list.itemAccentColor), sectionId: self.section, style: .blocks, action: {
arguments.openFeatured()
})
case let .masks(_, text):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openMasks()
})
case let .emoji(_, text):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openEmoji()
})
case let .quickReaction(title, reaction, availableReactions):
return ItemListReactionItem(context: arguments.context, presentationData: presentationData, title: title, reaction: reaction, availableReactions: availableReactions, sectionId: self.section, style: .blocks, action: {
arguments.openQuickReaction()
})
case let .archived(_, text, count, archived):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: {
arguments.openArchived(archived)
case let .largeEmoji(_, text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleAnimatedStickers(value)
})
case let .animatedStickers(_, text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
@ -444,25 +404,36 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
})
case let .animatedStickersInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .trending(theme, text, count):
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Trending")?.precomposed(), title: text, label: count == 0 ? "" : "\(count)", labelStyle: .badge(theme.list.itemAccentColor), sectionId: self.section, style: .blocks, action: {
arguments.openFeatured()
})
case let .masks(_, text):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openMasks()
})
case let .emoji(_, text, count):
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Emoji")?.precomposed(), title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: {
arguments.openEmoji()
})
case let .quickReaction(title, reaction, availableReactions):
return ItemListReactionItem(context: arguments.context, presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Reactions")?.precomposed(), title: title, arrowStyle: .arrow, reaction: reaction, availableReactions: availableReactions, sectionId: self.section, style: .blocks, action: {
arguments.openQuickReaction()
})
case let .archived(_, text, count, archived):
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Archived")?.precomposed(), title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: {
arguments.openArchived(archived)
})
case let .packOrder(_, text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleDynamicPackOrder(value)
})
case let .packOrderInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .suggestAnimatedEmoji(text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleSuggestAnimatedEmoji(value)
})
case let .trendingPacksTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .trendingPack(_, _, _, info, topItem, count, animatedStickers, unread, installed):
return ItemListStickerPackItem(presentationData: presentationData, context: arguments.context, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false, selectable: false), enabled: true, playAnimatedStickers: animatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { _, _ in
}, addPack: {
arguments.addPack(info)
}, removePack: {
}, toggleSelected: {
})
case let .trendingExpand(theme, text):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: text, sectionId: self.section, editing: false, action: {
arguments.expandTrendingPacks()
})
case let .packsTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing, selected):
@ -550,7 +521,7 @@ private func namespaceForMode(_ mode: InstalledStickerPacksControllerMode) -> It
private let maxTrendingPacksDisplayedLimit: Int32 = 3
private func installedStickerPacksControllerEntries(presentationData: PresentationData, state: InstalledStickerPacksControllerState, mode: InstalledStickerPacksControllerMode, view: CombinedView, temporaryPackOrder: [ItemCollectionId]?, featured: [FeaturedStickerPackItem], archived: [ArchivedStickerPackItem]?, stickerSettings: StickerSettings, quickReaction: MessageReaction.Reaction?, availableReactions: AvailableReactions?) -> [InstalledStickerPacksEntry] {
private func installedStickerPacksControllerEntries(presentationData: PresentationData, state: InstalledStickerPacksControllerState, mode: InstalledStickerPacksControllerMode, view: CombinedView, temporaryPackOrder: [ItemCollectionId]?, featured: [FeaturedStickerPackItem], archived: [ArchivedStickerPackItem]?, stickerSettings: StickerSettings, quickReaction: MessageReaction.Reaction?, availableReactions: AvailableReactions?, emojiCount: Int32) -> [InstalledStickerPacksEntry] {
var entries: [InstalledStickerPacksEntry] = []
var installedPacks = Set<ItemCollectionId>()
@ -579,52 +550,28 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
}
entries.append(.suggestOptions(presentationData.theme, presentationData.strings.Stickers_SuggestStickers, suggestString))
entries.append(.largeEmoji(presentationData.theme, presentationData.strings.Appearance_LargeEmoji, presentationData.largeEmoji))
entries.append(.animatedStickers(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickers, stickerSettings.loopAnimatedStickers))
entries.append(.animatedStickersInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickersInfo))
if !featured.isEmpty {
entries.append(.trending(presentationData.theme, presentationData.strings.StickerPacksSettings_TrendingStickers, Int32(featured.count)))
}
if let archived = archived, !archived.isEmpty {
entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived))
}
entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title))
entries.append(.emoji(presentationData.theme, presentationData.strings.StickersList_EmojiItem))
entries.append(.emoji(presentationData.theme, presentationData.strings.StickerPacksSettings_Emoji, emojiCount))
if let quickReaction = quickReaction, let availableReactions = availableReactions {
entries.append(.quickReaction(presentationData.strings.Settings_QuickReactionSetup_NavigationTitle, quickReaction, availableReactions))
}
entries.append(.animatedStickers(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickers, stickerSettings.loopAnimatedStickers))
entries.append(.animatedStickersInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickersInfo))
entries.append(.packOrder(presentationData.theme, presentationData.strings.StickerPacksSettings_DynamicOrder, stickerSettings.dynamicPackOrder))
entries.append(.packOrderInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_DynamicOrderInfo))
if featured.count > 0 {
entries.append(.trendingPacksTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_FeaturedPacks.uppercased()))
var index: Int32 = 0
var featuredPacks = featured
var effectiveExpanded = state.trendingPacksExpanded
if featuredPacks.count > maxTrendingPacksDisplayedLimit && !effectiveExpanded {
featuredPacks = Array(featuredPacks.prefix(Int(maxTrendingPacksDisplayedLimit)))
} else {
effectiveExpanded = true
}
for featuredPack in featuredPacks {
let countTitle: String
if featuredPack.info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks {
countTitle = presentationData.strings.StickerPack_EmojiCount(featuredPack.info.count)
} else if featuredPack.info.id.namespace == Namespaces.ItemCollection.CloudMaskPacks {
countTitle = presentationData.strings.StickerPack_MaskCount(featuredPack.info.count)
} else {
countTitle = presentationData.strings.StickerPack_StickerCount(featuredPack.info.count)
}
entries.append(.trendingPack(index, presentationData.theme, presentationData.strings, featuredPack.info, featuredPack.topItems.first, countTitle, stickerSettings.loopAnimatedStickers, featuredPack.unread, installedPacks.contains(featuredPack.info.id)))
index += 1
}
if !effectiveExpanded {
entries.append(.trendingExpand(presentationData.theme, presentationData.strings.Stickers_ShowMore))
}
}
entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_StickerPacksSection))
entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_MyStickers.uppercased()))
case .masks:
if let archived = archived, !archived.isEmpty {
entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedMasks, Int32(archived.count), archived))
@ -825,7 +772,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
context: context
))
}, openFeatured: {
pushControllerImpl?(featuredStickerPacksController(context: context))
pushControllerImpl?(FeaturedStickersScreen(context: context, highlightedPackId: nil))
}, openArchived: { archived in
let archivedMode: ArchivedStickerPacksControllerMode
switch mode {
@ -887,10 +834,14 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
return state
}
}
}, expandTrendingPacks: {
updateState { state in
return state.withUpdatedTrendingPacksExpanded(true)
}
}, toggleLargeEmoji: { value in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedLargeEmoji(value)
}).start()
}, toggleDynamicPackOrder: { value in
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedDynamicPackOrder(value)
}).start()
}, addPack: { info in
let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|> mapToSignal { result -> Signal<Void, NoError> in
@ -915,7 +866,8 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let featured = Promise<[FeaturedStickerPackItem]>()
let quickReaction: Signal<MessageReaction.Reaction?, NoError>
let emojiCount = Promise<Int32>()
switch mode {
case .general, .modal:
featured.set(context.account.viewTracker.featuredStickerPacks())
@ -938,14 +890,24 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
return reactionSettings.effectiveQuickReaction(hasPremium: hasPremium)
}
|> distinctUntilChanged
emojiCount.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])])
|> map { view in
if let info = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])] as? ItemCollectionInfosView, let entries = info.entriesByNamespace[Namespaces.ItemCollection.CloudEmojiPacks] {
return Int32(entries.count)
} else {
return 0
}
})
case .masks:
featured.set(.single([]))
archivedPromise.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks(namespace: .masks) |> map(Optional.init)))
quickReaction = .single(nil)
emojiCount.set(.single(0))
case .emoji:
featured.set(.single([]))
archivedPromise.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks(namespace: .emoji) |> map(Optional.init)))
quickReaction = .single(nil)
emojiCount.set(.single(0))
}
var previousPackCount: Int?
@ -956,10 +918,11 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
combineLatest(queue: .mainQueue(), featured.get(), archivedPromise.get()),
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]),
quickReaction,
context.engine.stickers.availableReactions()
context.engine.stickers.availableReactions(),
emojiCount.get()
)
|> deliverOnMainQueue
|> map { presentationData, state, view, temporaryPackOrder, featuredAndArchived, sharedData, quickReaction, availableReactions -> (ItemListControllerState, (ItemListNodeState, Any)) in
|> map { presentationData, state, view, temporaryPackOrder, featuredAndArchived, sharedData, quickReaction, availableReactions, emojiCount -> (ItemListControllerState, (ItemListNodeState, Any)) in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
stickerSettings = value
@ -1113,7 +1076,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: installedStickerPacksControllerEntries(presentationData: presentationData, state: state, mode: mode, view: view, temporaryPackOrder: temporaryPackOrder, featured: featuredAndArchived.0, archived: featuredAndArchived.1, stickerSettings: stickerSettings, quickReaction: quickReaction, availableReactions: availableReactions), style: .blocks, ensureVisibleItemTag: focusOnItemTag, toolbarItem: toolbarItem, animateChanges: previous != nil && packCount != nil && (previous! != 0 && previous! >= packCount! - 10))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: installedStickerPacksControllerEntries(presentationData: presentationData, state: state, mode: mode, view: view, temporaryPackOrder: temporaryPackOrder, featured: featuredAndArchived.0, archived: featuredAndArchived.1, stickerSettings: stickerSettings, quickReaction: quickReaction, availableReactions: availableReactions, emojiCount: emojiCount), style: .blocks, ensureVisibleItemTag: focusOnItemTag, toolbarItem: toolbarItem, animateChanges: previous != nil && packCount != nil && (previous! != 0 && previous! >= packCount! - 10))
return (controllerState, (listState, arguments))
}
|> afterDisposed {
@ -1124,26 +1087,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
if case .modal = mode {
controller.navigationPresentation = .modal
}
var alreadyReadIds = Set<ItemCollectionId>()
controller.visibleEntriesUpdated = { entries in
var unreadIds: [ItemCollectionId] = []
for entry in entries {
if let entry = entry as? InstalledStickerPacksEntry {
if case let .trendingPack(_, _, _, info, _, _, _, unread, _) = entry {
if unread && !alreadyReadIds.contains(info.id) {
unreadIds.append(info.id)
}
}
}
}
if !unreadIds.isEmpty {
alreadyReadIds.formUnion(Set(unreadIds))
let _ = context.engine.stickers.markFeaturedStickerPacksAsSeenInteractively(ids: unreadIds).start()
}
}
controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [InstalledStickerPacksEntry]) -> Signal<Bool, NoError> in
let fromEntry = entries[fromIndex]
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _, _) = fromEntry else {
@ -1234,6 +1178,23 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|> deliverOnMainQueue).start(completed: {
temporaryPackOrder.set(.single(nil))
})
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
|> take(1)
|> deliverOnMainQueue).start(next: { sharedData in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
stickerSettings = value
}
if stickerSettings.dynamicPackOrder {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: presentationData.strings.StickerPacksSettings_DynamicOrderOff, text: presentationData.strings.StickerPacksSettings_DynamicOrderOffInfo, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
return false }), nil)
arguments.toggleDynamicPackOrder(false)
}
})
})
presentControllerImpl = { [weak controller] c, p in

View File

@ -513,7 +513,7 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
nextOffset = newNextOffset
}
}
let merged = ChatContextResultCollection(botId: collection.botId, peerId: collection.peerId, query: collection.query, geoPoint: collection.geoPoint, queryId: nextResults?.queryId ?? collection.queryId, nextOffset: nextOffset ?? "", presentation: collection.presentation, switchPeer: collection.switchPeer, results: results, cacheTimeout: collection.cacheTimeout)
let merged = ChatContextResultCollection(botId: collection.botId, peerId: collection.peerId, query: collection.query, geoPoint: collection.geoPoint, queryId: nextResults?.queryId ?? collection.queryId, nextOffset: nextOffset ?? "", presentation: collection.presentation, switchPeer: collection.switchPeer, webView: collection.webView, results: results, cacheTimeout: collection.cacheTimeout)
return (merged, nextOffset)
}
|> mapToSignal { newCollection, nextOffset -> Signal<([ThemeGridSearchEntry], Bool)?, NoError> in

View File

@ -753,7 +753,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) }
dict[-2010155333] = { return Api.SimpleWebViewResult.parse_simpleWebViewResultUrl($0) }
dict[981691896] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[-64636888] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
dict[-1901828938] = { return Api.StatsGraph.parse_statsGraph($0) }

View File

@ -434,13 +434,13 @@ public extension Api {
}
public extension Api {
indirect enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, message: String, entities: [Api.MessageEntity]?)
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, message: String, entities: [Api.MessageEntity]?, sponsorInfo: String?, additionalInfo: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities):
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities, let sponsorInfo, let additionalInfo):
if boxed {
buffer.appendInt32(981691896)
buffer.appendInt32(-64636888)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeBytes(randomId, buffer: buffer, boxed: false)
@ -455,14 +455,16 @@ public extension Api {
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("message", message as Any), ("entities", entities as Any)])
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("message", message as Any), ("entities", entities as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
}
}
@ -491,6 +493,10 @@ public extension Api {
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _10: String?
if Int(_1!) & Int(1 << 7) != 0 {_10 = parseString(reader) }
var _11: String?
if Int(_1!) & Int(1 << 8) != 0 {_11 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
@ -500,8 +506,10 @@ public extension Api {
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
let _c8 = _8 != nil
let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, message: _8!, entities: _9)
let _c10 = (Int(_1!) & Int(1 << 7) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, message: _8!, entities: _9, sponsorInfo: _10, additionalInfo: _11)
}
else {
return nil

View File

@ -565,8 +565,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
dismissImpl?()
})
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
dismissImpl = { [weak contextController] in
contextController?.dismiss()
dismissImpl = {
//contextController?.dismiss()
}
self.presentInGlobalOverlay?(contextController)
}

View File

@ -352,6 +352,15 @@ public struct ChatContextResultSwitchPeer: Equatable, Codable {
}
}
public struct ChatContextResultWebView: Equatable, Codable {
public let text: String
public let url: String
public static func ==(lhs: ChatContextResultWebView, rhs: ChatContextResultWebView) -> Bool {
return lhs.text == rhs.text && lhs.url == rhs.url
}
}
public final class ChatContextResultCollection: Equatable, Codable {
public struct GeoPoint: Equatable, Codable {
public let latitude: Double
@ -371,10 +380,11 @@ public final class ChatContextResultCollection: Equatable, Codable {
public let nextOffset: String?
public let presentation: ChatContextResultCollectionPresentation
public let switchPeer: ChatContextResultSwitchPeer?
public let webView: ChatContextResultWebView?
public let results: [ChatContextResult]
public let cacheTimeout: Int32
public init(botId: PeerId, peerId: PeerId, query: String, geoPoint: ChatContextResultCollection.GeoPoint?, queryId: Int64, nextOffset: String?, presentation: ChatContextResultCollectionPresentation, switchPeer: ChatContextResultSwitchPeer?, results: [ChatContextResult], cacheTimeout: Int32) {
public init(botId: PeerId, peerId: PeerId, query: String, geoPoint: ChatContextResultCollection.GeoPoint?, queryId: Int64, nextOffset: String?, presentation: ChatContextResultCollectionPresentation, switchPeer: ChatContextResultSwitchPeer?, webView: ChatContextResultWebView?, results: [ChatContextResult], cacheTimeout: Int32) {
self.botId = botId
self.peerId = peerId
self.query = query
@ -383,6 +393,7 @@ public final class ChatContextResultCollection: Equatable, Codable {
self.nextOffset = nextOffset
self.presentation = presentation
self.switchPeer = switchPeer
self.webView = webView
self.results = results
self.cacheTimeout = cacheTimeout
}
@ -412,6 +423,9 @@ public final class ChatContextResultCollection: Equatable, Codable {
if lhs.switchPeer != rhs.switchPeer {
return false
}
if lhs.webView != rhs.webView {
return false
}
if lhs.results != rhs.results {
return false
}
@ -511,14 +525,27 @@ extension ChatContextResultSwitchPeer {
}
}
extension ChatContextResultWebView {
init(apiSwitchWebView: Api.InlineBotWebView) {
switch apiSwitchWebView {
case let .inlineBotWebView(text, url):
self.init(text: text, url: url)
}
}
}
extension ChatContextResultCollection {
convenience init(apiResults: Api.messages.BotResults, botId: PeerId, peerId: PeerId, query: String, geoPoint: (Double, Double)?) {
switch apiResults {
case let .botResults(flags, queryId, nextOffset, switchPm, _, results, cacheTime, _):
case let .botResults(flags, queryId, nextOffset, switchPm, switchWebView, results, cacheTime, _):
var switchPeer: ChatContextResultSwitchPeer?
if let switchPm = switchPm {
switchPeer = ChatContextResultSwitchPeer(apiSwitchPeer: switchPm)
}
var webView: ChatContextResultWebView?
if let switchWebView = switchWebView {
webView = ChatContextResultWebView(apiSwitchWebView: switchWebView)
}
let parsedResults = results.map({ ChatContextResult(apiResult: $0, queryId: queryId) })
/*.filter({ result in
switch result {
@ -531,7 +558,7 @@ extension ChatContextResultCollection {
let mappedGeoPoint = geoPoint.flatMap { geoPoint -> ChatContextResultCollection.GeoPoint in
return ChatContextResultCollection.GeoPoint(latitude: geoPoint.0, longitude: geoPoint.1)
}
self.init(botId: botId, peerId: peerId, query: query, geoPoint: mappedGeoPoint, queryId: queryId, nextOffset: nextOffset, presentation: (flags & (1 << 0) != 0) ? .media : .list, switchPeer: switchPeer, results: parsedResults, cacheTimeout: cacheTime)
self.init(botId: botId, peerId: peerId, query: query, geoPoint: mappedGeoPoint, queryId: queryId, nextOffset: nextOffset, presentation: (flags & (1 << 0) != 0) ? .media : .list, switchPeer: switchPeer, webView: webView, results: parsedResults, cacheTimeout: cacheTime)
}
}
}
@ -560,7 +587,7 @@ public func requestContextResults(engine: TelegramEngine, botId: EnginePeer.Id,
updated = true
}
}
collection = ChatContextResultCollection(botId: existingResults.botId, peerId: existingResults.peerId, query: existingResults.query, geoPoint: existingResults.geoPoint, queryId: results.queryId, nextOffset: results.nextOffset, presentation: existingResults.presentation, switchPeer: existingResults.switchPeer, results: newResults, cacheTimeout: existingResults.cacheTimeout)
collection = ChatContextResultCollection(botId: existingResults.botId, peerId: existingResults.peerId, query: existingResults.query, geoPoint: existingResults.geoPoint, queryId: results.queryId, nextOffset: results.nextOffset, presentation: existingResults.presentation, switchPeer: existingResults.switchPeer, webView: existingResults.webView, results: newResults, cacheTimeout: existingResults.cacheTimeout)
} else {
collection = results
updated = true

View File

@ -450,7 +450,9 @@ private class AdMessagesHistoryContextImpl {
for message in messages {
switch message {
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, message, entities):
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, message, entities, sponsorInfo, additionalInfo):
let _ = sponsorInfo
let _ = additionalInfo
var parsedEntities: [MessageTextEntity] = []
if let entities = entities {
parsedEntities = messageTextEntitiesFromApiEntities(entities)

View File

@ -467,8 +467,8 @@ func _internal_attachMenuBots(postbox: Postbox) -> Signal<[AttachMenuBot], NoErr
public enum GetAttachMenuBotError {
case generic
}
public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerId, cached: Bool) -> Signal<AttachMenuBot, GetAttachMenuBotError> {
func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerId, cached: Bool) -> Signal<AttachMenuBot, GetAttachMenuBotError> {
return postbox.transaction { transaction -> Signal<AttachMenuBot, GetAttachMenuBotError> in
if cached, let cachedBots = cachedAttachMenuBots(transaction: transaction)?.bots {
if let bot = cachedBots.first(where: { $0.peerId == botId }), let peer = transaction.getPeer(bot.peerId) {
@ -549,3 +549,183 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
|> castError(GetAttachMenuBotError.self)
|> switchToLatest
}
public enum BotAppReference {
case id(id: Int64, accessHash: Int64)
case shortName(peerId: PeerId, shortName: String)
}
public final class BotApp: Equatable, Codable {
private enum CodingKeys: String, CodingKey {
case id
case accessHash
case shortName
case title
case description
case photo
case document
case hash
case flags
}
public struct Flags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let notActivated = Flags(rawValue: 1 << 0)
public static let requiresWriteAccess = Flags(rawValue: 1 << 1)
}
public let id: Int64
public let accessHash: Int64
public let shortName: String
public let title: String
public let description: String
public let photo: TelegramMediaImage?
public let document: TelegramMediaFile?
public let hash: Int64
public let flags: Flags
public init(
id: Int64,
accessHash: Int64,
shortName: String,
title: String,
description: String,
photo: TelegramMediaImage?,
document: TelegramMediaFile?,
hash: Int64,
flags: Flags
) {
self.id = id
self.accessHash = accessHash
self.shortName = shortName
self.title = title
self.description = description
self.photo = photo
self.document = document
self.hash = hash
self.flags = flags
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int64.self, forKey: .id)
self.accessHash = try container.decode(Int64.self, forKey: .accessHash)
self.shortName = try container.decode(String.self, forKey: .shortName)
self.title = try container.decode(String.self, forKey: .title)
self.description = try container.decode(String.self, forKey: .description)
if let data = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .photo) {
self.photo = TelegramMediaImage(decoder: PostboxDecoder(buffer: MemoryBuffer(data: data.data)))
} else {
self.photo = nil
}
if let data = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .document) {
self.document = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: data.data)))
} else {
self.document = nil
}
self.hash = try container.decode(Int64.self, forKey: .hash)
self.flags = Flags(rawValue: try container.decode(Int32.self, forKey: .flags))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.accessHash, forKey: .accessHash)
try container.encode(self.shortName, forKey: .shortName)
try container.encode(self.title, forKey: .title)
try container.encode(self.description, forKey: .description)
try container.encodeIfPresent(self.photo, forKey: .photo)
try container.encodeIfPresent(self.document, forKey: .document)
try container.encode(self.hash, forKey: .hash)
try container.encode(self.flags.rawValue, forKey: .flags)
}
public static func ==(lhs: BotApp, rhs: BotApp) -> Bool {
if lhs.id != rhs.id {
return false
}
if lhs.accessHash != rhs.accessHash {
return false
}
if lhs.shortName != rhs.shortName {
return false
}
if lhs.title != rhs.title {
return false
}
if lhs.description != rhs.description {
return false
}
if lhs.photo != rhs.photo {
return false
}
if lhs.document != rhs.document {
return false
}
if lhs.hash != rhs.hash {
return false
}
if lhs.flags != rhs.flags {
return false
}
return true
}
}
public enum GetBotAppError {
case generic
}
func _internal_getBotApp(account: Account, reference: BotAppReference) -> Signal<BotApp, GetBotAppError> {
return account.postbox.transaction { transaction -> Signal<BotApp, GetBotAppError> in
let app: Api.InputBotApp
switch reference {
case let .id(id, accessHash):
app = .inputBotAppID(id: id, accessHash: accessHash)
case let .shortName(peerId, shortName):
guard let bot = transaction.getPeer(peerId), let inputBot = apiInputUser(bot) else {
return .fail(.generic)
}
app = .inputBotAppShortName(botId: inputBot, shortName: shortName)
}
return account.network.request(Api.functions.messages.getBotApp(app: app, hash: 0))
|> mapError { _ -> GetBotAppError in
return .generic
}
|> mapToSignal { result -> Signal<BotApp, GetBotAppError> in
switch result {
case let .botApp(_, app):
switch app {
case let .botApp(flags, id, accessHash, shortName, title, description, photo, document, hash):
var appFlags = BotApp.Flags()
if (flags & (1 << 0)) != 0 {
appFlags.insert(.notActivated)
}
if (flags & (1 << 1)) != 0 {
appFlags.insert(.requiresWriteAccess)
}
return .single(BotApp(id: id, accessHash: accessHash, shortName: shortName, title: title, description: description, photo: telegramMediaImageFromApiPhoto(photo), document: document.flatMap(telegramMediaFileFromApiDocument), hash: hash, flags: appFlags))
case .botAppNotModified:
return .complete()
}
}
}
}
|> castError(GetBotAppError.self)
|> switchToLatest
}

View File

@ -14,7 +14,7 @@ public enum RequestSimpleWebViewError {
case generic
}
func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String, inline: Bool, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
serializedThemeParams = .dataJSON(data: dataString)
@ -28,6 +28,9 @@ func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: P
if let _ = serializedThemeParams {
flags |= (1 << 0)
}
if inline {
flags |= (1 << 1)
}
return network.request(Api.functions.messages.requestSimpleWebView(flags: flags, bot: inputUser, url: url, themeParams: serializedThemeParams, platform: botWebViewPlatform))
|> mapError { _ -> RequestSimpleWebViewError in
return .generic
@ -131,9 +134,7 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager:
if threadId != nil {
flags |= (1 << 9)
}
// if _ {
// flags |= (1 << 13)
// }
return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform, replyToMsgId: replyToMsgId, topMsgId: threadId.flatMap(Int32.init(clamping:)), sendAs: nil))
|> mapError { _ -> RequestWebViewError in
return .generic
@ -172,3 +173,52 @@ func _internal_sendWebViewData(postbox: Postbox, network: Network, stateManager:
|> castError(SendWebViewDataError.self)
|> switchToLatest
}
public enum RequestAppWebViewError {
case generic
}
func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, allowWrite: Bool) -> Signal<String, RequestAppWebViewError> {
var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
serializedThemeParams = .dataJSON(data: dataString)
}
return postbox.transaction { transaction -> Signal<String, RequestAppWebViewError> in
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
return .fail(.generic)
}
let app: Api.InputBotApp
switch appReference {
case let .id(id, accessHash):
app = .inputBotAppID(id: id, accessHash: accessHash)
case let .shortName(peerId, shortName):
guard let bot = transaction.getPeer(peerId), let inputBot = apiInputUser(bot) else {
return .fail(.generic)
}
app = .inputBotAppShortName(botId: inputBot, shortName: shortName)
}
var flags: Int32 = 0
if let _ = serializedThemeParams {
flags |= (1 << 2)
}
if let _ = payload {
flags |= (1 << 1)
}
return network.request(Api.functions.messages.requestAppWebView(flags: flags, peer: inputPeer, app: app, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform))
|> mapError { _ -> RequestAppWebViewError in
return .generic
}
|> mapToSignal { result -> Signal<String, RequestAppWebViewError> in
switch result {
case let .appWebViewResultUrl(url):
return .single(url)
}
}
}
|> castError(RequestAppWebViewError.self)
|> switchToLatest
}

View File

@ -99,6 +99,7 @@ func _internal_requestChatContextResults(account: Account, botId: PeerId, peerId
nextOffset: nil,
presentation: cachedResult.presentation,
switchPeer: cachedResult.switchPeer,
webView: cachedResult.webView,
results: cachedResult.results,
cacheTimeout: 0
)

View File

@ -394,11 +394,14 @@ public extension TelegramEngine {
return _internal_requestWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, botId: botId, url: url, payload: payload, themeParams: themeParams, fromMenu: fromMenu, replyToMessageId: replyToMessageId, threadId: threadId)
}
public func requestSimpleWebView(botId: PeerId, url: String, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
return _internal_requestSimpleWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, url: url, themeParams: themeParams)
public func requestSimpleWebView(botId: PeerId, url: String, inline: Bool, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
return _internal_requestSimpleWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, url: url, inline: inline, themeParams: themeParams)
}
public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, allowWrite: Bool) -> Signal<String, RequestAppWebViewError> {
return _internal_requestAppWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, appReference: appReference, payload: payload, themeParams: themeParams, allowWrite: allowWrite)
}
public func sendWebViewData(botId: PeerId, buttonText: String, data: String) -> Signal<Never, SendWebViewDataError> {
return _internal_sendWebViewData(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, botId: botId, buttonText: buttonText, data: data)
}
@ -419,6 +422,10 @@ public extension TelegramEngine {
return _internal_attachMenuBots(postbox: self.account.postbox)
}
public func getBotApp(botId: PeerId, shortName: String, cached: Bool = false) -> Signal<BotApp, GetBotAppError> {
return _internal_getBotApp(account: self.account, reference: .shortName(peerId: botId, shortName: shortName))
}
public func ensureMessagesAreLocallyAvailable(messages: [EngineMessage]) {
let _ = self.account.postbox.transaction({ transaction in
for message in messages {

View File

@ -165,7 +165,7 @@ public final class ChatControllerInteraction {
public let commitEmojiInteraction: (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void
public let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void
public let openJoinLink: (String) -> Void
public let openWebView: (String, String, Bool, Bool) -> Void
public let openWebView: (String, String, Bool, ChatOpenWebViewSource) -> Void
public let activateAdAction: (EngineMessage.Id) -> Void
public let openRequestedPeerSelection: (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void
@ -277,7 +277,7 @@ public final class ChatControllerInteraction {
commitEmojiInteraction: @escaping (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void,
openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void,
openJoinLink: @escaping (String) -> Void,
openWebView: @escaping (String, String, Bool, Bool) -> Void,
openWebView: @escaping (String, String, Bool, ChatOpenWebViewSource) -> Void,
activateAdAction: @escaping (EngineMessage.Id) -> Void,
openRequestedPeerSelection: @escaping (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void,
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,

View File

@ -1243,7 +1243,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return
}
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
if let id = groupId.base as? ItemCollectionId {
if let id = groupId.base as? ItemCollectionId, context.sharedContext.currentStickerSettings.with({ $0 }).dynamicPackOrder {
bubbleUpEmojiOrStickersets.append(id)
}
let _ = interfaceInteraction.sendSticker(.standalone(media: file), false, view, rect, layer, bubbleUpEmojiOrStickersets)
@ -2507,12 +2507,15 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
return nil
}
let context = strongSelf.context
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
if let id = groupId.base as? ItemCollectionId {
bubbleUpEmojiOrStickersets.append(id)
if file.isCustomEmoji || context.sharedContext.currentStickerSettings.with({ $0 }).dynamicPackOrder {
bubbleUpEmojiOrStickersets.append(id)
}
}
let context = strongSelf.context
let accountPeerId = context.account.peerId
let chatPeerId = strongSelf.chatPeerId

View File

@ -199,7 +199,7 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
let range = self.maxValue - self.minValue
let location = gestureRecognizer.location(in: gestureRecognizer.view)
self.value = max(self.minValue, min(self.maxValue, location.x / range))
self.value = max(self.minValue, min(self.maxValue, self.minValue + location.x / self.bounds.width * range))
self.valueChanged(self.value, true)
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "archived.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,224 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.203922 0.780392 0.349020 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm
1.000000 1.000000 1.000000 scn
0.000000 11.600000 m
0.000000 13.840210 0.000000 14.960315 0.435974 15.815962 c
0.819467 16.568611 1.431390 17.180532 2.184038 17.564026 c
3.039685 18.000000 4.159790 18.000000 6.400000 18.000000 c
12.461538 18.000000 l
13.893039 18.000000 14.608788 18.000000 15.190012 17.818884 c
16.445040 17.427801 17.427801 16.445040 17.818884 15.190012 c
18.000000 14.608788 18.000000 13.893039 18.000000 12.461538 c
18.000000 9.240663 18.000000 7.630225 17.592487 6.322473 c
16.712553 3.498662 14.501338 1.287447 11.677527 0.407513 c
10.369775 0.000000 8.759336 0.000000 5.538461 0.000000 c
4.106961 0.000000 3.391211 0.000000 2.809988 0.181116 c
1.554961 0.572199 0.572199 1.554960 0.181116 2.809988 c
0.000000 3.391212 0.000000 4.106961 0.000000 5.538462 c
0.000000 11.600000 l
h
6.250000 9.625000 m
6.940356 9.625000 7.500000 10.324555 7.500000 11.187500 c
7.500000 12.050446 6.940356 12.750000 6.250000 12.750000 c
5.559644 12.750000 5.000000 12.050446 5.000000 11.187500 c
5.000000 10.324555 5.559644 9.625000 6.250000 9.625000 c
h
11.750000 9.625000 m
12.440355 9.625000 13.000000 10.324555 13.000000 11.187500 c
13.000000 12.050446 12.440355 12.750000 11.750000 12.750000 c
11.059645 12.750000 10.500000 12.050446 10.500000 11.187500 c
10.500000 10.324555 11.059645 9.625000 11.750000 9.625000 c
h
5.531705 6.899393 m
5.311266 7.192869 4.894684 7.252263 4.601000 7.032000 c
4.307185 6.811639 4.247638 6.394815 4.468000 6.101000 c
5.000000 6.500000 l
4.468000 6.101000 4.468162 6.100784 4.468329 6.100561 c
4.468694 6.100076 l
4.469531 6.098966 l
4.471629 6.096197 l
4.477521 6.088497 l
4.496066 6.064772 l
4.511416 6.045382 4.532754 6.018972 4.559963 5.986553 c
4.614337 5.921766 4.692429 5.832656 4.793315 5.727383 c
4.994541 5.517408 5.289614 5.239828 5.671366 4.962191 c
6.432307 4.408777 7.566772 3.834998 8.999999 3.834996 c
9.826370 3.834996 10.586204 3.974838 11.266029 4.271576 c
11.602630 4.418500 11.756393 4.810473 11.609470 5.147075 c
11.462546 5.483676 11.070572 5.637440 10.733971 5.490517 c
10.249819 5.279187 9.676319 5.164996 9.000001 5.164996 c
7.933228 5.164998 7.067693 5.591221 6.453634 6.037808 c
6.147886 6.260172 5.911709 6.482592 5.753560 6.647617 c
5.674759 6.729844 5.616131 6.796984 5.578709 6.841572 c
5.560019 6.863840 5.546690 6.880400 5.538846 6.890306 c
5.531443 6.899742 l
5.531705 6.899393 l
h
5.531705 6.899393 m
5.531803 6.899262 5.531902 6.899131 5.532000 6.899000 c
5.000000 6.500000 l
5.532000 6.899000 5.531850 6.899200 5.531705 6.899393 c
h
f*
n
Q
q
q
1.000000 0.000000 -0.000000 1.000000 16.000000 2.340088 cm
0.203922 0.780392 0.349020 scn
7.670000 7.159912 m
7.670000 5.409170 6.250742 3.989912 4.500000 3.989912 c
4.500000 1.329912 l
7.719820 1.329912 10.330000 3.940092 10.330000 7.159912 c
7.670000 7.159912 l
h
4.500000 3.989912 m
2.749257 3.989912 1.330000 5.409170 1.330000 7.159912 c
-1.330000 7.159912 l
-1.330000 3.940092 1.280180 1.329912 4.500000 1.329912 c
4.500000 3.989912 l
h
1.330000 7.159912 m
1.330000 8.910655 2.749257 10.329912 4.500000 10.329912 c
4.500000 12.989912 l
1.280180 12.989912 -1.330000 10.379732 -1.330000 7.159912 c
1.330000 7.159912 l
h
4.500000 10.329912 m
6.250742 10.329912 7.670000 8.910655 7.670000 7.159912 c
10.330000 7.159912 l
10.330000 10.379732 7.719820 12.989912 4.500000 12.989912 c
4.500000 10.329912 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 16.000000 2.340088 cm
1.000000 1.000000 1.000000 scn
9.000000 7.159912 m
9.000000 4.674631 6.985281 2.659912 4.500000 2.659912 c
2.014719 2.659912 0.000000 4.674631 0.000000 7.159912 c
0.000000 9.645193 2.014719 11.659912 4.500000 11.659912 c
6.985281 11.659912 9.000000 9.645193 9.000000 7.159912 c
h
f
n
Q
Q
q
1.000000 0.000000 -0.000000 1.000000 20.500000 8.169922 cm
0.203922 0.780392 0.349020 scn
0.665000 3.830078 m
0.665000 4.197348 0.367269 4.495078 0.000000 4.495078 c
-0.367269 4.495078 -0.665000 4.197348 -0.665000 3.830078 c
0.665000 3.830078 l
h
0.000000 1.330078 m
-0.665000 1.330078 l
-0.665000 0.962809 -0.367269 0.665078 0.000000 0.665078 c
0.000000 1.330078 l
h
2.000000 0.665078 m
2.367269 0.665078 2.665000 0.962809 2.665000 1.330078 c
2.665000 1.697347 2.367269 1.995078 2.000000 1.995078 c
2.000000 0.665078 l
h
-0.665000 3.830078 m
-0.665000 1.330078 l
0.665000 1.330078 l
0.665000 3.830078 l
-0.665000 3.830078 l
h
0.000000 0.665078 m
2.000000 0.665078 l
2.000000 1.995078 l
0.000000 1.995078 l
0.000000 0.665078 l
h
f
n
Q
endstream
endobj
3 0 obj
5423
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000005513 00000 n
0000005536 00000 n
0000005709 00000 n
0000005783 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
5842
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "emoji (2).pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,144 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.196078 0.678431 0.901961 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm
1.000000 1.000000 1.000000 scn
0.000000 10.000000 m
0.000000 5.332758 3.197396 1.412308 7.521235 0.309603 c
7.192342 0.790955 7.000000 1.373016 7.000000 2.000000 c
7.000000 3.113091 l
5.212887 3.938812 4.297566 5.421120 3.859255 6.432051 c
3.686586 6.830300 4.118104 7.180214 4.520657 7.017826 c
5.607882 6.579245 7.514322 6.000000 10.000001 6.000000 c
12.485676 6.000000 14.392107 6.579244 15.479328 7.017824 c
15.881880 7.180212 16.313400 6.830300 16.140732 6.432050 c
15.702424 5.421119 14.787108 3.938814 13.000000 3.113092 c
13.000000 2.000000 l
13.000000 1.373016 12.807658 0.790955 12.478765 0.309603 c
16.802603 1.412308 20.000000 5.332758 20.000000 10.000000 c
20.000000 15.522848 15.522847 20.000000 10.000000 20.000000 c
4.477152 20.000000 0.000000 15.522848 0.000000 10.000000 c
h
14.000001 9.250000 m
15.242641 9.250000 16.250000 10.257360 16.250000 11.500000 c
16.250000 12.742640 15.242641 13.750000 14.000001 13.750000 c
12.757360 13.750000 11.750001 12.742640 11.750001 11.500000 c
11.750001 10.257360 12.757360 9.250000 14.000001 9.250000 c
h
14.000001 8.250000 m
15.794927 8.250000 17.250000 9.705074 17.250000 11.500000 c
17.250000 13.294926 15.794927 14.750000 14.000001 14.750000 c
12.205075 14.750000 10.750001 13.294926 10.750001 11.500000 c
10.750001 9.705074 12.205075 8.250000 14.000001 8.250000 c
h
15.000001 11.500000 m
15.000001 10.947716 14.552285 10.500000 14.000001 10.500000 c
13.447717 10.500000 13.000001 10.947716 13.000001 11.500000 c
13.000001 12.052284 13.447717 12.500000 14.000001 12.500000 c
14.552285 12.500000 15.000001 12.052284 15.000001 11.500000 c
h
8.982612 10.447145 m
8.552257 10.964127 7.794065 11.500000 6.500000 11.500000 c
5.205936 11.500000 4.447744 10.964127 4.017390 10.447145 c
3.744092 10.118834 4.103591 9.767232 4.509996 9.898819 c
5.063556 10.078053 5.781778 10.250000 6.500000 10.250000 c
7.218223 10.250000 7.936446 10.078053 8.490005 9.898819 c
8.896410 9.767232 9.255910 10.118834 8.982612 10.447145 c
h
9.000000 5.000000 m
8.447716 5.000000 8.000000 4.552284 8.000000 4.000000 c
8.000000 2.000000 l
8.000000 0.895432 8.895431 0.000000 10.000000 0.000000 c
11.104569 0.000000 12.000000 0.895432 12.000000 2.000000 c
12.000000 4.000000 l
12.000000 4.552284 11.552284 5.000000 11.000000 5.000000 c
10.714355 5.000000 10.473413 4.787300 10.437983 4.503861 c
10.137049 2.096386 l
10.116884 1.935076 9.883116 1.935074 9.862951 2.096386 c
9.562017 4.503861 l
9.526587 4.787299 9.285645 5.000000 9.000000 5.000000 c
h
f*
n
Q
endstream
endobj
3 0 obj
3490
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000003580 00000 n
0000003603 00000 n
0000003776 00000 n
0000003850 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
3909
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "trending.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,187 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
1.000000 0.584314 0.000000 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 7.000000 4.949829 cm
1.000000 1.000000 1.000000 scn
7.482707 0.050171 m
13.209023 0.050171 16.000000 4.179979 16.000000 8.852560 c
16.000000 13.454344 13.257144 18.598904 7.987970 21.029589 c
7.771429 21.100386 7.627068 20.982391 7.651128 20.770000 c
8.060151 16.899782 7.458647 13.312751 6.183459 11.613630 c
5.581955 12.982367 4.860150 14.162312 3.753384 15.129866 c
3.584962 15.271460 3.440602 15.200663 3.392481 14.988272 c
3.007519 12.321597 0.000000 10.834866 0.000000 6.681460 c
0.000000 2.716846 2.983459 0.050171 7.482707 0.050171 c
h
f
n
Q
q
q
1.000000 0.000000 -0.000000 1.000000 16.000000 2.340088 cm
1.000000 0.584314 0.000000 scn
7.670000 7.159912 m
7.670000 5.409170 6.250742 3.989912 4.500000 3.989912 c
4.500000 1.329912 l
7.719820 1.329912 10.330000 3.940092 10.330000 7.159912 c
7.670000 7.159912 l
h
4.500000 3.989912 m
2.749257 3.989912 1.330000 5.409170 1.330000 7.159912 c
-1.330000 7.159912 l
-1.330000 3.940092 1.280180 1.329912 4.500000 1.329912 c
4.500000 3.989912 l
h
1.330000 7.159912 m
1.330000 8.910655 2.749257 10.329912 4.500000 10.329912 c
4.500000 12.989912 l
1.280180 12.989912 -1.330000 10.379732 -1.330000 7.159912 c
1.330000 7.159912 l
h
4.500000 10.329912 m
6.250742 10.329912 7.670000 8.910655 7.670000 7.159912 c
10.330000 7.159912 l
10.330000 10.379732 7.719820 12.989912 4.500000 12.989912 c
4.500000 10.329912 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 16.000000 2.340088 cm
1.000000 1.000000 1.000000 scn
9.000000 7.159912 m
9.000000 4.674631 6.985281 2.659912 4.500000 2.659912 c
2.014719 2.659912 0.000000 4.674631 0.000000 7.159912 c
0.000000 9.645193 2.014719 11.659912 4.500000 11.659912 c
6.985281 11.659912 9.000000 9.645193 9.000000 7.159912 c
h
f
n
Q
Q
q
1.000000 0.000000 -0.000000 1.000000 20.500000 6.169922 cm
1.000000 0.584314 0.000000 scn
0.665000 5.330078 m
0.665000 5.697348 0.367269 5.995078 0.000000 5.995078 c
-0.367269 5.995078 -0.665000 5.697348 -0.665000 5.330078 c
0.665000 5.330078 l
h
-0.665000 1.330078 m
-0.665000 0.962809 -0.367269 0.665078 0.000000 0.665078 c
0.367269 0.665078 0.665000 0.962809 0.665000 1.330078 c
-0.665000 1.330078 l
h
-0.665000 5.330078 m
-0.665000 1.330078 l
0.665000 1.330078 l
0.665000 5.330078 l
-0.665000 5.330078 l
h
f
n
Q
q
0.000000 1.000000 -1.000000 0.000000 19.830078 9.500000 cm
1.000000 0.584314 0.000000 scn
0.665000 1.330078 m
0.665000 1.697348 0.367269 1.995078 0.000000 1.995078 c
-0.367269 1.995078 -0.665000 1.697348 -0.665000 1.330078 c
0.665000 1.330078 l
h
-0.665000 -2.669922 m
-0.665000 -3.037191 -0.367269 -3.334922 0.000000 -3.334922 c
0.367269 -3.334922 0.665000 -3.037191 0.665000 -2.669922 c
-0.665000 -2.669922 l
h
-0.665000 1.330078 m
-0.665000 -2.669922 l
0.665000 -2.669922 l
0.665000 1.330078 l
-0.665000 1.330078 l
h
f
n
Q
endstream
endobj
3 0 obj
3743
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000003833 00000 n
0000003856 00000 n
0000004029 00000 n
0000004103 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
4162
%%EOF

View File

@ -433,7 +433,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
self.controllerInteraction.openPeer(peer, .info, nil, .default)
})
case let .openWebView(url, simple):
self.controllerInteraction.openWebView(markupButton.title, url, simple, false)
self.controllerInteraction.openWebView(markupButton.title, url, simple, .generic)
case let .requestPeer(peerType, buttonId):
if let message = self.message {
self.controllerInteraction.openRequestedPeerSelection(message.id, peerType, buttonId)

View File

@ -90,6 +90,7 @@ import ChatControllerInteraction
import FeaturedStickersScreen
import ChatEntityKeyboardInputNode
import StorageUsageScreen
import AvatarEditorScreen
#if DEBUG
import os.signpost
@ -234,6 +235,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
public let subject: ChatControllerSubject?
private let botStart: ChatControllerInitialBotStart?
private var attachBotStart: ChatControllerInitialAttachBotStart?
private var botAppStart: ChatControllerInitialBotAppStart?
private let peerDisposable = MetaDisposable()
private let titleDisposable = MetaDisposable()
@ -545,7 +547,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var currentSpeechHolder: SpeechSynthesizerHolder?
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) {
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) {
let _ = ChatControllerCount.modify { value in
return value + 1
}
@ -556,6 +558,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.subject = subject
self.botStart = botStart
self.attachBotStart = attachBotStart
self.botAppStart = botAppStart
self.peekData = peekData
self.currentChatListFilter = chatListFilter
self.chatNavigationStack = chatNavigationStack
@ -671,6 +674,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return false
}
if strongSelf.presentVoiceMessageDiscardAlert(action: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.1, {
let _ = strongSelf.controllerInteraction?.openMessage(message, mode)
})
}
}, performAction: false) {
return false
}
strongSelf.commitPurposefulAction()
strongSelf.dismissAllTooltips()
@ -833,38 +846,59 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.dismissInput()
if let image = image {
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if let result = itemNode.transitionNode(id: message.id, media: image) {
selectedNode = result
if let emojiMarkup = image.emojiMarkup {
let controller = AvatarEditorScreen(context: strongSelf.context, inputData: AvatarEditorScreen.inputData(context: strongSelf.context, isGroup: false), peerType: .user, markup: emojiMarkup)
controller.imageCompletion = { [weak self] image, commit in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfilePhoto(image, mode: .accept)
commit()
}
}
}
}
let transitionView = selectedNode?.0.view
let senderName: String?
if let peer = message.peers[message.id.peerId] {
senderName = EnginePeer(peer).compactDisplayTitle
controller.videoCompletion = { [weak self] image, url, adjustments, commit in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
commit()
}
}
}
strongSelf.push(controller)
} else {
senderName = nil
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if let result = itemNode.transitionNode(id: message.id, media: image) {
selectedNode = result
}
}
}
let transitionView = selectedNode?.0.view
let senderName: String?
if let peer = message.peers[message.id.peerId] {
senderName = EnginePeer(peer).compactDisplayTitle
} else {
senderName = nil
}
legacyAvatarEditor(context: strongSelf.context, media: .message(message: MessageReference(message), media: image), transitionView: transitionView, senderName: senderName, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
}, imageCompletion: { [weak self] image in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfilePhoto(image, mode: .accept)
}
}
}, videoCompletion: { [weak self] image, url, adjustments in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
}
}
})
}
legacyAvatarEditor(context: strongSelf.context, media: .message(message: MessageReference(message), media: image), transitionView: transitionView, senderName: senderName, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
}, imageCompletion: { [weak self] image in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfilePhoto(image, mode: .accept)
}
}
}, videoCompletion: { [weak self] image, url, adjustments in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
}
}
})
} else {
openMessageByAction = true
}
@ -2529,27 +2563,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}, openInstantPage: { [weak self] message, associatedData in
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.effectiveNavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
strongSelf.chatDisplayNode.dismissInput()
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
if case .overlay = strongSelf.presentationInterfaceState.mode {
strongSelf.chatDisplayNode.dismissAsOverlay()
}
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
strongSelf.chatDisplayNode.dismissInput()
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
if case .overlay = strongSelf.presentationInterfaceState.mode {
strongSelf.chatDisplayNode.dismissAsOverlay()
}
})
}
}, openWallpaper: { [weak self] message in
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
strongSelf.chatDisplayNode.dismissInput()
openChatWallpaper(context: strongSelf.context, message: message, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
strongSelf.chatDisplayNode.dismissInput()
openChatWallpaper(context: strongSelf.context, message: message, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
})
})
}
}, openTheme: { [weak self] message in
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
strongSelf.chatDisplayNode.dismissInput()
openChatTheme(context: strongSelf.context, message: message, pushController: { [weak self] c in
self?.effectiveNavigationController?.pushViewController(c)
}, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
strongSelf.chatDisplayNode.dismissInput()
openChatTheme(context: strongSelf.context, message: message, pushController: { [weak self] c in
self?.effectiveNavigationController?.pushViewController(c)
}, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
})
})
}
}, openHashtag: { [weak self] peerName, hashtag in
@ -3984,7 +4024,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil)
}, openWebView: { [weak self] buttonText, url, simple, fromMenu in
}, openWebView: { [weak self] buttonText, url, simple, source in
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser else {
return
}
@ -3993,7 +4033,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let botName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
if !fromMenu {
if source == .generic {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if !$0.contains(where: {
@ -4038,12 +4078,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let openWebView = {
if fromMenu {
if source == .menu {
strongSelf.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
}
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, isSimple: false)
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, isInline: false, isSimple: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
self?.openUrl(url, concealed: true, forceExternal: true)
}, getInputContainerNode: { [weak self] in
@ -4079,7 +4119,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.push(controller)
strongSelf.currentMenuWebAppController = controller
} else if simple {
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: peerId, url: url, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
var isInline = false
var botId = peerId
var botName = botName
var botAddress = ""
if case let .inline(bot) = source {
isInline = true
botId = bot.id
botName = bot.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
botAddress = bot.addressName ?? ""
}
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: botId, url: url, inline: isInline, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
|> afterDisposed {
updateProgress()
})
@ -4087,9 +4138,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, isSimple: true)
let params = WebAppParameters(peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, isInline: isInline, isSimple: true)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
self?.openUrl(url, concealed: true, forceExternal: true)
}, requestSwitchInline: { [weak self] query, chatTypes in
if let strongSelf = self {
if let _ = chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: nil, hasContactSelector: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)")
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)")
}
}
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController
})
@ -4111,7 +4177,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false, isSimple: false)
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false, isInline: false, isSimple: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
self?.openUrl(url, concealed: true, forceExternal: true)
}, completion: { [weak self] in
@ -10009,9 +10075,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
})
}
}, openWebView: { [weak self] buttonText, url, simple, fromMenu in
}, openWebView: { [weak self] buttonText, url, simple, source in
if let strongSelf = self {
strongSelf.controllerInteraction?.openWebView(buttonText, url, simple, fromMenu)
strongSelf.controllerInteraction?.openWebView(buttonText, url, simple, source)
}
}, updateShowWebView: { [weak self] f in
if let strongSelf = self {
@ -12228,7 +12294,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
private func editMessageMediaWithLegacySignals(_ signals: [Any]) {
let _ = (legacyAssetPickerEnqueueMessages(account: self.context.account, signals: signals)
let _ = (legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals)
|> deliverOnMainQueue).start(next: { [weak self] messages in
self?.editMessageMediaWithMessages(messages.map { $0.message })
})
@ -12424,6 +12490,81 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.presentAttachmentMenu(editMediaOptions: nil, editMediaReference: nil, botId: botId, botPayload: payload, botJustInstalled: justInstalled)
}
public func presentBotApp(botApp: BotApp, payload: String?) {
guard let peerId = self.chatLocation.peerId else {
return
}
self.attachmentController?.dismiss(animated: true, completion: nil)
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if !$0.contains(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.append(.requestInProgress)
return updatedContexts.sorted()
}
return $0
}
})
let updateProgress = { [weak self] in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if let index = $0.firstIndex(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.remove(at: index)
return updatedContexts
}
return $0
}
})
}
}
}
self.messageActionCallbackDisposable.set(((self.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(self.presentationData.theme), allowWrite: false)
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).start(next: { [weak self] url in
guard let strongSelf = self else {
return
}
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
self?.openUrl(url, concealed: true, forceExternal: true)
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.currentWebAppController = controller
strongSelf.push(controller)
}, error: { [weak self] error in
if let strongSelf = self {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}))
}
private func presentAttachmentMenu(editMediaOptions: MessageMediaEditingOptions?, editMediaReference: AnyMediaReference?, botId: PeerId? = nil, botPayload: String? = nil, botJustInstalled: Bool = false) {
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return
@ -12876,7 +13017,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
completion(controller, controller?.mediaPickerContext)
strongSelf.controllerNavigationDisposable.set(nil)
case let .app(bot, botName, _):
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: botPayload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isSimple: false)
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: botPayload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false)
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId, threadId: strongSelf.chatLocation.threadId)
controller.openUrl = { [weak self] url in
@ -14408,7 +14549,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.context.account, signals: signals!)
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!)
|> deliverOnMainQueue).start(next: { [weak self] items in
if let strongSelf = self {
var completionImpl: (() -> Void)? = completion
@ -16262,6 +16403,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let navigationController = self.effectiveNavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), attachBotStart: attachBotStart))
}
case let .withBotApp(botAppStart):
if let navigationController = self.effectiveNavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), botAppStart: botAppStart))
}
}
}
} else {
@ -16741,6 +16886,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let navigationController = strongSelf.effectiveNavigationController {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), attachBotStart: attachBotStart))
}
case let .withBotApp(botAppStart):
strongSelf.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload)
default:
break
}
@ -17647,24 +17794,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
private func openPinnedMessages(at messageId: MessageId?) {
guard let navigationController = self.effectiveNavigationController, navigationController.topViewController == self else {
return
}
let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .pinnedMessages(id: messageId))
controller.navigationPresentation = .modal
controller.updatedClosedPinnedMessageId = { [weak self] pinnedMessageId in
guard let strongSelf = self else {
let _ = self.presentVoiceMessageDiscardAlert(action: { [weak self] in
guard let self, let navigationController = self.effectiveNavigationController, navigationController.topViewController == self else {
return
}
strongSelf.performUpdatedClosedPinnedMessageId(pinnedMessageId: pinnedMessageId)
}
controller.requestedUnpinAllMessages = { [weak self] count, pinnedMessageId in
guard let strongSelf = self else {
return
let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .pinnedMessages(id: messageId))
controller.navigationPresentation = .modal
controller.updatedClosedPinnedMessageId = { [weak self] pinnedMessageId in
guard let strongSelf = self else {
return
}
strongSelf.performUpdatedClosedPinnedMessageId(pinnedMessageId: pinnedMessageId)
}
strongSelf.performRequestedUnpinAllMessages(count: count, pinnedMessageId: pinnedMessageId)
}
navigationController.pushViewController(controller)
controller.requestedUnpinAllMessages = { [weak self] count, pinnedMessageId in
guard let strongSelf = self else {
return
}
strongSelf.performRequestedUnpinAllMessages(count: count, pinnedMessageId: pinnedMessageId)
}
navigationController.pushViewController(controller)
})
}
private func performUpdatedClosedPinnedMessageId(pinnedMessageId: MessageId) {

View File

@ -154,7 +154,7 @@ func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfa
return nil
}
case let .contextRequestResult(_, results):
if let results = results, (!results.results.isEmpty || results.switchPeer != nil) {
if let results = results, (!results.results.isEmpty || results.switchPeer != nil || results.webView != nil) {
switch results.presentation {
case .list:
if let currentPanel = currentPanel as? VerticalListContextResultsChatInputContextPanelNode {

View File

@ -118,7 +118,6 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
let labelNode: TextNode
let backgroundNode: NavigationBackgroundNode
let stickBackgroundNode: ASImageNode
let activateArea: AccessibilityAreaNode
private var backgroundContent: WallpaperBubbleBackgroundNode?
@ -191,9 +190,6 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
}
self.text = text
self.activateArea = AccessibilityAreaNode()
self.activateArea.accessibilityTraits = .staticText
super.init(layerBacked: false, dynamicBounce: true, isRotated: true, seeThrough: false)
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
@ -212,16 +208,12 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.addSubnode(self.backgroundNode)
}
self.addSubnode(self.labelNode)
self.addSubnode(self.activateArea)
let titleFont = Font.medium(min(18.0, floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)))
let attributedString = NSAttributedString(string: text, font: titleFont, textColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
let labelLayout = TextNode.asyncLayout(self.labelNode)
self.activateArea.accessibilityLabel = text
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let _ = apply()
self.labelNode.frame = CGRect(origin: CGPoint(), size: size.size)
@ -285,9 +277,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.backgroundNode.frame = backgroundFrame
self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.size.height / 2.0, transition: .immediate)
self.labelNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + chatDateInset, y: backgroundFrame.origin.y + floorToScreenPixels((backgroundSize.height - labelSize.height) / 2.0)), size: labelSize)
self.activateArea.frame = backgroundFrame
if let backgroundContent = self.backgroundContent {
backgroundContent.allowsGroupOpacity = true
self.backgroundNode.isHidden = true

View File

@ -897,7 +897,7 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol
}
})
case let .openWebView(url, simple):
item.controllerInteraction.openWebView(button.title, url, simple, false)
item.controllerInteraction.openWebView(button.title, url, simple, .generic)
case .requestPeer:
break
}

View File

@ -900,7 +900,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
}
})
case let .openWebView(url, simple):
controllerInteraction.openWebView(button.title, url, simple, false)
controllerInteraction.openWebView(button.title, url, simple, .generic)
case .requestPeer:
break
}

View File

@ -3637,7 +3637,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} else if case let .webView(title, url) = presentationInterfaceState.botMenuButton {
let willShow = !(self.presentationInterfaceState?.showWebView ?? false)
if willShow {
self.interfaceInteraction?.openWebView(title, url, false, true)
self.interfaceInteraction?.openWebView(title, url, false, .menu)
} else {
self.interfaceInteraction?.updateShowWebView { _ in
return false

View File

@ -292,7 +292,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
existingIds.insert(result.id)
}
}
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.queryId, nextOffset: nextResults.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.queryId, nextOffset: nextResults.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, webView: currentProcessedResults.webView, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
strongSelf.currentProcessedResults = mergedResults
strongSelf.updateInternalResults(mergedResults)
}))

View File

@ -244,7 +244,7 @@ func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedMedia: B
})
if let parentController = parentController {
parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, _, text, account, silently in
return legacyAssetPickerEnqueueMessages(account: account, signals: signals!)
return legacyAssetPickerEnqueueMessages(context: context, account: account, signals: signals!)
|> `catch` { _ -> Signal<[LegacyAssetPickerEnqueueMessage], ShareControllerError> in
return .single([])
}

View File

@ -132,8 +132,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if let attachBotStart = params.attachBotStart {
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
}
if let botAppStart = params.botAppStart {
controller.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload)
}
} else {
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack)
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, botAppStart: params.botAppStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack)
}
controller.purposefulAction = params.purposefulAction
if let search = params.activateMessageSearch {

View File

@ -687,6 +687,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
var startAttach: String?
var choose: String?
var threadId: Int64?
var appName: String?
var startApp: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
@ -714,6 +716,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
choose = value
} else if queryItem.name == "thread" {
threadId = Int64(value)
} else if queryItem.name == "appname" {
appName = value
} else if queryItem.name == "startapp" {
startApp = value
}
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
voiceChat = ""
@ -731,13 +737,19 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
convertedUrl = "https://t.me/+\(phone)"
} else if let domain = domain {
var result = "https://t.me/\(domain)"
if let threadId = threadId {
if let appName {
result += "\(appName)"
}
if let startApp {
result += "?startapp=\(startApp)"
}
if let threadId {
result += "/\(threadId)"
if let post = post, let postValue = Int(post) {
if let post, let postValue = Int(post) {
result += "/\(postValue)"
}
} else {
if let post = post, let postValue = Int(post) {
if let post, let postValue = Int(post) {
result += "/\(postValue)"
}
}

View File

@ -3980,7 +3980,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return nil
}, sendMessagesWithSignals: { [weak self] signals, _, _ in
if let strongSelf = self {
strongSelf.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: strongSelf.context.account, signals: signals!)
strongSelf.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: strongSelf.context, account: strongSelf.context.account, signals: signals!)
|> deliverOnMainQueue).start(next: { [weak self] messages in
if let strongSelf = self {
let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.peerId, messages: messages.map { $0.message }).start()
@ -4019,6 +4019,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botStart: startPayload))
case let .withAttachBot(attachBotStart):
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), attachBotStart: attachBotStart))
case let .withBotApp(botAppStart):
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botAppStart: botAppStart))
default:
break
}
@ -4101,6 +4103,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if let navigationController = self.controller?.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), attachBotStart: attachBotStart))
}
case let .withBotApp(botAppStart):
if let navigationController = self.controller?.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), botAppStart: botAppStart))
}
}
})
}
@ -4332,9 +4338,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}, action: { _, f in
f(.default)
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
return
}
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)

View File

@ -187,6 +187,11 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
entries.append(entry)
resultIds.insert(entry.stableId)
}
if let webView = results.webView {
let entry: VerticalListContextResultsChatInputContextPanelEntry = .action(self.theme, webView.text)
entries.append(entry)
resultIds.insert(entry.stableId)
}
for result in results.results {
let entry: VerticalListContextResultsChatInputContextPanelEntry = .result(index, self.theme, result)
if resultIds.contains(entry.stableId) {
@ -204,8 +209,17 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
private func prepareTransition(from: [VerticalListContextResultsChatInputContextPanelEntry]?, to: [VerticalListContextResultsChatInputContextPanelEntry], results: ChatContextResultCollection) {
let firstTime = self.currentEntries == nil
let transition = preparedTransition(from: from ?? [], to: to, account: self.context.account, actionSelected: { [weak self] in
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction, let switchPeer = results.switchPeer {
interfaceInteraction.botSwitchChatWithPayload(results.botId, switchPeer.startParam)
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction {
if let switchPeer = results.switchPeer {
interfaceInteraction.botSwitchChatWithPayload(results.botId, switchPeer.startParam)
} else if let webView = results.webView {
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: results.botId))
|> deliverOnMainQueue).start(next: { bot in
if let bot {
interfaceInteraction.openWebView(webView.text, webView.url, true, .inline(bot: bot))
}
})
}
}
}, resultSelected: { [weak self] result, node, rect in
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction {
@ -241,7 +255,7 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
}
var insets = UIEdgeInsets()
insets.top = topInsetForLayout(size: validLayout.0, hasSwitchPeer: self.currentExternalResults?.switchPeer != nil)
insets.top = topInsetForLayout(size: validLayout.0, hasSwitchPeer: self.currentExternalResults?.switchPeer != nil || self.currentExternalResults?.webView != nil)
insets.left = validLayout.1
insets.right = validLayout.2
@ -284,7 +298,7 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
self.validLayout = (size, leftInset, rightInset, bottomInset)
var insets = UIEdgeInsets()
insets.top = self.topInsetForLayout(size: size, hasSwitchPeer: self.currentExternalResults?.switchPeer != nil)
insets.top = self.topInsetForLayout(size: size, hasSwitchPeer: self.currentExternalResults?.switchPeer != nil || self.currentExternalResults?.webView != nil)
insets.left = leftInset
insets.right = rightInset
@ -362,7 +376,7 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
existingIds.insert(result.id)
}
}
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.queryId, nextOffset: nextResults.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.queryId, nextOffset: nextResults.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, webView: currentProcessedResults.webView, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
strongSelf.currentProcessedResults = mergedResults
strongSelf.updateInternalResults(mergedResults)
}))

View File

@ -13,15 +13,17 @@ public struct StickerSettings: Codable, Equatable {
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
public var loopAnimatedStickers: Bool
public var suggestAnimatedEmoji: Bool
public var dynamicPackOrder: Bool
public static var defaultSettings: StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true, suggestAnimatedEmoji: true)
return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true, suggestAnimatedEmoji: true, dynamicPackOrder: true)
}
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool, suggestAnimatedEmoji: Bool) {
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool, suggestAnimatedEmoji: Bool, dynamicPackOrder: Bool) {
self.emojiStickerSuggestionMode = emojiStickerSuggestionMode
self.loopAnimatedStickers = loopAnimatedStickers
self.suggestAnimatedEmoji = suggestAnimatedEmoji
self.dynamicPackOrder = dynamicPackOrder
}
public init(from decoder: Decoder) throws {
@ -30,6 +32,7 @@ public struct StickerSettings: Codable, Equatable {
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: try container.decode(Int32.self, forKey: "emojiStickerSuggestionMode"))!
self.loopAnimatedStickers = try container.decodeIfPresent(Bool.self, forKey: "loopAnimatedStickers") ?? true
self.suggestAnimatedEmoji = try container.decodeIfPresent(Bool.self, forKey: "suggestAnimatedEmoji") ?? true
self.dynamicPackOrder = try container.decodeIfPresent(Bool.self, forKey: "dynamicPackOrder") ?? true
}
public func encode(to encoder: Encoder) throws {
@ -38,22 +41,27 @@ public struct StickerSettings: Codable, Equatable {
try container.encode(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
try container.encode(self.loopAnimatedStickers, forKey: "loopAnimatedStickers")
try container.encode(self.suggestAnimatedEmoji, forKey: "suggestAnimatedEmoji")
try container.encode(self.dynamicPackOrder, forKey: "dynamicPackOrder")
}
public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool {
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers && lhs.suggestAnimatedEmoji == rhs.suggestAnimatedEmoji
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers && lhs.suggestAnimatedEmoji == rhs.suggestAnimatedEmoji && lhs.dynamicPackOrder == rhs.dynamicPackOrder
}
public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji)
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji)
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedSuggestAnimatedEmoji(_ suggestAnimatedEmoji: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: suggestAnimatedEmoji)
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedDynamicPackOrder(_ dynamicPackOrder: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: dynamicPackOrder)
}
}

View File

@ -71,6 +71,7 @@ public enum ParsedInternalPeerUrlParameter {
case channelMessage(Int32, Double?)
case replyThread(Int32, Int32)
case voiceChat(String?)
case appStart(String, String?)
}
public enum ParsedInternalUrl {
@ -510,6 +511,19 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
} else {
return .peer(.name(peerName), .channelMessage(value, timecode))
}
} else if pathComponents.count == 2 {
let appName = pathComponents[1]
var startApp: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "startApp"{
startApp = value
}
}
}
}
return .peer(.name(peerName), .appStart(appName, startApp))
} else {
return nil
}
@ -598,6 +612,20 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
}
}
case let .appStart(name, payload):
return context.engine.messages.getBotApp(botId: peer.id, shortName: name, cached: false)
|> map(Optional.init)
|> `catch` { _ -> Signal<BotApp?, NoError> in
return .single(nil)
}
|> take(1)
|> mapToSignal { botApp -> Signal<ResolvedUrl?, NoError> in
if let botApp {
return .single(.peer(peer, .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false))))
} else {
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
}
}
case let .channelMessage(id, timecode):
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
let messageId = MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: id)

View File

@ -629,7 +629,7 @@ class WebSearchControllerNode: ASDisplayNode {
existingIds.insert(result.id)
}
}
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.results.queryId, nextOffset: nextResults.results.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.results.queryId, nextOffset: nextResults.results.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, webView: currentProcessedResults.webView, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
strongSelf.currentProcessedResults = mergedResults
strongSelf.results.set(mergedResults)
}))

View File

@ -134,6 +134,7 @@ public struct WebAppParameters {
let buttonText: String?
let keepAliveSignal: Signal<Never, KeepWebViewError>?
let fromMenu: Bool
let isInline: Bool
let isSimple: Bool
public init(
@ -146,6 +147,7 @@ public struct WebAppParameters {
buttonText: String?,
keepAliveSignal: Signal<Never, KeepWebViewError>?,
fromMenu: Bool,
isInline: Bool,
isSimple: Bool
) {
self.peerId = peerId
@ -157,6 +159,7 @@ public struct WebAppParameters {
self.buttonText = buttonText
self.keepAliveSignal = keepAliveSignal
self.fromMenu = fromMenu
self.isInline = isInline
self.isSimple = isSimple
}
}
@ -186,7 +189,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
public var cancelPanGesture: () -> Void = { }
public var isContainerPanning: () -> Bool = { return false }
public var isContainerExpanded: () -> Bool = { return false }
fileprivate class Node: ViewControllerTracingNode, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
private weak var controller: WebAppController?
@ -622,6 +625,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
switch eventName {
case "web_app_ready":
self.animateTransitionIn()
case "web_app_switch_inline_query":
if controller.isInline, let json, let query = json["query"] as? String {
controller.dismiss()
if let chatTypes = json["chat_types"] as? [String], !chatTypes.isEmpty {
controller.requestSwitchInline(query, .user(.init(isBot: nil, isPremium: nil)))
} else {
controller.requestSwitchInline(query, nil)
}
}
case "web_app_data_send":
if controller.isSimple, let eventData = body["eventData"] as? String {
self.handleSendData(data: eventData)
@ -1029,6 +1041,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
private let payload: String?
private let buttonText: String?
private let fromMenu: Bool
private let isInline: Bool
private let isSimple: Bool
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
private let replyToMessageId: MessageId?
@ -1041,7 +1054,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
public var openUrl: (String) -> Void = { _ in }
public var getNavigationController: () -> NavigationController? = { return nil }
public var completion: () -> Void = {}
public var requestSwitchInline: (String, ReplyMarkupButtonRequestPeerType?) -> Void = { _, _ in }
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, replyToMessageId: MessageId?, threadId: Int64?) {
self.context = context
self.peerId = params.peerId
@ -1052,6 +1066,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.payload = params.payload
self.buttonText = params.buttonText
self.fromMenu = params.fromMenu
self.isInline = params.isInline
self.isSimple = params.isSimple
self.keepAliveSignal = params.keepAliveSignal
self.replyToMessageId = replyToMessageId
@ -1334,7 +1349,7 @@ private final class WebAppContextReferenceContentSource: ContextReferenceContent
}
}
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, threadId: Int64?, openUrl: @escaping (String) -> Void, getInputContainerNode: @escaping () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil }, completion: @escaping () -> Void = {}, willDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {}, getNavigationController: @escaping () -> NavigationController? = { return nil }, getSourceRect: (() -> CGRect?)? = nil) -> ViewController {
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, threadId: Int64?, openUrl: @escaping (String) -> Void, requestSwitchInline: @escaping (String, ReplyMarkupButtonRequestPeerType?) -> Void = { _, _ in }, getInputContainerNode: @escaping () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil }, completion: @escaping () -> Void = {}, willDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {}, getNavigationController: @escaping () -> NavigationController? = { return nil }, getSourceRect: (() -> CGRect?)? = nil) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.fromMenu, hasTextInput: false, makeEntityInputView: {
return nil
})
@ -1344,6 +1359,7 @@ public func standaloneWebAppController(context: AccountContext, updatedPresentat
webAppController.openUrl = openUrl
webAppController.completion = completion
webAppController.getNavigationController = getNavigationController
webAppController.requestSwitchInline = requestSwitchInline
present(webAppController, webAppController.mediaPickerContext)
}
controller.willDismiss = willDismiss