diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index de48533a50..cb23442c95 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -93,6 +93,8 @@ swift_library( "//submodules/ItemListUI", "//submodules/QrCodeUI", "//submodules/TelegramUI/Components/ActionPanelComponent", + "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", + "//submodules/TelegramUI/Components/Stories/StoryContentComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 94832b4542..6669fd504f 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -45,6 +45,8 @@ import ChatListHeaderComponent import ChatListTitleView import InviteLinksUI import ChatFolderLinkPreviewScreen +import StoryContainerScreen +import StoryContentComponent private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { if listNode.scroller.isDragging { @@ -205,6 +207,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var powerSavingMonitoringDisposable: Disposable? + private var storyListContext: StoryListContext? + private var storyListStateDisposable: Disposable? + public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { if self.isNodeLoaded { self.chatListDisplayNode.effectiveContainerNode.updateSelectedChatLocation(data: data as? ChatLocation, progress: progress, transition: transition) @@ -739,6 +744,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.joinForumDisposable.dispose() self.actionDisposables.dispose() self.powerSavingMonitoringDisposable?.dispose() + self.storyListStateDisposable?.dispose() } private func updateNavigationMetadata() { @@ -1314,6 +1320,30 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } + self.chatListDisplayNode.mainContainerNode.openStories = { [weak self] peerId in + guard let self, let storyListContext = self.storyListContext else { + return + } + + let _ = (StoryChatContent.stories( + context: context, + storyList: storyListContext, + focusItem: nil + ) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] initialContent in + guard let self else { + return + } + let storyContainerScreen = StoryContainerScreen( + context: self.context, + initialFocusedId: AnyHashable(peerId), + initialContent: initialContent + ) + self.push(storyContainerScreen) + }) + } + self.chatListDisplayNode.peerContextAction = { [weak self] peer, source, node, gesture, location in guard let strongSelf = self else { gesture?.cancel() @@ -2029,6 +2059,29 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } }) + + let storyListContext = self.context.engine.messages.allStories() + self.storyListContext = storyListContext + self.storyListStateDisposable = (storyListContext.state + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let self else { + return + } + let _ = self + let _ = state + + self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in + var chatListState = chatListState + + var peersWithNewStories = Set() + for itemSet in state.itemSets { + peersWithNewStories.insert(itemSet.peerId) + } + chatListState.peersWithNewStories = peersWithNewStories + + return chatListState + } + }) } self.chatListDisplayNode.mainContainerNode.addedVisibleChatsWithPeerIds = { [weak self] peerIds in diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 875f0a0b05..ef573294fd 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -190,7 +190,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {}) + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _ in }) interaction.isInlineMode = isInlineMode let items = (0 ..< 2).map { _ -> ChatListItem in @@ -237,7 +237,8 @@ private final class ChatListShimmerNode: ASDisplayNode { hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } @@ -818,6 +819,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele previousItemNode.listNode.contentOffsetChanged = nil previousItemNode.listNode.contentScrollingEnded = nil previousItemNode.listNode.activateChatPreview = nil + previousItemNode.listNode.openStories = nil previousItemNode.listNode.addedVisibleChatsWithPeerIds = nil previousItemNode.listNode.didBeginSelectingChats = nil @@ -877,6 +879,9 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele itemNode.listNode.activateChatPreview = { [weak self] item, threadId, sourceNode, gesture, location in self?.activateChatPreview?(item, threadId, sourceNode, gesture, location) } + itemNode.listNode.openStories = { [weak self] peerId in + self?.openStories?(peerId) + } itemNode.listNode.addedVisibleChatsWithPeerIds = { [weak self] ids in self?.addedVisibleChatsWithPeerIds?(ids) } @@ -940,6 +945,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)? public var contentScrollingEnded: ((ListView) -> Bool)? var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? + var openStories: ((EnginePeer.Id) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? public var displayFilterLimit: (() -> Void)? @@ -1976,6 +1982,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { } inlineStackContainerNode.activateChatPreview = self.mainContainerNode.activateChatPreview + inlineStackContainerNode.openStories = self.mainContainerNode.openStories inlineStackContainerNode.addedVisibleChatsWithPeerIds = self.mainContainerNode.addedVisibleChatsWithPeerIds inlineStackContainerNode.didBeginSelectingChats = self.mainContainerNode.didBeginSelectingChats inlineStackContainerNode.displayFilterLimit = nil diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index e1354ef937..f9f215148a 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -797,7 +797,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable { hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } case let .addContact(phoneNumber, theme, strings): @@ -2166,6 +2167,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }, openPremiumIntro: { }, openChatFolderUpdates: { }, hideChatFolderUpdates: { + }, openStories: { _ in }) chatListInteraction.isSearchMode = true @@ -3400,6 +3402,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: { + }, openStories: { _ in }) var isInlineMode = false if case .topics = key { @@ -3453,7 +3456,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) case .media: return nil diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 3949543938..e41219df5b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -82,6 +82,7 @@ public enum ChatListItemContent { public var forumTopicData: EngineChatList.ForumTopicData? public var topForumTopicItems: [EngineChatList.ForumTopicData] public var autoremoveTimeout: Int32? + public var hasNewStories: Bool public init( messages: [EngineMessage], @@ -100,7 +101,8 @@ public enum ChatListItemContent { hasFailedMessages: Bool, forumTopicData: EngineChatList.ForumTopicData?, topForumTopicItems: [EngineChatList.ForumTopicData], - autoremoveTimeout: Int32? + autoremoveTimeout: Int32?, + hasNewStories: Bool ) { self.messages = messages self.peer = peer @@ -119,6 +121,7 @@ public enum ChatListItemContent { self.forumTopicData = forumTopicData self.topForumTopicItems = topForumTopicItems self.autoremoveTimeout = autoremoveTimeout + self.hasNewStories = hasNewStories } } @@ -899,6 +902,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var avatarIconView: ComponentHostView? var avatarIconComponent: EmojiStatusComponent? var avatarVideoNode: AvatarVideoNode? + var avatarStoryIndicatorNode: ASImageNode? private var inlineNavigationMarkLayer: SimpleLayer? @@ -2725,6 +2729,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0) + var displayStoryIndicator = false + if case let .peer(peerData) = item.content, peerData.hasNewStories { + displayStoryIndicator = true + } + let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) var avatarScaleOffset: CGFloat = 0.0 var avatarScale: CGFloat = 1.0 @@ -2735,6 +2744,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let targetAvatarScaleOffset: CGFloat = -(avatarFrame.width - avatarFrame.width * avatarScale) * 0.5 avatarScaleOffset = targetAvatarScaleOffset * inlineNavigationLocation.progress } + + if displayStoryIndicator { + avatarScale *= (avatarFrame.width - 4.0 * 2.0) / avatarFrame.width + } + transition.updateFrame(node: strongSelf.avatarContainerNode, frame: avatarFrame) transition.updatePosition(node: strongSelf.avatarNode, position: avatarFrame.offsetBy(dx: -avatarFrame.minX, dy: -avatarFrame.minY).center.offsetBy(dx: avatarScaleOffset, dy: 0.0)) transition.updateBounds(node: strongSelf.avatarNode, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size)) @@ -2742,6 +2756,53 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.avatarNode.updateSize(size: avatarFrame.size) strongSelf.updateVideoVisibility() + if displayStoryIndicator { + let avatarStoryIndicatorNode: ASImageNode + if let current = strongSelf.avatarStoryIndicatorNode { + avatarStoryIndicatorNode = current + } else { + avatarStoryIndicatorNode = ASImageNode() + strongSelf.avatarStoryIndicatorNode = avatarStoryIndicatorNode + strongSelf.contextContainer.insertSubnode(avatarStoryIndicatorNode, belowSubnode: strongSelf.avatarContainerNode) + + avatarStoryIndicatorNode.isUserInteractionEnabled = true + avatarStoryIndicatorNode.view.addGestureRecognizer(UITapGestureRecognizer(target: strongSelf, action: #selector(strongSelf.avatarStoryTapGesture(_:)))) + } + var updateImage = false + if let image = avatarStoryIndicatorNode.image { + if image.size != avatarFrame.size { + updateImage = true + } + } else { + updateImage = true + } + if updateImage { + avatarStoryIndicatorNode.image = generateImage(avatarFrame.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let lineWidth: CGFloat = 2.0 + context.setLineWidth(lineWidth) + context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)) + context.replacePathWithStrokedPath() + context.clip() + + var locations: [CGFloat] = [1.0, 0.0] + let colors: [CGColor] = [UIColor(rgb: 0x34C76F).cgColor, UIColor(rgb: 0x3DA1FD).cgColor] + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) + }) + } + transition.updateFrame(node: avatarStoryIndicatorNode, frame: avatarFrame) + } else { + if let avatarStoryIndicatorNode = strongSelf.avatarStoryIndicatorNode { + strongSelf.avatarStoryIndicatorNode = nil + avatarStoryIndicatorNode.removeFromSupernode() + } + } + var itemPeerId: EnginePeer.Id? if case let .chatList(index) = item.index { itemPeerId = index.messageIndex.id.peerId @@ -3756,6 +3817,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } + if let avatarStoryIndicatorNode = self.avatarStoryIndicatorNode, let result = avatarStoryIndicatorNode.view.hitTest(self.view.convert(point, to: avatarStoryIndicatorNode.view), with: event) { + return result + } + return super.hitTest(point, with: event) } + + @objc private func avatarStoryTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + guard let item = self.item, case let .peer(peerData) = item.content else { + return + } + item.interaction.openStories(peerData.peer.peerId) + } + } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 35759919c5..19b8f53a1b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -18,6 +18,7 @@ import AnimationCache import MultiAnimationRenderer import Postbox import ChatFolderLinkPreviewScreen +import StoryContainerScreen public enum ChatListNodeMode { case chatList(appendContacts: Bool) @@ -98,6 +99,7 @@ public final class ChatListNodeInteraction { let openPremiumIntro: () -> Void let openChatFolderUpdates: () -> Void let hideChatFolderUpdates: () -> Void + let openStories: (EnginePeer.Id) -> Void public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? @@ -144,7 +146,8 @@ public final class ChatListNodeInteraction { openPasswordSetup: @escaping () -> Void, openPremiumIntro: @escaping () -> Void, openChatFolderUpdates: @escaping () -> Void, - hideChatFolderUpdates: @escaping () -> Void + hideChatFolderUpdates: @escaping () -> Void, + openStories: @escaping (EnginePeer.Id) -> Void ) { self.activateSearch = activateSearch self.peerSelected = peerSelected @@ -179,6 +182,7 @@ public final class ChatListNodeInteraction { self.openPremiumIntro = openPremiumIntro self.openChatFolderUpdates = openChatFolderUpdates self.hideChatFolderUpdates = hideChatFolderUpdates + self.openStories = openStories } } @@ -236,6 +240,7 @@ public struct ChatListNodeState: Equatable { public var foundPeers: [(EnginePeer, EnginePeer?)] public var selectedPeerMap: [EnginePeer.Id: EnginePeer] public var selectedThreadIds: Set + public var peersWithNewStories: Set public init( presentationData: ChatListPresentationData, @@ -250,7 +255,8 @@ public struct ChatListNodeState: Equatable { pendingClearHistoryPeerIds: Set, hiddenItemShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: EnginePeer.Id?, - selectedThreadIds: Set + selectedThreadIds: Set, + peersWithNewStories: Set ) { self.presentationData = presentationData self.editing = editing @@ -265,6 +271,7 @@ public struct ChatListNodeState: Equatable { self.hiddenItemShouldBeTemporaryRevealed = hiddenItemShouldBeTemporaryRevealed self.hiddenPsaPeerId = hiddenPsaPeerId self.selectedThreadIds = selectedThreadIds + self.peersWithNewStories = peersWithNewStories } public static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool { @@ -307,6 +314,9 @@ public struct ChatListNodeState: Equatable { if lhs.selectedThreadIds != rhs.selectedThreadIds { return false } + if lhs.peersWithNewStories != rhs.peersWithNewStories { + return false + } return true } } @@ -384,7 +394,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL hasFailedMessages: hasFailedMessages, forumTopicData: forumTopicData, topForumTopicItems: topForumTopicItems, - autoremoveTimeout: peerEntry.autoremoveTimeout + autoremoveTimeout: peerEntry.autoremoveTimeout, + hasNewStories: peerEntry.hasNewStories )), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -727,7 +738,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL hasFailedMessages: hasFailedMessages, forumTopicData: forumTopicData, topForumTopicItems: topForumTopicItems, - autoremoveTimeout: peerEntry.autoremoveTimeout + autoremoveTimeout: peerEntry.autoremoveTimeout, + hasNewStories: peerEntry.hasNewStories )), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -1069,6 +1081,7 @@ public final class ChatListNode: ListView { public var toggleArchivedFolderHiddenByDefault: (() -> Void)? public var hidePsa: ((EnginePeer.Id) -> Void)? public var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? + public var openStories: ((EnginePeer.Id) -> Void)? private var theme: PresentationTheme @@ -1182,7 +1195,7 @@ public final class ChatListNode: ListView { isSelecting = true } - self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: isSelecting, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), foundPeers: [], selectedPeerMap: [:], selectedAdditionalCategoryIds: Set(), peerInputActivities: nil, pendingRemovalItemIds: Set(), pendingClearHistoryPeerIds: Set(), hiddenItemShouldBeTemporaryRevealed: false, hiddenPsaPeerId: nil, selectedThreadIds: Set()) + self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: isSelecting, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), foundPeers: [], selectedPeerMap: [:], selectedAdditionalCategoryIds: Set(), peerInputActivities: nil, pendingRemovalItemIds: Set(), pendingClearHistoryPeerIds: Set(), hiddenItemShouldBeTemporaryRevealed: false, hiddenPsaPeerId: nil, selectedThreadIds: Set(), peersWithNewStories: Set()) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.theme = theme @@ -1517,6 +1530,11 @@ public final class ChatListNode: ListView { let _ = self.context.engine.peers.hideChatFolderUpdates(folderId: localFilterId).start() } }) + }, openStories: { [weak self] peerId in + guard let self else { + return + } + self.openStories?(peerId) }) nodeInteraction.isInlineMode = isInlineMode diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index 0bea6d2d5d..eadb228339 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -111,6 +111,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { var forumTopicData: EngineChatList.ForumTopicData? var topForumTopicItems: [EngineChatList.ForumTopicData] var revealed: Bool + var hasNewStories: Bool init( index: EngineChatList.Item.Index, @@ -134,7 +135,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { autoremoveTimeout: Int32?, forumTopicData: EngineChatList.ForumTopicData?, topForumTopicItems: [EngineChatList.ForumTopicData], - revealed: Bool + revealed: Bool, + hasNewStories: Bool ) { self.index = index self.presentationData = presentationData @@ -158,6 +160,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { self.forumTopicData = forumTopicData self.topForumTopicItems = topForumTopicItems self.revealed = revealed + self.hasNewStories = hasNewStories } static func ==(lhs: PeerEntryData, rhs: PeerEntryData) -> Bool { @@ -267,6 +270,9 @@ enum ChatListNodeEntry: Comparable, Identifiable { if lhs.revealed != rhs.revealed { return false } + if lhs.hasNewStories != rhs.hasNewStories { + return false + } return true } } @@ -632,7 +638,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState autoremoveTimeout: entry.autoremoveTimeout, forumTopicData: entry.forumTopicData, topForumTopicItems: entry.topForumTopicItems, - revealed: threadId == 1 && (state.hiddenItemShouldBeTemporaryRevealed || state.editing) + revealed: threadId == 1 && (state.hiddenItemShouldBeTemporaryRevealed || state.editing), + hasNewStories: state.peersWithNewStories.contains(entry.renderedPeer.peerId) )) if let threadInfo, threadInfo.isHidden { @@ -681,7 +688,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState autoremoveTimeout: nil, forumTopicData: nil, topForumTopicItems: [], - revealed: false + revealed: false, + hasNewStories: false ))) if foundPinningIndex != 0 { foundPinningIndex -= 1 @@ -711,7 +719,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState autoremoveTimeout: nil, forumTopicData: nil, topForumTopicItems: [], - revealed: false + revealed: false, + hasNewStories: false ))) } else { if !filteredAdditionalItemEntries.isEmpty { @@ -761,7 +770,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState autoremoveTimeout: item.item.autoremoveTimeout, forumTopicData: item.item.forumTopicData, topForumTopicItems: item.item.topForumTopicItems, - revealed: state.hiddenItemShouldBeTemporaryRevealed || state.editing + revealed: state.hiddenItemShouldBeTemporaryRevealed || state.editing, + hasNewStories: false ))) if pinningIndex != 0 { pinningIndex -= 1 diff --git a/submodules/GalleryData/Sources/GalleryData.swift b/submodules/GalleryData/Sources/GalleryData.swift index a429a45efd..3d45808e8f 100644 --- a/submodules/GalleryData/Sources/GalleryData.swift +++ b/submodules/GalleryData/Sources/GalleryData.swift @@ -270,21 +270,6 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati openChatLocationContextHolder = Atomic(value: nil) } - if context.sharedContext.immediateExperimentalUISettings.storiesExperiment { - return .story(StoryChatContent.messages( - context: context, - messageId: message.id - ) - |> take(1) - |> deliverOnMainQueue - |> map { initialContent in - return StoryContainerScreen( - context: context, - initialContent: initialContent - ) - }) - } - return .gallery(startState |> deliverOnMainQueue |> map { startState in diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 8f35db5067..aeca6ea9e9 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -95,6 +95,7 @@ public final class HashtagSearchController: TelegramBaseController { }, openPremiumIntro: { }, openChatFolderUpdates: { }, hideChatFolderUpdates: { + }, openStories: { _ in }) let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil) diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index d38c8efc06..1a24b6e5f1 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -513,7 +513,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.errorTextNode.textAlignment = .center self.errorTextNode.isHidden = true - self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false)) + self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false, photo: true, metadata: false)) super.init() diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 1ea0d48d3b..80e7983466 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -1325,7 +1325,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { return } if groupId == AnyHashable("popular") { - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let presentationData = strongSelf.presentationData let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) var items: [ActionSheetItem] = [] let context = strongSelf.context @@ -1601,7 +1601,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.didAnimateIn = true - if !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion { + if !self.presentationData.reduceMotion { for i in 0 ..< self.items.count { guard let itemNode = self.visibleItemNodes[i] else { continue @@ -2053,7 +2053,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { standaloneReactionAnimation.animateReactionSelection( context: strongSelf.context, - theme: strongSelf.context.sharedContext.currentPresentationData.with({ $0 }).theme, + theme: strongSelf.presentationData.theme, animationCache: strongSelf.animationCache, reaction: itemNode.item, avatarPeers: [], diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index c353ba6286..fb1a19df2d 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -223,6 +223,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: { + }, openStories: { _ in }) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) @@ -287,7 +288,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index a7ff579fdb..386107f9e0 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -857,6 +857,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: { + }, openStories: { _ in }) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) @@ -920,7 +921,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index dbd2386ad8..28442cc489 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -371,6 +371,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: { + }, openStories: { _ in }) func makeChatListItem( @@ -433,7 +434,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 4b92fdfbe0..b70cc30a1e 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -14,6 +14,7 @@ public enum Api { public enum stats {} public enum stickers {} public enum storage {} + public enum stories {} public enum updates {} public enum upload {} public enum users {} @@ -33,6 +34,7 @@ public enum Api { public enum photos {} public enum stats {} public enum stickers {} + public enum stories {} public enum updates {} public enum upload {} public enum users {} @@ -59,7 +61,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-247016673] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypePM($0) } dict[2104224014] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypeSameBotPM($0) } dict[-1392388579] = { return Api.Authorization.parse_authorization($0) } - dict[-1896171181] = { return Api.AutoDownloadSettings.parse_autoDownloadSettings($0) } + dict[-1163561432] = { return Api.AutoDownloadSettings.parse_autoDownloadSettings($0) } dict[-2124403385] = { return Api.AutoSaveException.parse_autoSaveException($0) } dict[-934791986] = { return Api.AutoSaveSettings.parse_autoSaveSettings($0) } dict[-1065882623] = { return Api.AvailableReaction.parse_availableReaction($0) } @@ -780,6 +782,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1087454222] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) } dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) } dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) } + dict[-1230510430] = { return Api.StoryItem.parse_storyItem($0) } + dict[-2020380585] = { return Api.StoryItem.parse_storyItemDeleted($0) } dict[1964978502] = { return Api.TextWithEntities.parse_textWithEntities($0) } dict[-1609668650] = { return Api.Theme.parse_theme($0) } dict[-94849324] = { return Api.ThemeSettings.parse_themeSettings($0) } @@ -894,6 +898,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) } dict[834816008] = { return Api.Update.parse_updateStickerSets($0) } dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) } + dict[1727715253] = { return Api.Update.parse_updateStories($0) } dict[-2112423005] = { return Api.Update.parse_updateTheme($0) } dict[8703322] = { return Api.Update.parse_updateTranscribedAudio($0) } dict[542282808] = { return Api.Update.parse_updateUser($0) } @@ -916,7 +921,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[-1885878744] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } - dict[-1813324973] = { return Api.UserFull.parse_userFull($0) } + dict[1340198022] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) } dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) } @@ -925,6 +930,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[9203775] = { return Api.UserStatus.parse_userStatusOffline($0) } dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) } dict[-496024847] = { return Api.UserStatus.parse_userStatusRecently($0) } + dict[333268946] = { return Api.UserStories.parse_userStories($0) } + dict[-47503192] = { return Api.UserStories.parse_userStoriesShort($0) } dict[-1274595769] = { return Api.Username.parse_username($0) } dict[-567037804] = { return Api.VideoSize.parse_videoSize($0) } dict[-128171716] = { return Api.VideoSize.parse_videoSizeEmojiMarkup($0) } @@ -1141,6 +1148,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[172975040] = { return Api.storage.FileType.parse_filePng($0) } dict[-1432995067] = { return Api.storage.FileType.parse_fileUnknown($0) } dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) } + dict[1214632796] = { return Api.stories.AllStories.parse_allStories($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } @@ -1692,6 +1700,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.StickerSetCovered: _1.serialize(buffer, boxed) + case let _1 as Api.StoryItem: + _1.serialize(buffer, boxed) case let _1 as Api.TextWithEntities: _1.serialize(buffer, boxed) case let _1 as Api.Theme: @@ -1718,6 +1728,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.UserStatus: _1.serialize(buffer, boxed) + case let _1 as Api.UserStories: + _1.serialize(buffer, boxed) case let _1 as Api.Username: _1.serialize(buffer, boxed) case let _1 as Api.VideoSize: @@ -1994,6 +2006,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.storage.FileType: _1.serialize(buffer, boxed) + case let _1 as Api.stories.AllStories: + _1.serialize(buffer, boxed) case let _1 as Api.updates.ChannelDifference: _1.serialize(buffer, boxed) case let _1 as Api.updates.Difference: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index fd586293be..6a69f9c7fd 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -508,27 +508,29 @@ public extension Api { } public extension Api { enum AutoDownloadSettings: TypeConstructorDescription { - case autoDownloadSettings(flags: Int32, photoSizeMax: Int32, videoSizeMax: Int64, fileSizeMax: Int64, videoUploadMaxbitrate: Int32) + case autoDownloadSettings(flags: Int32, photoSizeMax: Int32, videoSizeMax: Int64, fileSizeMax: Int64, videoUploadMaxbitrate: Int32, smallQueueActiveOperationsMax: Int32, largeQueueActiveOperationsMax: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .autoDownloadSettings(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax, let videoUploadMaxbitrate): + case .autoDownloadSettings(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax, let videoUploadMaxbitrate, let smallQueueActiveOperationsMax, let largeQueueActiveOperationsMax): if boxed { - buffer.appendInt32(-1896171181) + buffer.appendInt32(-1163561432) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(photoSizeMax, buffer: buffer, boxed: false) serializeInt64(videoSizeMax, buffer: buffer, boxed: false) serializeInt64(fileSizeMax, buffer: buffer, boxed: false) serializeInt32(videoUploadMaxbitrate, buffer: buffer, boxed: false) + serializeInt32(smallQueueActiveOperationsMax, buffer: buffer, boxed: false) + serializeInt32(largeQueueActiveOperationsMax, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .autoDownloadSettings(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax, let videoUploadMaxbitrate): - return ("autoDownloadSettings", [("flags", flags as Any), ("photoSizeMax", photoSizeMax as Any), ("videoSizeMax", videoSizeMax as Any), ("fileSizeMax", fileSizeMax as Any), ("videoUploadMaxbitrate", videoUploadMaxbitrate as Any)]) + case .autoDownloadSettings(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax, let videoUploadMaxbitrate, let smallQueueActiveOperationsMax, let largeQueueActiveOperationsMax): + return ("autoDownloadSettings", [("flags", flags as Any), ("photoSizeMax", photoSizeMax as Any), ("videoSizeMax", videoSizeMax as Any), ("fileSizeMax", fileSizeMax as Any), ("videoUploadMaxbitrate", videoUploadMaxbitrate as Any), ("smallQueueActiveOperationsMax", smallQueueActiveOperationsMax as Any), ("largeQueueActiveOperationsMax", largeQueueActiveOperationsMax as Any)]) } } @@ -543,13 +545,19 @@ public extension Api { _4 = reader.readInt64() var _5: Int32? _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.AutoDownloadSettings.autoDownloadSettings(flags: _1!, photoSizeMax: _2!, videoSizeMax: _3!, fileSizeMax: _4!, videoUploadMaxbitrate: _5!) + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.AutoDownloadSettings.autoDownloadSettings(flags: _1!, photoSizeMax: _2!, videoSizeMax: _3!, fileSizeMax: _4!, videoUploadMaxbitrate: _5!, smallQueueActiveOperationsMax: _6!, largeQueueActiveOperationsMax: _7!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api21.swift b/submodules/TelegramApi/Sources/Api21.swift index bce9ec2791..a719e0f69a 100644 --- a/submodules/TelegramApi/Sources/Api21.swift +++ b/submodules/TelegramApi/Sources/Api21.swift @@ -326,6 +326,72 @@ public extension Api { } } +public extension Api { + indirect enum StoryItem: TypeConstructorDescription { + case storyItem(id: Int64, date: Int32, media: Api.MessageMedia) + case storyItemDeleted(id: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyItem(let id, let date, let media): + if boxed { + buffer.appendInt32(-1230510430) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + media.serialize(buffer, true) + break + case .storyItemDeleted(let id): + if boxed { + buffer.appendInt32(-2020380585) + } + serializeInt64(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyItem(let id, let date, let media): + return ("storyItem", [("id", id as Any), ("date", date as Any), ("media", media as Any)]) + case .storyItemDeleted(let id): + return ("storyItemDeleted", [("id", id as Any)]) + } + } + + public static func parse_storyItem(_ reader: BufferReader) -> StoryItem? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.MessageMedia? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.MessageMedia + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.StoryItem.storyItem(id: _1!, date: _2!, media: _3!) + } + else { + return nil + } + } + public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.StoryItem.storyItemDeleted(id: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum TextWithEntities: TypeConstructorDescription { case textWithEntities(text: String, entities: [Api.MessageEntity]) @@ -823,6 +889,7 @@ public extension Api { case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity]) case updateStickerSets(flags: Int32) case updateStickerSetsOrder(flags: Int32, order: [Int64]) + case updateStories(stories: Api.UserStories) case updateTheme(theme: Api.Theme) case updateTranscribedAudio(flags: Int32, peer: Api.Peer, msgId: Int32, transcriptionId: Int64, text: String) case updateUser(userId: Int64) @@ -1715,6 +1782,12 @@ public extension Api { serializeInt64(item, buffer: buffer, boxed: false) } break + case .updateStories(let stories): + if boxed { + buffer.appendInt32(1727715253) + } + stories.serialize(buffer, true) + break case .updateTheme(let theme): if boxed { buffer.appendInt32(-2112423005) @@ -1999,6 +2072,8 @@ public extension Api { return ("updateStickerSets", [("flags", flags as Any)]) case .updateStickerSetsOrder(let flags, let order): return ("updateStickerSetsOrder", [("flags", flags as Any), ("order", order as Any)]) + case .updateStories(let stories): + return ("updateStories", [("stories", stories as Any)]) case .updateTheme(let theme): return ("updateTheme", [("theme", theme as Any)]) case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text): @@ -3772,6 +3847,19 @@ public extension Api { return nil } } + public static func parse_updateStories(_ reader: BufferReader) -> Update? { + var _1: Api.UserStories? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.UserStories + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateStories(stories: _1!) + } + else { + return nil + } + } public static func parse_updateTheme(_ reader: BufferReader) -> Update? { var _1: Api.Theme? if let signature = reader.readInt32() { diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 321570b40f..5f42bc1a0d 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -586,13 +586,13 @@ public extension Api { } public extension Api { enum UserFull: TypeConstructorDescription { - case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, premiumGifts: [Api.PremiumGiftOption]?, wallpaper: Api.WallPaper?) + case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, premiumGifts: [Api.PremiumGiftOption]?, wallpaper: Api.WallPaper?, stories: Api.UserStories?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts, let wallpaper): + case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts, let wallpaper, let stories): if boxed { - buffer.appendInt32(-1813324973) + buffer.appendInt32(1340198022) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -617,14 +617,15 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags) & Int(1 << 24) != 0 {wallpaper!.serialize(buffer, true)} + if Int(flags) & Int(1 << 25) != 0 {stories!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts, let wallpaper): - return ("userFull", [("flags", flags as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("themeEmoticon", themeEmoticon as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("premiumGifts", premiumGifts as Any), ("wallpaper", wallpaper as Any)]) + case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts, let wallpaper, let stories): + return ("userFull", [("flags", flags as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("themeEmoticon", themeEmoticon as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("premiumGifts", premiumGifts as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any)]) } } @@ -687,6 +688,10 @@ public extension Api { if Int(_1!) & Int(1 << 24) != 0 {if let signature = reader.readInt32() { _19 = Api.parse(reader, signature: signature) as? Api.WallPaper } } + var _20: Api.UserStories? + if Int(_1!) & Int(1 << 25) != 0 {if let signature = reader.readInt32() { + _20 = Api.parse(reader, signature: signature) as? Api.UserStories + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil @@ -706,8 +711,9 @@ public extension Api { let _c17 = (Int(_1!) & Int(1 << 18) == 0) || _17 != nil let _c18 = (Int(_1!) & Int(1 << 19) == 0) || _18 != nil let _c19 = (Int(_1!) & Int(1 << 24) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, fallbackPhoto: _7, notifySettings: _8!, botInfo: _9, pinnedMsgId: _10, commonChatsCount: _11!, folderId: _12, ttlPeriod: _13, themeEmoticon: _14, privateForwardName: _15, botGroupAdminRights: _16, botBroadcastAdminRights: _17, premiumGifts: _18, wallpaper: _19) + let _c20 = (Int(_1!) & Int(1 << 25) == 0) || _20 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 { + return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, fallbackPhoto: _7, notifySettings: _8!, botInfo: _9, pinnedMsgId: _10, commonChatsCount: _11!, folderId: _12, ttlPeriod: _13, themeEmoticon: _14, privateForwardName: _15, botGroupAdminRights: _16, botBroadcastAdminRights: _17, premiumGifts: _18, wallpaper: _19, stories: _20) } else { return nil @@ -880,6 +886,86 @@ public extension Api { } } +public extension Api { + enum UserStories: TypeConstructorDescription { + case userStories(userId: Int64, stories: [Api.StoryItem]) + case userStoriesShort(userId: Int64, stories: [Api.StoryItem], totalCount: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .userStories(let userId, let stories): + if boxed { + buffer.appendInt32(333268946) + } + serializeInt64(userId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + break + case .userStoriesShort(let userId, let stories, let totalCount): + if boxed { + buffer.appendInt32(-47503192) + } + serializeInt64(userId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + serializeInt32(totalCount, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .userStories(let userId, let stories): + return ("userStories", [("userId", userId as Any), ("stories", stories as Any)]) + case .userStoriesShort(let userId, let stories, let totalCount): + return ("userStoriesShort", [("userId", userId as Any), ("stories", stories as Any), ("totalCount", totalCount as Any)]) + } + } + + public static func parse_userStories(_ reader: BufferReader) -> UserStories? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StoryItem]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.UserStories.userStories(userId: _1!, stories: _2!) + } + else { + return nil + } + } + public static func parse_userStoriesShort(_ reader: BufferReader) -> UserStories? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StoryItem]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.UserStories.userStoriesShort(userId: _1!, stories: _2!, totalCount: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum Username: TypeConstructorDescription { case username(flags: Int32, username: String) @@ -1356,191 +1442,3 @@ public extension Api { } } -public extension Api { - enum WebPage: TypeConstructorDescription { - case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) - case webPageEmpty(id: Int64) - case webPageNotModified(flags: Int32, cachedPageViews: Int32?) - case webPagePending(id: Int64, date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - if boxed { - buffer.appendInt32(-392411726) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - serializeString(displayUrl, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} - if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} - if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes!.count)) - for item in attributes! { - item.serialize(buffer, true) - }} - break - case .webPageEmpty(let id): - if boxed { - buffer.appendInt32(-350980120) - } - serializeInt64(id, buffer: buffer, boxed: false) - break - case .webPageNotModified(let flags, let cachedPageViews): - if boxed { - buffer.appendInt32(1930545681) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} - break - case .webPagePending(let id, let date): - if boxed { - buffer.appendInt32(-981018084) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) - case .webPageEmpty(let id): - return ("webPageEmpty", [("id", id as Any)]) - case .webPageNotModified(let flags, let cachedPageViews): - return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) - case .webPagePending(let id, let date): - return ("webPagePending", [("id", id as Any), ("date", date as Any)]) - } - } - - public static func parse_webPage(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } - var _7: String? - if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } - var _8: String? - if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } - var _9: String? - if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } - var _10: Api.Photo? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _11: String? - if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } - var _12: String? - if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } - var _13: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } - var _14: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } - var _15: Int32? - if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } - var _16: String? - if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } - var _17: Api.Document? - if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.Document - } } - var _18: Api.Page? - if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { - _18 = Api.parse(reader, signature: signature) as? Api.Page - } } - var _19: [Api.WebPageAttribute]? - if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { - _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil - let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) - } - else { - return nil - } - } - public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.WebPage.webPageEmpty(id: _1!) - } - else { - return nil - } - } - public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) - } - else { - return nil - } - } - public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.WebPage.webPagePending(id: _1!, date: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index f8ee7aa4dd..f255fdec59 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -1,3 +1,191 @@ +public extension Api { + enum WebPage: TypeConstructorDescription { + case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) + case webPageEmpty(id: Int64) + case webPageNotModified(flags: Int32, cachedPageViews: Int32?) + case webPagePending(id: Int64, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): + if boxed { + buffer.appendInt32(-392411726) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeString(displayUrl, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} + if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes!.count)) + for item in attributes! { + item.serialize(buffer, true) + }} + break + case .webPageEmpty(let id): + if boxed { + buffer.appendInt32(-350980120) + } + serializeInt64(id, buffer: buffer, boxed: false) + break + case .webPageNotModified(let flags, let cachedPageViews): + if boxed { + buffer.appendInt32(1930545681) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} + break + case .webPagePending(let id, let date): + if boxed { + buffer.appendInt32(-981018084) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): + return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) + case .webPageEmpty(let id): + return ("webPageEmpty", [("id", id as Any)]) + case .webPageNotModified(let flags, let cachedPageViews): + return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) + case .webPagePending(let id, let date): + return ("webPagePending", [("id", id as Any), ("date", date as Any)]) + } + } + + public static func parse_webPage(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + var _7: String? + if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } + var _8: String? + if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } + var _9: String? + if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } + var _10: Api.Photo? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _11: String? + if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } + var _12: String? + if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } + var _13: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } + var _14: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } + var _15: Int32? + if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } + var _16: String? + if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } + var _17: Api.Document? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _18: Api.Page? + if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.Page + } } + var _19: [Api.WebPageAttribute]? + if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { + _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) + } + else { + return nil + } + } + public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.WebPage.webPageEmpty(id: _1!) + } + else { + return nil + } + } + public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) + } + else { + return nil + } + } + public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.WebPage.webPagePending(id: _1!, date: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum WebPageAttribute: TypeConstructorDescription { case webPageAttributeTheme(flags: Int32, documents: [Api.Document]?, settings: Api.ThemeSettings?) @@ -1144,193 +1332,3 @@ public extension Api.account { } } -public extension Api.account { - enum WallPapers: TypeConstructorDescription { - case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) - case wallPapersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .wallPapers(let hash, let wallpapers): - if boxed { - buffer.appendInt32(-842824308) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(wallpapers.count)) - for item in wallpapers { - item.serialize(buffer, true) - } - break - case .wallPapersNotModified: - if boxed { - buffer.appendInt32(471437699) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .wallPapers(let hash, let wallpapers): - return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) - case .wallPapersNotModified: - return ("wallPapersNotModified", []) - } - } - - public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.WallPaper]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) - } - else { - return nil - } - } - public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { - return Api.account.WallPapers.wallPapersNotModified - } - - } -} -public extension Api.account { - enum WebAuthorizations: TypeConstructorDescription { - case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webAuthorizations(let authorizations, let users): - if boxed { - buffer.appendInt32(-313079300) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(authorizations.count)) - for item in authorizations { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webAuthorizations(let authorizations, let users): - return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) - } - } - - public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { - var _1: [Api.WebAuthorization]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.auth { - enum Authorization: TypeConstructorDescription { - case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) - case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): - if boxed { - buffer.appendInt32(782418132) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(otherwiseReloginDays!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} - user.serialize(buffer, true) - break - case .authorizationSignUpRequired(let flags, let termsOfService): - if boxed { - buffer.appendInt32(1148485274) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {termsOfService!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): - return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("futureAuthToken", futureAuthToken as Any), ("user", user as Any)]) - case .authorizationSignUpRequired(let flags, let termsOfService): - return ("authorizationSignUpRequired", [("flags", flags as Any), ("termsOfService", termsOfService as Any)]) - } - } - - public static func parse_authorization(_ reader: BufferReader) -> Authorization? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Buffer? - if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) } - var _5: Api.User? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.User - } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) - } - else { - return nil - } - } - public static func parse_authorizationSignUpRequired(_ reader: BufferReader) -> Authorization? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.help.TermsOfService? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 1b87bd7dd4..b154cef686 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,3 +1,193 @@ +public extension Api.account { + enum WallPapers: TypeConstructorDescription { + case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) + case wallPapersNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .wallPapers(let hash, let wallpapers): + if boxed { + buffer.appendInt32(-842824308) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(wallpapers.count)) + for item in wallpapers { + item.serialize(buffer, true) + } + break + case .wallPapersNotModified: + if boxed { + buffer.appendInt32(471437699) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .wallPapers(let hash, let wallpapers): + return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) + case .wallPapersNotModified: + return ("wallPapersNotModified", []) + } + } + + public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.WallPaper]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) + } + else { + return nil + } + } + public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { + return Api.account.WallPapers.wallPapersNotModified + } + + } +} +public extension Api.account { + enum WebAuthorizations: TypeConstructorDescription { + case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webAuthorizations(let authorizations, let users): + if boxed { + buffer.appendInt32(-313079300) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(authorizations.count)) + for item in authorizations { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webAuthorizations(let authorizations, let users): + return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) + } + } + + public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { + var _1: [Api.WebAuthorization]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.auth { + enum Authorization: TypeConstructorDescription { + case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) + case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + if boxed { + buffer.appendInt32(782418132) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(otherwiseReloginDays!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} + user.serialize(buffer, true) + break + case .authorizationSignUpRequired(let flags, let termsOfService): + if boxed { + buffer.appendInt32(1148485274) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {termsOfService!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("futureAuthToken", futureAuthToken as Any), ("user", user as Any)]) + case .authorizationSignUpRequired(let flags, let termsOfService): + return ("authorizationSignUpRequired", [("flags", flags as Any), ("termsOfService", termsOfService as Any)]) + } + } + + public static func parse_authorization(_ reader: BufferReader) -> Authorization? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) } + var _5: Api.User? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.User + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) + } + else { + return nil + } + } + public static func parse_authorizationSignUpRequired(_ reader: BufferReader) -> Authorization? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.help.TermsOfService? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) + } + else { + return nil + } + } + + } +} public extension Api.auth { enum CodeType: TypeConstructorDescription { case codeTypeCall @@ -832,305 +1022,3 @@ public extension Api.channels { } } -public extension Api.channels { - enum SendAsPeers: TypeConstructorDescription { - case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sendAsPeers(let peers, let chats, let users): - if boxed { - buffer.appendInt32(-191450938) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sendAsPeers(let peers, let chats, let users): - return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { - var _1: [Api.SendAsPeer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ChatlistInvite: TypeConstructorDescription { - case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): - if boxed { - buffer.appendInt32(500007837) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): - if boxed { - buffer.appendInt32(-91752871) - } - serializeInt32(filterId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingPeers.count)) - for item in missingPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(alreadyPeers.count)) - for item in alreadyPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): - return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): - return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: [Api.Peer]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) - } - else { - return nil - } - } - public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Peer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _3: [Api.Peer]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ChatlistUpdates: TypeConstructorDescription { - case chatlistUpdates(missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .chatlistUpdates(let missingPeers, let chats, let users): - if boxed { - buffer.appendInt32(-1816295539) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingPeers.count)) - for item in missingPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .chatlistUpdates(let missingPeers, let chats, let users): - return ("chatlistUpdates", [("missingPeers", missingPeers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_chatlistUpdates(_ reader: BufferReader) -> ChatlistUpdates? { - var _1: [Api.Peer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ExportedChatlistInvite: TypeConstructorDescription { - case exportedChatlistInvite(filter: Api.DialogFilter, invite: Api.ExportedChatlistInvite) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedChatlistInvite(let filter, let invite): - if boxed { - buffer.appendInt32(283567014) - } - filter.serialize(buffer, true) - invite.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedChatlistInvite(let filter, let invite): - return ("exportedChatlistInvite", [("filter", filter as Any), ("invite", invite as Any)]) - } - } - - public static func parse_exportedChatlistInvite(_ reader: BufferReader) -> ExportedChatlistInvite? { - var _1: Api.DialogFilter? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.DialogFilter - } - var _2: Api.ExportedChatlistInvite? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index ae63ac4381..51ed09f604 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,305 @@ +public extension Api.channels { + enum SendAsPeers: TypeConstructorDescription { + case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sendAsPeers(let peers, let chats, let users): + if boxed { + buffer.appendInt32(-191450938) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sendAsPeers(let peers, let chats, let users): + return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { + var _1: [Api.SendAsPeer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.chatlists { + enum ChatlistInvite: TypeConstructorDescription { + case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + if boxed { + buffer.appendInt32(500007837) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + if boxed { + buffer.appendInt32(-91752871) + } + serializeInt32(filterId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(missingPeers.count)) + for item in missingPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(alreadyPeers.count)) + for item in alreadyPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: [Api.Peer]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) + } + else { + return nil + } + } + public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Peer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _3: [Api.Peer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + + } +} +public extension Api.chatlists { + enum ChatlistUpdates: TypeConstructorDescription { + case chatlistUpdates(missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatlistUpdates(let missingPeers, let chats, let users): + if boxed { + buffer.appendInt32(-1816295539) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(missingPeers.count)) + for item in missingPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatlistUpdates(let missingPeers, let chats, let users): + return ("chatlistUpdates", [("missingPeers", missingPeers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_chatlistUpdates(_ reader: BufferReader) -> ChatlistUpdates? { + var _1: [Api.Peer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.chatlists { + enum ExportedChatlistInvite: TypeConstructorDescription { + case exportedChatlistInvite(filter: Api.DialogFilter, invite: Api.ExportedChatlistInvite) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedChatlistInvite(let filter, let invite): + if boxed { + buffer.appendInt32(283567014) + } + filter.serialize(buffer, true) + invite.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedChatlistInvite(let filter, let invite): + return ("exportedChatlistInvite", [("filter", filter as Any), ("invite", invite as Any)]) + } + } + + public static func parse_exportedChatlistInvite(_ reader: BufferReader) -> ExportedChatlistInvite? { + var _1: Api.DialogFilter? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.DialogFilter + } + var _2: Api.ExportedChatlistInvite? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatlistInvite + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) + } + else { + return nil + } + } + + } +} public extension Api.chatlists { enum ExportedInvites: TypeConstructorDescription { case exportedInvites(invites: [Api.ExportedChatlistInvite], chats: [Api.Chat], users: [Api.User]) @@ -1176,265 +1478,3 @@ public extension Api.help { } } -public extension Api.help { - enum RecentMeUrls: TypeConstructorDescription { - case recentMeUrls(urls: [Api.RecentMeUrl], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .recentMeUrls(let urls, let chats, let users): - if boxed { - buffer.appendInt32(235081943) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(urls.count)) - for item in urls { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .recentMeUrls(let urls, let chats, let users): - return ("recentMeUrls", [("urls", urls as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_recentMeUrls(_ reader: BufferReader) -> RecentMeUrls? { - var _1: [Api.RecentMeUrl]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RecentMeUrl.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.help { - enum Support: TypeConstructorDescription { - case support(phoneNumber: String, user: Api.User) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .support(let phoneNumber, let user): - if boxed { - buffer.appendInt32(398898678) - } - serializeString(phoneNumber, buffer: buffer, boxed: false) - user.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .support(let phoneNumber, let user): - return ("support", [("phoneNumber", phoneNumber as Any), ("user", user as Any)]) - } - } - - public static func parse_support(_ reader: BufferReader) -> Support? { - var _1: String? - _1 = parseString(reader) - var _2: Api.User? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.User - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.Support.support(phoneNumber: _1!, user: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.help { - enum SupportName: TypeConstructorDescription { - case supportName(name: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .supportName(let name): - if boxed { - buffer.appendInt32(-1945767479) - } - serializeString(name, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .supportName(let name): - return ("supportName", [("name", name as Any)]) - } - } - - public static func parse_supportName(_ reader: BufferReader) -> SupportName? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.help.SupportName.supportName(name: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.help { - enum TermsOfService: TypeConstructorDescription { - case termsOfService(flags: Int32, id: Api.DataJSON, text: String, entities: [Api.MessageEntity], minAgeConfirm: Int32?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): - if boxed { - buffer.appendInt32(2013922064) - } - serializeInt32(flags, buffer: buffer, boxed: false) - id.serialize(buffer, true) - serializeString(text, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities.count)) - for item in entities { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(minAgeConfirm!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): - return ("termsOfService", [("flags", flags as Any), ("id", id as Any), ("text", text as Any), ("entities", entities as Any), ("minAgeConfirm", minAgeConfirm as Any)]) - } - } - - public static func parse_termsOfService(_ reader: BufferReader) -> TermsOfService? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - var _3: String? - _3 = parseString(reader) - var _4: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } - var _5: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) - } - else { - return nil - } - } - - } -} -public extension Api.help { - enum TermsOfServiceUpdate: TypeConstructorDescription { - case termsOfServiceUpdate(expires: Int32, termsOfService: Api.help.TermsOfService) - case termsOfServiceUpdateEmpty(expires: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .termsOfServiceUpdate(let expires, let termsOfService): - if boxed { - buffer.appendInt32(686618977) - } - serializeInt32(expires, buffer: buffer, boxed: false) - termsOfService.serialize(buffer, true) - break - case .termsOfServiceUpdateEmpty(let expires): - if boxed { - buffer.appendInt32(-483352705) - } - serializeInt32(expires, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .termsOfServiceUpdate(let expires, let termsOfService): - return ("termsOfServiceUpdate", [("expires", expires as Any), ("termsOfService", termsOfService as Any)]) - case .termsOfServiceUpdateEmpty(let expires): - return ("termsOfServiceUpdateEmpty", [("expires", expires as Any)]) - } - } - - public static func parse_termsOfServiceUpdate(_ reader: BufferReader) -> TermsOfServiceUpdate? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.help.TermsOfService? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) - } - else { - return nil - } - } - public static func parse_termsOfServiceUpdateEmpty(_ reader: BufferReader) -> TermsOfServiceUpdate? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 1bbb94bdce..3934b279de 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,3 +1,265 @@ +public extension Api.help { + enum RecentMeUrls: TypeConstructorDescription { + case recentMeUrls(urls: [Api.RecentMeUrl], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .recentMeUrls(let urls, let chats, let users): + if boxed { + buffer.appendInt32(235081943) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(urls.count)) + for item in urls { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .recentMeUrls(let urls, let chats, let users): + return ("recentMeUrls", [("urls", urls as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_recentMeUrls(_ reader: BufferReader) -> RecentMeUrls? { + var _1: [Api.RecentMeUrl]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RecentMeUrl.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.help { + enum Support: TypeConstructorDescription { + case support(phoneNumber: String, user: Api.User) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .support(let phoneNumber, let user): + if boxed { + buffer.appendInt32(398898678) + } + serializeString(phoneNumber, buffer: buffer, boxed: false) + user.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .support(let phoneNumber, let user): + return ("support", [("phoneNumber", phoneNumber as Any), ("user", user as Any)]) + } + } + + public static func parse_support(_ reader: BufferReader) -> Support? { + var _1: String? + _1 = parseString(reader) + var _2: Api.User? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.User + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.help.Support.support(phoneNumber: _1!, user: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.help { + enum SupportName: TypeConstructorDescription { + case supportName(name: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .supportName(let name): + if boxed { + buffer.appendInt32(-1945767479) + } + serializeString(name, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .supportName(let name): + return ("supportName", [("name", name as Any)]) + } + } + + public static func parse_supportName(_ reader: BufferReader) -> SupportName? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.help.SupportName.supportName(name: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.help { + enum TermsOfService: TypeConstructorDescription { + case termsOfService(flags: Int32, id: Api.DataJSON, text: String, entities: [Api.MessageEntity], minAgeConfirm: Int32?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): + if boxed { + buffer.appendInt32(2013922064) + } + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + serializeString(text, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(minAgeConfirm!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .termsOfService(let flags, let id, let text, let entities, let minAgeConfirm): + return ("termsOfService", [("flags", flags as Any), ("id", id as Any), ("text", text as Any), ("entities", entities as Any), ("minAgeConfirm", minAgeConfirm as Any)]) + } + } + + public static func parse_termsOfService(_ reader: BufferReader) -> TermsOfService? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + var _3: String? + _3 = parseString(reader) + var _4: [Api.MessageEntity]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } + var _5: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) + } + else { + return nil + } + } + + } +} +public extension Api.help { + enum TermsOfServiceUpdate: TypeConstructorDescription { + case termsOfServiceUpdate(expires: Int32, termsOfService: Api.help.TermsOfService) + case termsOfServiceUpdateEmpty(expires: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .termsOfServiceUpdate(let expires, let termsOfService): + if boxed { + buffer.appendInt32(686618977) + } + serializeInt32(expires, buffer: buffer, boxed: false) + termsOfService.serialize(buffer, true) + break + case .termsOfServiceUpdateEmpty(let expires): + if boxed { + buffer.appendInt32(-483352705) + } + serializeInt32(expires, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .termsOfServiceUpdate(let expires, let termsOfService): + return ("termsOfServiceUpdate", [("expires", expires as Any), ("termsOfService", termsOfService as Any)]) + case .termsOfServiceUpdateEmpty(let expires): + return ("termsOfServiceUpdateEmpty", [("expires", expires as Any)]) + } + } + + public static func parse_termsOfServiceUpdate(_ reader: BufferReader) -> TermsOfServiceUpdate? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.help.TermsOfService? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.help.TermsOfService + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) + } + else { + return nil + } + } + public static func parse_termsOfServiceUpdateEmpty(_ reader: BufferReader) -> TermsOfServiceUpdate? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) + } + else { + return nil + } + } + + } +} public extension Api.help { enum UserInfo: TypeConstructorDescription { case userInfo(message: String, entities: [Api.MessageEntity], author: String, date: Int32) @@ -1168,213 +1430,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum ExportedChatInvite: TypeConstructorDescription { - case exportedChatInvite(invite: Api.ExportedChatInvite, users: [Api.User]) - case exportedChatInviteReplaced(invite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedChatInvite(let invite, let users): - if boxed { - buffer.appendInt32(410107472) - } - invite.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .exportedChatInviteReplaced(let invite, let newInvite, let users): - if boxed { - buffer.appendInt32(572915951) - } - invite.serialize(buffer, true) - newInvite.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedChatInvite(let invite, let users): - return ("exportedChatInvite", [("invite", invite as Any), ("users", users as Any)]) - case .exportedChatInviteReplaced(let invite, let newInvite, let users): - return ("exportedChatInviteReplaced", [("invite", invite as Any), ("newInvite", newInvite as Any), ("users", users as Any)]) - } - } - - public static func parse_exportedChatInvite(_ reader: BufferReader) -> ExportedChatInvite? { - var _1: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) - } - else { - return nil - } - } - public static func parse_exportedChatInviteReplaced(_ reader: BufferReader) -> ExportedChatInvite? { - var _1: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - var _2: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum ExportedChatInvites: TypeConstructorDescription { - case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedChatInvites(let count, let invites, let users): - if boxed { - buffer.appendInt32(-1111085620) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(invites.count)) - for item in invites { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedChatInvites(let count, let invites, let users): - return ("exportedChatInvites", [("count", count as Any), ("invites", invites as Any), ("users", users as Any)]) - } - } - - public static func parse_exportedChatInvites(_ reader: BufferReader) -> ExportedChatInvites? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.ExportedChatInvite]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatInvite.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum FavedStickers: TypeConstructorDescription { - case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) - case favedStickersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .favedStickers(let hash, let packs, let stickers): - if boxed { - buffer.appendInt32(750063767) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { - item.serialize(buffer, true) - } - break - case .favedStickersNotModified: - if boxed { - buffer.appendInt32(-1634752813) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .favedStickers(let hash, let packs, let stickers): - return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) - case .favedStickersNotModified: - return ("favedStickersNotModified", []) - } - } - - public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.Document]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) - } - else { - return nil - } - } - public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { - return Api.messages.FavedStickers.favedStickersNotModified - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index bf6b66668f..43e20004de 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,3 +1,213 @@ +public extension Api.messages { + enum ExportedChatInvite: TypeConstructorDescription { + case exportedChatInvite(invite: Api.ExportedChatInvite, users: [Api.User]) + case exportedChatInviteReplaced(invite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedChatInvite(let invite, let users): + if boxed { + buffer.appendInt32(410107472) + } + invite.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .exportedChatInviteReplaced(let invite, let newInvite, let users): + if boxed { + buffer.appendInt32(572915951) + } + invite.serialize(buffer, true) + newInvite.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedChatInvite(let invite, let users): + return ("exportedChatInvite", [("invite", invite as Any), ("users", users as Any)]) + case .exportedChatInviteReplaced(let invite, let newInvite, let users): + return ("exportedChatInviteReplaced", [("invite", invite as Any), ("newInvite", newInvite as Any), ("users", users as Any)]) + } + } + + public static func parse_exportedChatInvite(_ reader: BufferReader) -> ExportedChatInvite? { + var _1: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) + } + else { + return nil + } + } + public static func parse_exportedChatInviteReplaced(_ reader: BufferReader) -> ExportedChatInvite? { + var _1: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _2: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum ExportedChatInvites: TypeConstructorDescription { + case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedChatInvites(let count, let invites, let users): + if boxed { + buffer.appendInt32(-1111085620) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(invites.count)) + for item in invites { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedChatInvites(let count, let invites, let users): + return ("exportedChatInvites", [("count", count as Any), ("invites", invites as Any), ("users", users as Any)]) + } + } + + public static func parse_exportedChatInvites(_ reader: BufferReader) -> ExportedChatInvites? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.ExportedChatInvite]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedChatInvite.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum FavedStickers: TypeConstructorDescription { + case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) + case favedStickersNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .favedStickers(let hash, let packs, let stickers): + if boxed { + buffer.appendInt32(750063767) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + break + case .favedStickersNotModified: + if boxed { + buffer.appendInt32(-1634752813) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .favedStickers(let hash, let packs, let stickers): + return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) + case .favedStickersNotModified: + return ("favedStickersNotModified", []) + } + } + + public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.Document]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) + } + else { + return nil + } + } + public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { + return Api.messages.FavedStickers.favedStickersNotModified + } + + } +} public extension Api.messages { enum FeaturedStickers: TypeConstructorDescription { case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64]) @@ -1316,227 +1526,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum SentEncryptedMessage: TypeConstructorDescription { - case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) - case sentEncryptedMessage(date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sentEncryptedFile(let date, let file): - if boxed { - buffer.appendInt32(-1802240206) - } - serializeInt32(date, buffer: buffer, boxed: false) - file.serialize(buffer, true) - break - case .sentEncryptedMessage(let date): - if boxed { - buffer.appendInt32(1443858741) - } - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sentEncryptedFile(let date, let file): - return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) - case .sentEncryptedMessage(let date): - return ("sentEncryptedMessage", [("date", date as Any)]) - } - } - - public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.EncryptedFile? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) - } - else { - return nil - } - } - public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum SponsoredMessages: TypeConstructorDescription { - case sponsoredMessages(flags: Int32, postsBetween: Int32?, messages: [Api.SponsoredMessage], chats: [Api.Chat], users: [Api.User]) - case sponsoredMessagesEmpty - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): - if boxed { - buffer.appendInt32(-907141753) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(postsBetween!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .sponsoredMessagesEmpty: - if boxed { - buffer.appendInt32(406407439) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): - return ("sponsoredMessages", [("flags", flags as Any), ("postsBetween", postsBetween as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .sponsoredMessagesEmpty: - return ("sponsoredMessagesEmpty", []) - } - } - - public static func parse_sponsoredMessages(_ reader: BufferReader) -> SponsoredMessages? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - var _3: [Api.SponsoredMessage]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SponsoredMessage.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, messages: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - public static func parse_sponsoredMessagesEmpty(_ reader: BufferReader) -> SponsoredMessages? { - return Api.messages.SponsoredMessages.sponsoredMessagesEmpty - } - - } -} -public extension Api.messages { - enum StickerSet: TypeConstructorDescription { - case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) - case stickerSetNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stickerSet(let set, let packs, let keywords, let documents): - if boxed { - buffer.appendInt32(1846886166) - } - set.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(keywords.count)) - for item in keywords { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { - item.serialize(buffer, true) - } - break - case .stickerSetNotModified: - if boxed { - buffer.appendInt32(-738646805) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stickerSet(let set, let packs, let keywords, let documents): - return ("stickerSet", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) - case .stickerSetNotModified: - return ("stickerSetNotModified", []) - } - } - - public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { - var _1: Api.StickerSet? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StickerSet - } - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.StickerKeyword]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) - } - var _4: [Api.Document]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) - } - else { - return nil - } - } - public static func parse_stickerSetNotModified(_ reader: BufferReader) -> StickerSet? { - return Api.messages.StickerSet.stickerSetNotModified - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index e1794c8206..dcca360e0e 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1,3 +1,227 @@ +public extension Api.messages { + enum SentEncryptedMessage: TypeConstructorDescription { + case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) + case sentEncryptedMessage(date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sentEncryptedFile(let date, let file): + if boxed { + buffer.appendInt32(-1802240206) + } + serializeInt32(date, buffer: buffer, boxed: false) + file.serialize(buffer, true) + break + case .sentEncryptedMessage(let date): + if boxed { + buffer.appendInt32(1443858741) + } + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sentEncryptedFile(let date, let file): + return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) + case .sentEncryptedMessage(let date): + return ("sentEncryptedMessage", [("date", date as Any)]) + } + } + + public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.EncryptedFile? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) + } + else { + return nil + } + } + public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum SponsoredMessages: TypeConstructorDescription { + case sponsoredMessages(flags: Int32, postsBetween: Int32?, messages: [Api.SponsoredMessage], chats: [Api.Chat], users: [Api.User]) + case sponsoredMessagesEmpty + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-907141753) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(postsBetween!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .sponsoredMessagesEmpty: + if boxed { + buffer.appendInt32(406407439) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sponsoredMessages(let flags, let postsBetween, let messages, let chats, let users): + return ("sponsoredMessages", [("flags", flags as Any), ("postsBetween", postsBetween as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .sponsoredMessagesEmpty: + return ("sponsoredMessagesEmpty", []) + } + } + + public static func parse_sponsoredMessages(_ reader: BufferReader) -> SponsoredMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: [Api.SponsoredMessage]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SponsoredMessage.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, messages: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + public static func parse_sponsoredMessagesEmpty(_ reader: BufferReader) -> SponsoredMessages? { + return Api.messages.SponsoredMessages.sponsoredMessagesEmpty + } + + } +} +public extension Api.messages { + enum StickerSet: TypeConstructorDescription { + case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) + case stickerSetNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stickerSet(let set, let packs, let keywords, let documents): + if boxed { + buffer.appendInt32(1846886166) + } + set.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(keywords.count)) + for item in keywords { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + item.serialize(buffer, true) + } + break + case .stickerSetNotModified: + if boxed { + buffer.appendInt32(-738646805) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stickerSet(let set, let packs, let keywords, let documents): + return ("stickerSet", [("set", set as Any), ("packs", packs as Any), ("keywords", keywords as Any), ("documents", documents as Any)]) + case .stickerSetNotModified: + return ("stickerSetNotModified", []) + } + } + + public static func parse_stickerSet(_ reader: BufferReader) -> StickerSet? { + var _1: Api.StickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StickerSet + } + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.StickerKeyword]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self) + } + var _4: [Api.Document]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) + } + else { + return nil + } + } + public static func parse_stickerSetNotModified(_ reader: BufferReader) -> StickerSet? { + return Api.messages.StickerSet.stickerSetNotModified + } + + } +} public extension Api.messages { enum StickerSetInstallResult: TypeConstructorDescription { case stickerSetInstallResultArchive(sets: [Api.StickerSetCovered]) @@ -1366,363 +1590,3 @@ public extension Api.stats { } } -public extension Api.stats { - enum MegagroupStats: TypeConstructorDescription { - case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): - if boxed { - buffer.appendInt32(-276825834) - } - period.serialize(buffer, true) - members.serialize(buffer, true) - messages.serialize(buffer, true) - viewers.serialize(buffer, true) - posters.serialize(buffer, true) - growthGraph.serialize(buffer, true) - membersGraph.serialize(buffer, true) - newMembersBySourceGraph.serialize(buffer, true) - languagesGraph.serialize(buffer, true) - messagesGraph.serialize(buffer, true) - actionsGraph.serialize(buffer, true) - topHoursGraph.serialize(buffer, true) - weekdaysGraph.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topPosters.count)) - for item in topPosters { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topAdmins.count)) - for item in topAdmins { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topInviters.count)) - for item in topInviters { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): - return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) - } - } - - public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { - var _1: Api.StatsDateRangeDays? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays - } - var _2: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _3: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _4: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _5: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _6: Api.StatsGraph? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _7: Api.StatsGraph? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _8: Api.StatsGraph? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _9: Api.StatsGraph? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _10: Api.StatsGraph? - if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _11: Api.StatsGraph? - if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _12: Api.StatsGraph? - if let signature = reader.readInt32() { - _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _13: Api.StatsGraph? - if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _14: [Api.StatsGroupTopPoster]? - if let _ = reader.readInt32() { - _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) - } - var _15: [Api.StatsGroupTopAdmin]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) - } - var _16: [Api.StatsGroupTopInviter]? - if let _ = reader.readInt32() { - _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) - } - var _17: [Api.User]? - if let _ = reader.readInt32() { - _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - let _c16 = _16 != nil - let _c17 = _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) - } - else { - return nil - } - } - - } -} -public extension Api.stats { - enum MessageStats: TypeConstructorDescription { - case messageStats(viewsGraph: Api.StatsGraph) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .messageStats(let viewsGraph): - if boxed { - buffer.appendInt32(-1986399595) - } - viewsGraph.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .messageStats(let viewsGraph): - return ("messageStats", [("viewsGraph", viewsGraph as Any)]) - } - } - - public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { - var _1: Api.StatsGraph? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - let _c1 = _1 != nil - if _c1 { - return Api.stats.MessageStats.messageStats(viewsGraph: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.stickers { - enum SuggestedShortName: TypeConstructorDescription { - case suggestedShortName(shortName: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .suggestedShortName(let shortName): - if boxed { - buffer.appendInt32(-2046910401) - } - serializeString(shortName, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .suggestedShortName(let shortName): - return ("suggestedShortName", [("shortName", shortName as Any)]) - } - } - - public static func parse_suggestedShortName(_ reader: BufferReader) -> SuggestedShortName? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.storage { - enum FileType: TypeConstructorDescription { - case fileGif - case fileJpeg - case fileMov - case fileMp3 - case fileMp4 - case filePartial - case filePdf - case filePng - case fileUnknown - case fileWebp - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .fileGif: - if boxed { - buffer.appendInt32(-891180321) - } - - break - case .fileJpeg: - if boxed { - buffer.appendInt32(8322574) - } - - break - case .fileMov: - if boxed { - buffer.appendInt32(1258941372) - } - - break - case .fileMp3: - if boxed { - buffer.appendInt32(1384777335) - } - - break - case .fileMp4: - if boxed { - buffer.appendInt32(-1278304028) - } - - break - case .filePartial: - if boxed { - buffer.appendInt32(1086091090) - } - - break - case .filePdf: - if boxed { - buffer.appendInt32(-1373745011) - } - - break - case .filePng: - if boxed { - buffer.appendInt32(172975040) - } - - break - case .fileUnknown: - if boxed { - buffer.appendInt32(-1432995067) - } - - break - case .fileWebp: - if boxed { - buffer.appendInt32(276907596) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .fileGif: - return ("fileGif", []) - case .fileJpeg: - return ("fileJpeg", []) - case .fileMov: - return ("fileMov", []) - case .fileMp3: - return ("fileMp3", []) - case .fileMp4: - return ("fileMp4", []) - case .filePartial: - return ("filePartial", []) - case .filePdf: - return ("filePdf", []) - case .filePng: - return ("filePng", []) - case .fileUnknown: - return ("fileUnknown", []) - case .fileWebp: - return ("fileWebp", []) - } - } - - public static func parse_fileGif(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileGif - } - public static func parse_fileJpeg(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileJpeg - } - public static func parse_fileMov(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMov - } - public static func parse_fileMp3(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMp3 - } - public static func parse_fileMp4(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileMp4 - } - public static func parse_filePartial(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePartial - } - public static func parse_filePdf(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePdf - } - public static func parse_filePng(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.filePng - } - public static func parse_fileUnknown(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileUnknown - } - public static func parse_fileWebp(_ reader: BufferReader) -> FileType? { - return Api.storage.FileType.fileWebp - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 477390eda6..2b8dcc4db3 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -1,3 +1,423 @@ +public extension Api.stats { + enum MegagroupStats: TypeConstructorDescription { + case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + if boxed { + buffer.appendInt32(-276825834) + } + period.serialize(buffer, true) + members.serialize(buffer, true) + messages.serialize(buffer, true) + viewers.serialize(buffer, true) + posters.serialize(buffer, true) + growthGraph.serialize(buffer, true) + membersGraph.serialize(buffer, true) + newMembersBySourceGraph.serialize(buffer, true) + languagesGraph.serialize(buffer, true) + messagesGraph.serialize(buffer, true) + actionsGraph.serialize(buffer, true) + topHoursGraph.serialize(buffer, true) + weekdaysGraph.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topPosters.count)) + for item in topPosters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topAdmins.count)) + for item in topAdmins { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topInviters.count)) + for item in topInviters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) + } + } + + public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { + var _1: Api.StatsDateRangeDays? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays + } + var _2: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _3: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _4: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _5: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _6: Api.StatsGraph? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _7: Api.StatsGraph? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _8: Api.StatsGraph? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _9: Api.StatsGraph? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _10: Api.StatsGraph? + if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _11: Api.StatsGraph? + if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _12: Api.StatsGraph? + if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _13: Api.StatsGraph? + if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _14: [Api.StatsGroupTopPoster]? + if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) + } + var _15: [Api.StatsGroupTopAdmin]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) + } + var _16: [Api.StatsGroupTopInviter]? + if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) + } + var _17: [Api.User]? + if let _ = reader.readInt32() { + _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + let _c16 = _16 != nil + let _c17 = _17 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum MessageStats: TypeConstructorDescription { + case messageStats(viewsGraph: Api.StatsGraph) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageStats(let viewsGraph): + if boxed { + buffer.appendInt32(-1986399595) + } + viewsGraph.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .messageStats(let viewsGraph): + return ("messageStats", [("viewsGraph", viewsGraph as Any)]) + } + } + + public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { + var _1: Api.StatsGraph? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + let _c1 = _1 != nil + if _c1 { + return Api.stats.MessageStats.messageStats(viewsGraph: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.stickers { + enum SuggestedShortName: TypeConstructorDescription { + case suggestedShortName(shortName: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .suggestedShortName(let shortName): + if boxed { + buffer.appendInt32(-2046910401) + } + serializeString(shortName, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .suggestedShortName(let shortName): + return ("suggestedShortName", [("shortName", shortName as Any)]) + } + } + + public static func parse_suggestedShortName(_ reader: BufferReader) -> SuggestedShortName? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.storage { + enum FileType: TypeConstructorDescription { + case fileGif + case fileJpeg + case fileMov + case fileMp3 + case fileMp4 + case filePartial + case filePdf + case filePng + case fileUnknown + case fileWebp + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .fileGif: + if boxed { + buffer.appendInt32(-891180321) + } + + break + case .fileJpeg: + if boxed { + buffer.appendInt32(8322574) + } + + break + case .fileMov: + if boxed { + buffer.appendInt32(1258941372) + } + + break + case .fileMp3: + if boxed { + buffer.appendInt32(1384777335) + } + + break + case .fileMp4: + if boxed { + buffer.appendInt32(-1278304028) + } + + break + case .filePartial: + if boxed { + buffer.appendInt32(1086091090) + } + + break + case .filePdf: + if boxed { + buffer.appendInt32(-1373745011) + } + + break + case .filePng: + if boxed { + buffer.appendInt32(172975040) + } + + break + case .fileUnknown: + if boxed { + buffer.appendInt32(-1432995067) + } + + break + case .fileWebp: + if boxed { + buffer.appendInt32(276907596) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .fileGif: + return ("fileGif", []) + case .fileJpeg: + return ("fileJpeg", []) + case .fileMov: + return ("fileMov", []) + case .fileMp3: + return ("fileMp3", []) + case .fileMp4: + return ("fileMp4", []) + case .filePartial: + return ("filePartial", []) + case .filePdf: + return ("filePdf", []) + case .filePng: + return ("filePng", []) + case .fileUnknown: + return ("fileUnknown", []) + case .fileWebp: + return ("fileWebp", []) + } + } + + public static func parse_fileGif(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileGif + } + public static func parse_fileJpeg(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileJpeg + } + public static func parse_fileMov(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMov + } + public static func parse_fileMp3(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMp3 + } + public static func parse_fileMp4(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileMp4 + } + public static func parse_filePartial(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePartial + } + public static func parse_filePdf(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePdf + } + public static func parse_filePng(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.filePng + } + public static func parse_fileUnknown(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileUnknown + } + public static func parse_fileWebp(_ reader: BufferReader) -> FileType? { + return Api.storage.FileType.fileWebp + } + + } +} +public extension Api.stories { + enum AllStories: TypeConstructorDescription { + case allStories(flags: Int32, userStories: [Api.UserStories], nextOffset: String?, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .allStories(let flags, let userStories, let nextOffset, let users): + if boxed { + buffer.appendInt32(1214632796) + } + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(userStories.count)) + for item in userStories { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .allStories(let flags, let userStories, let nextOffset, let users): + return ("allStories", [("flags", flags as Any), ("userStories", userStories as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)]) + } + } + + public static func parse_allStories(_ reader: BufferReader) -> AllStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.UserStories]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.UserStories.self) + } + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.stories.AllStories.allStories(flags: _1!, userStories: _2!, nextOffset: _3, users: _4!) + } + else { + return nil + } + } + + } +} public extension Api.updates { enum ChannelDifference: TypeConstructorDescription { case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 9a9f6b60a7..35417f2dcc 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -8402,6 +8402,91 @@ public extension Api.functions.stickers { }) } } +public extension Api.functions.stories { + static func deleteStory(id: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2130720150) + serializeInt64(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.deleteStory", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} +public extension Api.functions.stories { + static func editStoryPrivacy(id: Int64, privacyRules: [Api.InputPrivacyRule]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(82204943) + serializeInt64(id, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(privacyRules.count)) + for item in privacyRules { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "stories.editStoryPrivacy", parameters: [("id", String(describing: id)), ("privacyRules", String(describing: privacyRules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func getAllStories(offset: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1976539530) + serializeString(offset, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getAllStories", parameters: [("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.AllStories? in + let reader = BufferReader(buffer) + var result: Api.stories.AllStories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.AllStories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getUserStories(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-103893274) + userId.serialize(buffer, true) + return (FunctionDescription(name: "stories.getUserStories", parameters: [("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} +public extension Api.functions.stories { + static func sendStory(media: Api.InputMedia, privacyRules: [Api.InputPrivacyRule]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1310573354) + media.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(privacyRules.count)) + for item in privacyRules { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "stories.sendStory", parameters: [("media", String(describing: media)), ("privacyRules", String(describing: privacyRules))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} public extension Api.functions.updates { static func getChannelDifference(flags: Int32, channel: Api.InputChannel, filter: Api.ChannelMessagesFilter, pts: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 30b0ec57da..57ab51a2a0 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -120,6 +120,7 @@ enum AccountStateMutationOperation { case UpdateConfig case UpdateExtendedMedia(MessageId, Api.MessageExtendedMedia) case ResetForumTopic(topicId: MessageId, data: StoreMessageHistoryThreadData, pts: Int32) + case UpdateStories(Api.UserStories) } struct HoleFromPreviousState { @@ -609,9 +610,13 @@ struct AccountMutableState { self.addOperation(.UpdateExtendedMedia(messageId, extendedMedia)) } + mutating func updateStories(stories: Api.UserStories) { + self.addOperation(.UpdateStories(stories)) + } + mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStories: break case let .AddMessages(messages, location): for message in messages { @@ -732,6 +737,7 @@ struct AccountReplayedFinalState { let updatedCalls: [Api.PhoneCall] let addedCallSignalingData: [(Int64, Data)] let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] + let storyUpdates: [InternalStoryUpdate] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let delayNotificatonsUntil: Int32? @@ -751,6 +757,7 @@ struct AccountFinalStateEvents { let updatedCalls: [Api.PhoneCall] let addedCallSignalingData: [(Int64, Data)] let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] + let storyUpdates: [InternalStoryUpdate] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let displayAlerts: [(text: String, isDropAuth: Bool)] @@ -766,10 +773,10 @@ struct AccountFinalStateEvents { let isPremiumUpdated: Bool var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !isPremiumUpdated + return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !isPremiumUpdated } - init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false) { + init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false) { self.addedIncomingMessageIds = addedIncomingMessageIds self.addedReactionEvents = addedReactionEvents self.wasScheduledMessageIds = wasScheduledMessageIds @@ -779,6 +786,7 @@ struct AccountFinalStateEvents { self.updatedCalls = updatedCalls self.addedCallSignalingData = addedCallSignalingData self.updatedGroupCallParticipants = updatedGroupCallParticipants + self.storyUpdates = storyUpdates self.updatedPeersNearby = updatedPeersNearby self.isContactUpdates = isContactUpdates self.displayAlerts = displayAlerts @@ -804,6 +812,7 @@ struct AccountFinalStateEvents { self.updatedCalls = state.updatedCalls self.addedCallSignalingData = state.addedCallSignalingData self.updatedGroupCallParticipants = state.updatedGroupCallParticipants + self.storyUpdates = state.storyUpdates self.updatedPeersNearby = state.updatedPeersNearby self.isContactUpdates = state.isContactUpdates self.displayAlerts = state.state.state.displayAlerts @@ -846,6 +855,6 @@ struct AccountFinalStateEvents { let isPremiumUpdated = self.isPremiumUpdated || other.isPremiumUpdated - return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated) + return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, storyUpdates: self.storyUpdates + other.storyUpdates, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated) } } diff --git a/submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift b/submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift index 6213365e05..93daeb39a5 100644 --- a/submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift +++ b/submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift @@ -21,7 +21,7 @@ public func updateAutodownloadSettingsInteractively(accountManager: AccountManag extension AutodownloadPresetSettings { init(apiAutodownloadSettings: Api.AutoDownloadSettings) { switch apiAutodownloadSettings { - case let .autoDownloadSettings(flags, photoSizeMax, videoSizeMax, fileSizeMax, videoUploadMaxbitrate): + case let .autoDownloadSettings(flags, photoSizeMax, videoSizeMax, fileSizeMax, videoUploadMaxbitrate, _, _): self.init(disabled: (flags & (1 << 0)) != 0, photoSizeMax: Int64(photoSizeMax), videoSizeMax: videoSizeMax, fileSizeMax: fileSizeMax, preloadLargeVideo: (flags & (1 << 1)) != 0, lessDataForPhoneCalls: (flags & (1 << 3)) != 0, videoUploadMaxbitrate: videoUploadMaxbitrate) } } @@ -47,6 +47,6 @@ func apiAutodownloadPresetSettings(_ autodownloadPresetSettings: AutodownloadPre if autodownloadPresetSettings.lessDataForPhoneCalls { flags |= (1 << 3) } - return .autoDownloadSettings(flags: flags, photoSizeMax: Int32(autodownloadPresetSettings.photoSizeMax), videoSizeMax: autodownloadPresetSettings.videoSizeMax, fileSizeMax: autodownloadPresetSettings.fileSizeMax, videoUploadMaxbitrate: autodownloadPresetSettings.videoUploadMaxbitrate) + return .autoDownloadSettings(flags: flags, photoSizeMax: Int32(autodownloadPresetSettings.photoSizeMax), videoSizeMax: autodownloadPresetSettings.videoSizeMax, fileSizeMax: autodownloadPresetSettings.fileSizeMax, videoUploadMaxbitrate: autodownloadPresetSettings.videoUploadMaxbitrate, smallQueueActiveOperationsMax: 0, largeQueueActiveOperationsMax: 0) } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 8c8bd96131..7fe725beef 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1628,6 +1628,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: updatedState.reloadConfig() case let .updateMessageExtendedMedia(peer, msgId, extendedMedia): updatedState.updateExtendedMedia(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), extendedMedia: extendedMedia) + case let .updateStories(stories): + updatedState.updateStories(stories: stories) default: break } @@ -3000,7 +3002,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStories: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -3102,6 +3104,7 @@ func replayFinalState( var updatedCalls: [Api.PhoneCall] = [] var addedCallSignalingData: [(Int64, Data)] = [] var updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [] + var storyUpdates: [InternalStoryUpdate] = [] var updatedPeersNearby: [PeerNearby]? var isContactUpdates: [(PeerId, Bool)] = [] var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = [] @@ -4323,6 +4326,22 @@ func replayFinalState( transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId) transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId) } + case let .UpdateStories(updateStories): + switch updateStories { + case .userStories(let userId, let stories), .userStoriesShort(let userId, let stories, _): + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) + for storyItem in stories { + switch storyItem { + case let .storyItemDeleted(id): + storyUpdates.append(InternalStoryUpdate.deleted(id)) + case let .storyItem(id, date, media): + let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) + if let parsedMedia = parsedMedia { + storyUpdates.append(InternalStoryUpdate.added(peerId: peerId, item: StoryListContext.Item(id: id, timestamp: date, media: EngineMedia(parsedMedia)))) + } + } + } + } } } @@ -4777,5 +4796,5 @@ func replayFinalState( requestChatListFiltersSync(transaction: transaction) } - return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedReactionEvents: addedReactionEvents, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates, updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated) + return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedReactionEvents: addedReactionEvents, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, storyUpdates: storyUpdates, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates, updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated) } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index 4cbcdfa3a9..04dc00bccc 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -224,6 +224,11 @@ public final class AccountStateManager { return self.deletedMessagesPipe.signal() } + private let storyUpdatesPipe = ValuePipe<[InternalStoryUpdate]>() + public var storyUpdates: Signal<[InternalStoryUpdate], NoError> { + return self.storyUpdatesPipe.signal() + } + private var updatedWebpageContexts: [MediaId: UpdatedWebpageSubscriberContext] = [:] private var updatedPeersNearbyContext = UpdatedPeersNearbySubscriberContext() @@ -960,6 +965,9 @@ public final class AccountStateManager { if !events.updatedGroupCallParticipants.isEmpty { strongSelf.groupCallParticipantUpdatesPipe.putNext(events.updatedGroupCallParticipants) } + if !events.storyUpdates.isEmpty { + strongSelf.storyUpdatesPipe.putNext(events.storyUpdates) + } if !events.updatedIncomingThreadReadStates.isEmpty || !events.updatedOutgoingThreadReadStates.isEmpty { strongSelf.threadReadStateUpdatesPipe.putNext((events.updatedIncomingThreadReadStates, events.updatedOutgoingThreadReadStates)) } @@ -1625,6 +1633,12 @@ public final class AccountStateManager { } } + var storyUpdates: Signal<[InternalStoryUpdate], NoError> { + return self.impl.signalWith { impl, subscriber in + return impl.storyUpdates.start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion) + } + } + var updateConfigRequested: (() -> Void)? var isPremiumUpdated: (() -> Void)? diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 72656f0174..dfaeb00101 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 159 + return 160 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift index 70eb29f827..100ed57144 100644 --- a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift +++ b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift @@ -108,6 +108,6 @@ extension UserLimitsConfiguration { self.maxAnimatedEmojisInText = getGeneralValue("message_animated_emoji_max", orElse: defaultValue.maxAnimatedEmojisInText) self.maxReactionsPerMessage = getValue("reactions_user_max", orElse: 1) self.maxSharedFolderInviteLinks = getValue("chatlist_invites_limit", orElse: isPremium ? 100 : 3) - self.maxSharedFolderJoin = getValue("chatlist_joined_limit", orElse: isPremium ? 100 : 2) + self.maxSharedFolderJoin = getValue("chatlists_joined_limit", orElse: isPremium ? 100 : 2) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift new file mode 100644 index 0000000000..6ec564bb2b --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -0,0 +1,107 @@ +import Foundation +import SwiftSignalKit +import Postbox +import TelegramApi + +public enum EngineStoryInputMedia { + case image(dimensions: PixelDimensions, data: Data) +} + +func _internal_uploadStory(account: Account, media: EngineStoryInputMedia) -> Signal { + switch media { + case let .image(dimensions, data): + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) + account.postbox.mediaBox.storeResourceData(resource.id, data: data) + + let imageMedia = TelegramMediaImage( + imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: MediaId.Id.random(in: MediaId.Id.min ... MediaId.Id.max)), + representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], + immediateThumbnailData: nil, + reference: nil, + partialReference: nil, + flags: [] + ) + + let contentToUpload = messageContentToUpload( + network: account.network, + postbox: account.postbox, + auxiliaryMethods: account.auxiliaryMethods, + transformOutgoingMessageMedia: nil, + messageMediaPreuploadManager: account.messageMediaPreuploadManager, + revalidationContext: account.mediaReferenceRevalidationContext, + forceReupload: true, + isGrouped: false, + peerId: account.peerId, + messageId: nil, + attributes: [], + text: "", + media: [imageMedia] + ) + let contentSignal: Signal + switch contentToUpload { + case let .immediate(result, _): + contentSignal = .single(result) + case let .signal(signal, _): + contentSignal = signal + } + + return contentSignal + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + switch result { + case let .content(content): + switch content.content { + case let .media(inputMedia, _): + return account.network.request(Api.functions.stories.sendStory(media: inputMedia, privacyRules: [.inputPrivacyValueAllowAll])) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + if let updates = updates { + for update in updates.allUpdates { + if case let .updateStories(stories) = update { + switch stories { + case .userStories(let userId, let apiStories), .userStoriesShort(let userId, let apiStories, _): + if PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) == account.peerId, apiStories.count == 1 { + switch apiStories[0] { + case let .storyItem(_, _, media): + let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId) + if let parsedMedia = parsedMedia { + applyMediaResourceChanges(from: imageMedia, to: parsedMedia, postbox: account.postbox, force: false) + } + default: + break + } + } + } + } + } + + account.stateManager.addUpdates(updates) + } + + return .complete() + } + default: + return .complete() + } + default: + return .complete() + } + } + } +} + +func _internal_deleteStory(account: Account, id: Int64) -> Signal { + return account.network.request(Api.functions.stories.deleteStory(id: id)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift new file mode 100644 index 0000000000..0d05910d51 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -0,0 +1,490 @@ +import Foundation +import Postbox +import TelegramApi +import SwiftSignalKit + +enum InternalStoryUpdate { + case deleted(Int64) + case added(peerId: PeerId, item: StoryListContext.Item) +} + +public final class StoryListContext { + public enum Scope { + case all + case peer(EnginePeer.Id) + } + + public final class Item: Equatable { + public let id: Int64 + public let timestamp: Int32 + public let media: EngineMedia + + public init(id: Int64, timestamp: Int32, media: EngineMedia) { + self.id = id + self.timestamp = timestamp + self.media = media + } + + public static func ==(lhs: Item, rhs: Item) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.media != rhs.media { + return false + } + return true + } + } + + public final class PeerItemSet: Equatable { + public let peerId: EnginePeer.Id + public let peer: EnginePeer? + public fileprivate(set) var items: [Item] + public fileprivate(set) var totalCount: Int? + + public init(peerId: EnginePeer.Id, peer: EnginePeer?, items: [Item], totalCount: Int?) { + self.peerId = peerId + self.peer = peer + self.items = items + self.totalCount = totalCount + } + + public static func ==(lhs: PeerItemSet, rhs: PeerItemSet) -> Bool { + if lhs.peerId != rhs.peerId { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.items != rhs.items { + return false + } + if lhs.totalCount != rhs.totalCount { + return false + } + return true + } + } + + public final class LoadMoreToken: Equatable { + fileprivate let value: String? + + init(value: String?) { + self.value = value + } + + public static func ==(lhs: LoadMoreToken, rhs: LoadMoreToken) -> Bool { + if lhs.value != rhs.value { + return false + } + return true + } + } + + public struct State: Equatable { + public var itemSets: [PeerItemSet] + public var loadMoreToken: LoadMoreToken? + + public init(itemSets: [PeerItemSet], loadMoreToken: LoadMoreToken?) { + self.itemSets = itemSets + self.loadMoreToken = loadMoreToken + } + } + + private final class Impl { + private let queue: Queue + private let account: Account + private let scope: Scope + + private let loadMoreDisposable = MetaDisposable() + private var isLoadingMore = false + + private var pollDisposable: Disposable? + private var updatesDisposable: Disposable? + private var peerDisposables: [PeerId: Disposable] = [:] + + private var stateValue: State { + didSet { + self.state.set(.single(self.stateValue)) + } + } + let state = Promise() + + init(queue: Queue, account: Account, scope: Scope) { + self.queue = queue + self.account = account + self.scope = scope + + self.stateValue = State(itemSets: [], loadMoreToken: LoadMoreToken(value: nil)) + self.state.set(.single(self.stateValue)) + + self.updatesDisposable = (account.stateManager.storyUpdates + |> deliverOn(queue)).start(next: { [weak self] updates in + if updates.isEmpty { + return + } + + let _ = account.postbox.transaction({ transaction -> [PeerId: Peer] in + var peers: [PeerId: Peer] = [:] + for update in updates { + switch update { + case let .added(peerId, _): + if peers[peerId] == nil, let peer = transaction.getPeer(peerId) { + peers[peer.id] = peer + } + case .deleted: + break + } + } + return peers + }).start(next: { peers in + guard let self else { + return + } + + var itemSets: [PeerItemSet] = self.stateValue.itemSets + + for update in updates { + switch update { + case let .deleted(id): + for i in 0 ..< itemSets.count { + if let index = itemSets[i].items.firstIndex(where: { $0.id != id }) { + var items = itemSets[i].items + items.remove(at: index) + itemSets[i] = PeerItemSet( + peerId: itemSets[i].peerId, + peer: itemSets[i].peer, + items: items, + totalCount: items.count + ) + } + } + case let .added(peerId, item): + var found = false + for i in 0 ..< itemSets.count { + if itemSets[i].peerId == peerId { + found = true + + var items = itemSets[i].items + if let index = items.firstIndex(where: { $0.id == item.id }) { + items.remove(at: index) + } + items.append(item) + items.sort(by: { lhsItem, rhsItem in + if lhsItem.timestamp != rhsItem.timestamp { + return lhsItem.timestamp > rhsItem.timestamp + } + return lhsItem.id > rhsItem.id + }) + itemSets[i] = PeerItemSet( + peerId: itemSets[i].peerId, + peer: itemSets[i].peer, + items: items, + totalCount: items.count + ) + } + } + if !found, let peer = peers[peerId] { + itemSets.insert(PeerItemSet( + peerId: peerId, + peer: EnginePeer(peer), + items: [item], + totalCount: 1 + ), at: 0) + } + } + } + + itemSets.sort(by: { lhs, rhs in + guard let lhsItem = lhs.items.first, let rhsItem = rhs.items.first else { + if lhs.items.first != nil { + return false + } else { + return true + } + } + + if lhsItem.timestamp != rhsItem.timestamp { + return lhsItem.timestamp > rhsItem.timestamp + } + return lhsItem.id > rhsItem.id + }) + + self.stateValue.itemSets = itemSets + }) + }) + + self.loadMore(refresh: true) + } + + deinit { + self.loadMoreDisposable.dispose() + self.pollDisposable?.dispose() + for (_, disposable) in self.peerDisposables { + disposable.dispose() + } + } + + func loadPeer(id: EnginePeer.Id) { + if self.peerDisposables[id] == nil { + let disposable = MetaDisposable() + self.peerDisposables[id] = disposable + + let account = self.account + let queue = self.queue + + disposable.set((self.account.postbox.transaction { transaction -> Api.InputUser? in + return transaction.getPeer(id).flatMap(apiInputUser) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .single(nil) + } + return account.network.request(Api.functions.stories.getUserStories(userId: inputPeer)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + guard let updates = updates else { + return .single(nil) + } + return account.postbox.transaction { transaction -> PeerItemSet? in + for update in updates.allUpdates { + if case let .updateStories(stories) = update { + switch stories { + case .userStories(_, let apiStories), .userStoriesShort(_, let apiStories, _): + var parsedItemSets: [PeerItemSet] = [] + + let peerId = id + + for apiStory in apiStories { + switch apiStory { + case let .storyItem(id, date, media): + let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) + if let parsedMedia = parsedMedia { + let item = StoryListContext.Item(id: id, timestamp: date, media: EngineMedia(parsedMedia)) + if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId { + parsedItemSets[parsedItemSets.count - 1].items.append(item) + parsedItemSets[parsedItemSets.count - 1].totalCount = parsedItemSets[parsedItemSets.count - 1].items.count + } else { + parsedItemSets.append(StoryListContext.PeerItemSet(peerId: peerId, peer: transaction.getPeer(peerId).flatMap(EnginePeer.init), items: [item], totalCount: 1)) + } + } + case .storyItemDeleted: + break + } + } + + return parsedItemSets.first + } + } + } + return nil + } + } + } + |> deliverOn(queue)).start(next: { [weak self] itemSet in + guard let `self` = self, let itemSet = itemSet else { + return + } + var itemSets = self.stateValue.itemSets + if let index = itemSets.firstIndex(where: { $0.peerId == id }) { + itemSets[index] = itemSet + } + self.stateValue.itemSets = itemSets + })) + } + } + + func loadMore(refresh: Bool) { + if self.isLoadingMore { + return + } + + var effectiveLoadMoreToken: String? + if refresh { + effectiveLoadMoreToken = "" + } else if let loadMoreToken = self.stateValue.loadMoreToken { + effectiveLoadMoreToken = loadMoreToken.value ?? "" + } + guard let loadMoreToken = effectiveLoadMoreToken else { + return + } + + self.isLoadingMore = true + let account = self.account + + self.pollDisposable?.dispose() + self.pollDisposable = nil + + self.loadMoreDisposable.set((account.network.request(Api.functions.stories.getAllStories(offset: loadMoreToken)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<([PeerItemSet], LoadMoreToken?), NoError> in + guard let result else { + return .single(([], nil)) + } + return account.postbox.transaction { transaction -> ([PeerItemSet], LoadMoreToken?) in + switch result { + case let .allStories(_, userStorySets, nextOffset, users): + var parsedItemSets: [PeerItemSet] = [] + + var peers: [Peer] = [] + var peerPresences: [PeerId: Api.User] = [:] + + for user in users { + let telegramUser = TelegramUser(user: user) + peers.append(telegramUser) + peerPresences[telegramUser.id] = user + } + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + + for userStories in userStorySets { + let apiUserId: Int64 + let apiStories: [Api.StoryItem] + var apiTotalCount: Int32? + switch userStories { + case let .userStories(userId, stories): + apiUserId = userId + apiStories = stories + case let .userStoriesShort(userId, stories, totalCount): + apiUserId = userId + apiStories = stories + apiTotalCount = totalCount + } + + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(apiUserId)) + for apiStory in apiStories { + switch apiStory { + case let .storyItem(id, date, media): + let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) + if let parsedMedia = parsedMedia { + let item = StoryListContext.Item(id: id, timestamp: date, media: EngineMedia(parsedMedia)) + if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId { + parsedItemSets[parsedItemSets.count - 1].items.append(item) + } else { + parsedItemSets.append(StoryListContext.PeerItemSet(peerId: peerId, peer: transaction.getPeer(peerId).flatMap(EnginePeer.init), items: [item], totalCount: apiTotalCount.flatMap(Int.init))) + } + } + case .storyItemDeleted: + break + } + } + } + + return (parsedItemSets, nextOffset.flatMap { LoadMoreToken(value: $0) }) + } + } + } + |> deliverOn(self.queue)).start(next: { [weak self] result in + guard let `self` = self else { + return + } + self.isLoadingMore = false + + var itemSets = self.stateValue.itemSets + for itemSet in result.0 { + if let index = itemSets.firstIndex(where: { $0.peerId == itemSet.peerId }) { + let currentItemSet = itemSets[index] + + var items = currentItemSet.items + for item in itemSet.items { + if !items.contains(where: { $0.id == item.id }) { + items.append(item) + } + } + + items.sort(by: { lhsItem, rhsItem in + if lhsItem.timestamp != rhsItem.timestamp { + return lhsItem.timestamp > rhsItem.timestamp + } + return lhsItem.id > rhsItem.id + }) + + itemSets[index] = PeerItemSet( + peerId: itemSet.peerId, + peer: itemSet.peer, + items: items, + totalCount: items.count + ) + } else { + itemSets.append(itemSet) + } + } + + itemSets.sort(by: { lhs, rhs in + guard let lhsItem = lhs.items.first, let rhsItem = rhs.items.first else { + if lhs.items.first != nil { + return false + } else { + return true + } + } + + if lhsItem.timestamp != rhsItem.timestamp { + return lhsItem.timestamp > rhsItem.timestamp + } + return lhsItem.id > rhsItem.id + }) + + self.stateValue = State(itemSets: itemSets, loadMoreToken: result.1) + })) + } + + func delete(id: Int64) { + let _ = _internal_deleteStory(account: self.account, id: id).start() + + var itemSets: [PeerItemSet] = self.stateValue.itemSets + for i in (0 ..< itemSets.count).reversed() { + if let index = itemSets[i].items.firstIndex(where: { $0.id == id }) { + var items = itemSets[i].items + items.remove(at: index) + if items.isEmpty { + itemSets.remove(at: i) + } else { + itemSets[i] = PeerItemSet( + peerId: itemSets[i].peerId, + peer: itemSets[i].peer, + items: items, + totalCount: items.count + ) + } + } + } + self.stateValue.itemSets = itemSets + } + } + + private let queue: Queue + private let impl: QueueLocalObject + + public var state: Signal { + return self.impl.signalWith { impl, subscriber in + return impl.state.get().start(next: subscriber.putNext) + } + } + + init(account: Account, scope: Scope) { + let queue = Queue.mainQueue() + self.queue = queue + self.impl = QueueLocalObject(queue: queue, generate: { + return Impl(queue: queue, account: account, scope: scope) + }) + } + + public func delete(id: Int64) { + self.impl.with { impl in + impl.delete(id: id) + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 61a89f8c32..22307f2892 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -568,5 +568,21 @@ public extension TelegramEngine { _internal_removeSyncrhonizeAutosaveItemOperations(transaction: transaction, indices: indices) }).start() } + + public func allStories() -> StoryListContext { + return StoryListContext(account: self.account, scope: .all) + } + + public func peerStories(id: EnginePeer.Id) -> StoryListContext { + return StoryListContext(account: self.account, scope: .peer(id)) + } + + public func uploadStory(media: EngineStoryInputMedia) -> Signal { + return _internal_uploadStory(account: self.account, media: media) + } + + public func deleteStory(id: Int64) -> Signal { + return _internal_deleteStory(account: account, id: id) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 6d5eb08fef..6015b6f45e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -237,7 +237,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } switch fullUser { - case let .userFull(_, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _): + case let .userFull(_, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _): updatePeers(transaction: transaction, peers: peers, update: { previous, updated -> Peer in if previous?.id == accountPeerId, let accountUser = accountUser, let user = TelegramUser.merge(previous as? TelegramUser, rhs: accountUser) { return user @@ -255,7 +255,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee previous = CachedUserData() } switch fullUser { - case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userPremiumGiftOptions, userWallpaper): + case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userPremiumGiftOptions, userWallpaper, stories): + let _ = stories let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:)) let isBlocked = (userFullFlags & (1 << 0)) != 0 let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0 diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 6c13d7c648..6e596d91e2 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -26,6 +26,7 @@ public final class MessageInputPanelComponent: Component { public let sendMessageAction: () -> Void public let setMediaRecordingActive: (Bool, Bool, Bool) -> Void public let attachmentAction: () -> Void + public let reactionAction: (UIView) -> Void public let audioRecorder: ManagedAudioRecorder? public let videoRecordingStatus: InstantVideoControllerRecordingStatus? @@ -38,6 +39,7 @@ public final class MessageInputPanelComponent: Component { sendMessageAction: @escaping () -> Void, setMediaRecordingActive: @escaping (Bool, Bool, Bool) -> Void, attachmentAction: @escaping () -> Void, + reactionAction: @escaping (UIView) -> Void, audioRecorder: ManagedAudioRecorder?, videoRecordingStatus: InstantVideoControllerRecordingStatus? ) { @@ -49,6 +51,7 @@ public final class MessageInputPanelComponent: Component { self.sendMessageAction = sendMessageAction self.setMediaRecordingActive = setMediaRecordingActive self.attachmentAction = attachmentAction + self.reactionAction = reactionAction self.audioRecorder = audioRecorder self.videoRecordingStatus = videoRecordingStatus } @@ -87,7 +90,8 @@ public final class MessageInputPanelComponent: Component { private let attachmentButton = ComponentView() private let inputActionButton = ComponentView() - private let stickerIconView: UIImageView + private let stickerButton = ComponentView() + private let reactionButton = ComponentView() private var mediaRecordingPanel: ComponentView? private weak var dismissingMediaRecordingPanel: UIView? @@ -100,14 +104,10 @@ public final class MessageInputPanelComponent: Component { override init(frame: CGRect) { self.fieldBackgroundView = UIImageView() - self.stickerIconView = UIImageView() super.init(frame: frame) self.addSubview(self.fieldBackgroundView) - - self.addSubview(self.fieldBackgroundView) - self.addSubview(self.stickerIconView) } required init?(coder: NSCoder) { @@ -146,11 +146,6 @@ public final class MessageInputPanelComponent: Component { if self.fieldBackgroundView.image == nil { self.fieldBackgroundView.image = generateStretchableFilledCircleImage(diameter: fieldCornerRadius * 2.0, color: nil, strokeColor: UIColor(white: 1.0, alpha: 0.16), strokeWidth: 1.0, backgroundColor: nil) } - if self.stickerIconView.image == nil { - self.stickerIconView.image = UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconStickers")?.withRenderingMode(.alwaysTemplate) - self.stickerIconView.tintColor = .white - } - transition.setAlpha(view: self.stickerIconView, alpha: (component.audioRecorder != nil || component.videoRecordingStatus != nil) ? 0.0 : 1.0) let availableTextFieldSize = CGSize(width: availableSize.width - insets.left - insets.right, height: availableSize.height - insets.top - insets.bottom) @@ -169,7 +164,7 @@ public final class MessageInputPanelComponent: Component { transition.setFrame(view: self.fieldBackgroundView, frame: fieldFrame) transition.setAlpha(view: self.fieldBackgroundView, alpha: (component.audioRecorder != nil || component.videoRecordingStatus != nil) ? 0.0 : 1.0) - let rightFieldInset: CGFloat = 34.0 + //let rightFieldInset: CGFloat = 34.0 let size = CGSize(width: availableSize.width, height: textFieldSize.height + insets.top + insets.bottom) @@ -258,15 +253,80 @@ public final class MessageInputPanelComponent: Component { } transition.setFrame(view: inputActionButtonView, frame: CGRect(origin: CGPoint(x: size.width - insets.right + floorToScreenPixels((insets.right - inputActionButtonSize.width) * 0.5), y: size.height - baseHeight + floorToScreenPixels((baseHeight - inputActionButtonSize.height) * 0.5)), size: inputActionButtonSize)) } - if let image = self.stickerIconView.image { - let stickerIconFrame = CGRect(origin: CGPoint(x: fieldFrame.maxX - rightFieldInset + floor((rightFieldInset - image.size.width) * 0.5), y: fieldFrame.minY + floor((fieldFrame.height - image.size.height) * 0.5)), size: image.size) - transition.setPosition(view: self.stickerIconView, position: stickerIconFrame.center) - transition.setBounds(view: self.stickerIconView, bounds: CGRect(origin: CGPoint(), size: stickerIconFrame.size)) + var fieldIconNextX = fieldFrame.maxX - 2.0 + let stickerButtonSize = self.stickerButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(BundleIconComponent( + name: "Chat/Input/Text/AccessoryIconStickers", + tintColor: .white + )), + action: { [weak self] in + guard let self else { + return + } + self.component?.attachmentAction() + } + ).minSize(CGSize(width: 32.0, height: 32.0))), + environment: {}, + containerSize: CGSize(width: 32.0, height: 32.0) + ) + if let stickerButtonView = self.stickerButton.view { + if stickerButtonView.superview == nil { + self.addSubview(stickerButtonView) + } + let stickerIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - stickerButtonSize.width, y: fieldFrame.minY + floor((fieldFrame.height - stickerButtonSize.height) * 0.5)), size: stickerButtonSize) + transition.setPosition(view: stickerButtonView, position: stickerIconFrame.center) + transition.setBounds(view: stickerButtonView, bounds: CGRect(origin: CGPoint(), size: stickerIconFrame.size)) - transition.setAlpha(view: self.stickerIconView, alpha: self.textFieldExternalState.hasText ? 0.0 : 1.0) - transition.setScale(view: self.stickerIconView, scale: self.textFieldExternalState.hasText ? 0.1 : 1.0) + transition.setAlpha(view: stickerButtonView, alpha: self.textFieldExternalState.hasText ? 0.0 : 1.0) + transition.setScale(view: stickerButtonView, scale: self.textFieldExternalState.hasText ? 0.1 : 1.0) + + fieldIconNextX -= stickerButtonSize.width + 2.0 } + let reactionButtonSize = self.reactionButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(BundleIconComponent( + name: "Chat/Input/Text/AccessoryIconReaction", + tintColor: .white + )), + action: { [weak self] in + guard let self, let reactionButtonView = self.reactionButton.view else { + return + } + self.component?.reactionAction(reactionButtonView) + } + ).minSize(CGSize(width: 32.0, height: 32.0))), + environment: {}, + containerSize: CGSize(width: 32.0, height: 32.0) + ) + if let reactionButtonView = self.reactionButton.view { + if reactionButtonView.superview == nil { + self.addSubview(reactionButtonView) + } + let reactionIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - reactionButtonSize.width, y: fieldFrame.minY + 1.0 + floor((fieldFrame.height - reactionButtonSize.height) * 0.5)), size: reactionButtonSize) + transition.setPosition(view: reactionButtonView, position: reactionIconFrame.center) + transition.setBounds(view: reactionButtonView, bounds: CGRect(origin: CGPoint(), size: reactionIconFrame.size)) + + transition.setAlpha(view: reactionButtonView, alpha: self.textFieldExternalState.hasText ? 0.0 : 1.0) + transition.setScale(view: reactionButtonView, scale: self.textFieldExternalState.hasText ? 0.1 : 1.0) + + fieldIconNextX -= reactionButtonSize.width + 2.0 + } + + /*if let image = self.reactionIconView.image { + let stickerIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - image.size.width, y: fieldFrame.minY + floor((fieldFrame.height - image.size.height) * 0.5)), size: image.size) + transition.setPosition(view: self.reactionIconView, position: stickerIconFrame.center) + transition.setBounds(view: self.reactionIconView, bounds: CGRect(origin: CGPoint(), size: stickerIconFrame.size)) + + transition.setAlpha(view: self.reactionIconView, alpha: self.textFieldExternalState.hasText ? 0.0 : 1.0) + transition.setScale(view: self.reactionIconView, scale: self.textFieldExternalState.hasText ? 0.1 : 1.0) + + fieldIconNextX -= image.size.width + 4.0 + }*/ + component.externalState.isEditing = self.textFieldExternalState.isEditing component.externalState.hasText = self.textFieldExternalState.hasText diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD index 3e3596dd2e..0524dc5a4b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD @@ -11,6 +11,7 @@ swift_library( ], deps = [ "//submodules/Display", + "//submodules/AsyncDisplayKit", "//submodules/ComponentFlow", "//submodules/Components/ViewControllerComponent", "//submodules/Components/ComponentDisplayAdapters", @@ -20,6 +21,7 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit", "//submodules/AppBundle", "//submodules/TelegramCore", + "//submodules/Postbox", "//submodules/ShareController", "//submodules/UndoUI", "//submodules/AttachmentUI", @@ -41,8 +43,11 @@ swift_library( "//submodules/LegacyComponents", "//submodules/TelegramUI/Components/LegacyCamera", "//submodules/TelegramUI/Components/LegacyInstantVideoController", + "//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent", "//submodules/TelegramPresentationData", + "//submodules/ReactionSelectionNode", + "//submodules/ContextUI", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 30e23668f2..64bf41fe96 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -9,6 +9,7 @@ import AppBundle import MessageInputPanelComponent import ShareController import TelegramCore +import Postbox import UndoUI import AttachmentUI import TelegramUIPreferences @@ -32,8 +33,12 @@ import LegacyCamera import StoryFooterPanelComponent import TelegramPresentationData import LegacyInstantVideoController +import ReactionSelectionNode +import EntityKeyboard +import AsyncDisplayKit +import simd -private func hasFirstResponder(_ view: UIView) -> Bool { +func hasFirstResponder(_ view: UIView) -> Bool { if view.isFirstResponder { return true } @@ -49,13 +54,16 @@ private final class StoryContainerScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext - let initialContent: StoryContentItemSlice + let initialFocusedId: AnyHashable? + let initialContent: [StoryContentItemSlice] init( context: AccountContext, - initialContent: StoryContentItemSlice + initialFocusedId: AnyHashable?, + initialContent: [StoryContentItemSlice] ) { self.context = context + self.initialFocusedId = initialFocusedId self.initialContent = initialContent } @@ -63,70 +71,35 @@ private final class StoryContainerScreenComponent: Component { if lhs.context !== rhs.context { return false } - if lhs.initialContent !== rhs.initialContent { - return false - } return true } - private final class ScrollView: UIScrollView { - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - return super.hitTest(point, with: event) - } - - override func touchesShouldCancel(in view: UIView) -> Bool { - return true - } - } - - private struct ItemLayout { - var size: CGSize - - init(size: CGSize) { - self.size = size - } - } - - private final class VisibleItem { - let externalState = StoryContentItem.ExternalState() - let view = ComponentView() - var currentProgress: Double = 0.0 - var requestedNext: Bool = false - - init() { - } - } - - private final class InfoItem { - let component: AnyComponent + private final class ItemSetView: UIView { let view = ComponentView() + let externalState = StoryItemSetContainerComponent.ExternalState() - init(component: AnyComponent) { - self.component = component + override static var layerClass: AnyClass { + return CATransformLayer.self + } + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + + private struct ItemSetPanState: Equatable { + var fraction: CGFloat + + init(fraction: CGFloat) { + self.fraction = fraction } } final class View: UIView, UIScrollViewDelegate { - private let scrollView: ScrollView - - private let contentContainerView: UIView - private let topContentGradientLayer: SimpleGradientLayer - private let bottomContentGradientLayer: SimpleGradientLayer - private let contentDimLayer: SimpleLayer - - private let closeButton: HighlightableButton - private let closeButtonIconView: UIImageView - - private let navigationStrip = ComponentView() - private let inlineActions = ComponentView() - - private var centerInfoItem: InfoItem? - private var rightInfoItem: InfoItem? - - private let inputPanel = ComponentView() - private let footerPanel = ComponentView() - private let inputPanelExternalState = MessageInputPanelComponent.ExternalState() - private weak var attachmentController: AttachmentController? private let controllerNavigationDisposable = MetaDisposable() private let enqueueMediaMessageDisposable = MetaDisposable() @@ -135,12 +108,11 @@ private final class StoryContainerScreenComponent: Component { private weak var state: EmptyComponentState? private var environment: ViewControllerComponentContainer.Environment? - private var itemLayout: ItemLayout? - private var ignoreScrolling: Bool = false + private var focusedItemSet: AnyHashable? + private var itemSets: [StoryContentItemSlice] = [] + private var visibleItemSetViews: [AnyHashable: ItemSetView] = [:] - private var focusedItemId: AnyHashable? - private var currentSlice: StoryContentItemSlice? - private var currentSliceDisposable: Disposable? + private var itemSetPanState: ItemSetPanState? private var audioRecorderValue: ManagedAudioRecorder? private var audioRecorder = Promise() @@ -152,55 +124,12 @@ private final class StoryContainerScreenComponent: Component { private var videoRecorder = Promise() private var videoRecorderDisposable: Disposable? - private var visibleItems: [AnyHashable: VisibleItem] = [:] - - private var preloadContexts: [AnyHashable: Disposable] = [:] - override init(frame: CGRect) { - self.scrollView = ScrollView() - - self.contentContainerView = UIView() - self.contentContainerView.clipsToBounds = true - - self.topContentGradientLayer = SimpleGradientLayer() - self.bottomContentGradientLayer = SimpleGradientLayer() - self.contentDimLayer = SimpleLayer() - - self.closeButton = HighlightableButton() - self.closeButtonIconView = UIImageView() - super.init(frame: frame) self.backgroundColor = .black - self.scrollView.delaysContentTouches = false - self.scrollView.canCancelContentTouches = true - self.scrollView.clipsToBounds = false - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - self.scrollView.contentInsetAdjustmentBehavior = .never - } - if #available(iOS 13.0, *) { - self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false - } - self.scrollView.showsVerticalScrollIndicator = false - self.scrollView.showsHorizontalScrollIndicator = false - self.scrollView.alwaysBounceHorizontal = false - self.scrollView.alwaysBounceVertical = true - self.scrollView.scrollsToTop = false - self.scrollView.delegate = self - self.scrollView.clipsToBounds = true - - self.addSubview(self.contentContainerView) - self.layer.addSublayer(self.contentDimLayer) - self.layer.addSublayer(self.topContentGradientLayer) - self.layer.addSublayer(self.bottomContentGradientLayer) - - self.closeButton.addSubview(self.closeButtonIconView) - self.addSubview(self.closeButton) - self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside) - - self.contentContainerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) - self.contentContainerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) + self.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) self.audioRecorderDisposable = (self.audioRecorder.get() |> deliverOnMainQueue).start(next: { [weak self] audioRecorder in @@ -307,189 +236,76 @@ private final class StoryContainerScreenComponent: Component { } deinit { - self.currentSliceDisposable?.dispose() self.controllerNavigationDisposable.dispose() self.enqueueMediaMessageDisposable.dispose() self.audioRecorderDisposable?.dispose() self.audioRecorderStatusDisposable?.dispose() } - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state, let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let currentIndex = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }), let itemLayout = self.itemLayout { - if hasFirstResponder(self) { - self.endEditing(true) - } else { - let point = recognizer.location(in: self) - - var nextIndex: Int - if point.x < itemLayout.size.width * 0.25 { - nextIndex = currentIndex + 1 - } else { - nextIndex = currentIndex - 1 - } - nextIndex = max(0, min(nextIndex, currentSlice.items.count - 1)) - if nextIndex != currentIndex { - let focusedItemId = currentSlice.items[nextIndex].id - self.focusedItemId = focusedItemId - self.state?.updated(transition: .immediate) - - self.currentSliceDisposable?.dispose() - self.currentSliceDisposable = (currentSlice.update( - currentSlice, - focusedItemId - ) - |> deliverOnMainQueue).start(next: { [weak self] contentSlice in - guard let self else { - return - } - self.currentSlice = contentSlice - self.state?.updated(transition: .immediate) - }) - } - } - } - } - @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: - break + self.layer.removeAnimation(forKey: "panState") + + self.itemSetPanState = ItemSetPanState(fraction: 0.0) + self.state?.updated(transition: .immediate) case .changed: - break + if var itemSetPanState = self.itemSetPanState, self.bounds.width > 0.0 { + let translation = recognizer.translation(in: self) + + let fraction = translation.x / self.bounds.width + itemSetPanState.fraction = -max(-1.0, min(1.0, fraction)) + self.itemSetPanState = itemSetPanState + + self.state?.updated(transition: .immediate) + } case .cancelled, .ended: - break + if var itemSetPanState = self.itemSetPanState { + if let focusedItemSet = self.focusedItemSet, let focusedIndex = self.itemSets.firstIndex(where: { $0.id == focusedItemSet }) { + let velocity = recognizer.velocity(in: self) + + var switchToIndex = focusedIndex + if abs(velocity.x) > 10.0 { + if velocity.x < 0.0 { + switchToIndex += 1 + } else { + switchToIndex -= 1 + } + } + + switchToIndex = max(0, min(switchToIndex, self.itemSets.count - 1)) + if switchToIndex != focusedIndex { + self.focusedItemSet = self.itemSets[switchToIndex].id + + if switchToIndex < focusedIndex { + itemSetPanState.fraction = 1.0 + itemSetPanState.fraction + } else { + itemSetPanState.fraction = itemSetPanState.fraction - 1.0 + } + self.itemSetPanState = itemSetPanState + self.state?.updated(transition: .immediate) + } + } + + itemSetPanState.fraction = 0.0 + self.itemSetPanState = itemSetPanState + + let transition = Transition(animation: .curve(duration: 0.4, curve: .spring)) + self.state?.updated(transition: transition) + + transition.attachAnimation(view: self, id: "panState", completion: { [weak self] completed in + guard let self, completed else { + return + } + self.itemSetPanState = nil + self.state?.updated(transition: .immediate) + }) + } default: break } } - @objc private func closePressed() { - guard let environment = self.environment, let controller = environment.controller() else { - return - } - controller.dismiss() - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - if !self.ignoreScrolling { - self.updateScrolling(transition: .immediate) - } - } - - func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - return super.hitTest(point, with: event) - } - - private func updateScrolling(transition: Transition) { - guard let itemLayout = self.itemLayout else { - return - } - - var validIds: [AnyHashable] = [] - if let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) { - validIds.append(focusedItemId) - - var itemTransition = transition - let visibleItem: VisibleItem - if let current = self.visibleItems[focusedItemId] { - visibleItem = current - } else { - itemTransition = .immediate - visibleItem = VisibleItem() - self.visibleItems[focusedItemId] = visibleItem - } - - let _ = visibleItem.view.update( - transition: itemTransition, - component: focusedItem.component, - environment: { - StoryContentItem.Environment( - externalState: visibleItem.externalState, - presentationProgressUpdated: { [weak self, weak visibleItem] progress in - guard let self = self else { - return - } - guard let visibleItem else { - return - } - visibleItem.currentProgress = progress - - if let navigationStripView = self.navigationStrip.view as? MediaNavigationStripComponent.View { - navigationStripView.updateCurrentItemProgress(value: progress, transition: .immediate) - } - if progress >= 1.0 && !visibleItem.requestedNext { - visibleItem.requestedNext = true - - if let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let currentIndex = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }) { - var nextIndex = currentIndex - 1 - nextIndex = max(0, min(nextIndex, currentSlice.items.count - 1)) - if nextIndex != currentIndex { - let focusedItemId = currentSlice.items[nextIndex].id - self.focusedItemId = focusedItemId - self.state?.updated(transition: .immediate) - - self.currentSliceDisposable?.dispose() - self.currentSliceDisposable = (currentSlice.update( - currentSlice, - focusedItemId - ) - |> deliverOnMainQueue).start(next: { [weak self] contentSlice in - guard let self else { - return - } - self.currentSlice = contentSlice - self.state?.updated(transition: .immediate) - }) - } else { - self.environment?.controller()?.dismiss() - } - } - } - } - ) - }, - containerSize: itemLayout.size - ) - if let view = visibleItem.view.view { - if view.superview == nil { - view.isUserInteractionEnabled = false - self.contentContainerView.addSubview(view) - } - itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size)) - - if let view = view as? StoryContentItem.View { - view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController != nil || self.audioRecorderValue != nil || self.videoRecorderValue != nil) - } - } - } - - var removeIds: [AnyHashable] = [] - for (id, visibleItem) in self.visibleItems { - if !validIds.contains(id) { - removeIds.append(id) - if let view = visibleItem.view.view { - view.removeFromSuperview() - } - } - } - for id in removeIds { - self.visibleItems.removeValue(forKey: id) - } - } - - private func updateIsProgressPaused() { - for (_, visibleItem) in self.visibleItems { - if let view = visibleItem.view.view { - if let view = view as? StoryContentItem.View { - view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController?.window != nil || self.audioRecorderValue != nil || self.videoRecorderValue != nil) - } - } - } - } - func animateIn() { self.layer.allowsGroupOpacity = true self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in @@ -508,7 +324,7 @@ private final class StoryContainerScreenComponent: Component { } private func performSendMessageAction() { - guard let component = self.component else { + /*guard let component = self.component else { return } guard let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) else { @@ -543,11 +359,11 @@ private final class StoryContainerScreenComponent: Component { ), in: .current) } } - } + }*/ } private func setMediaRecordingActive(isActive: Bool, isVideo: Bool, sendAction: Bool) { - guard let component = self.component else { + /*guard let component = self.component else { return } guard let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) else { @@ -656,15 +472,14 @@ private final class StoryContainerScreenComponent: Component { self.videoRecorder.set(.single(nil)) } } - }) + })*/ } private func stopMediaRecorder() { - } private func performInlineAction(item: StoryActionsComponent.Item) { - guard let component = self.component else { + /*guard let component = self.component else { return } guard let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) else { @@ -708,21 +523,21 @@ private final class StoryContainerScreenComponent: Component { ) controller.present(shareController, in: .window(.root)) }) - } + }*/ } private func clearInputText() { - guard let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View else { + /*guard let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View else { return } - inputPanelView.clearSendMessageInput() + inputPanelView.clearSendMessageInput()*/ } private enum AttachMenuSubject { case `default` } - private func presentAttachmentMenu(subject: AttachMenuSubject) { + /*private func presentAttachmentMenu(subject: AttachMenuSubject) { guard let component = self.component else { return } @@ -2167,13 +1982,13 @@ private final class StoryContainerScreenComponent: Component { } let _ = (legacyAssetPickerEnqueueMessages(context: component.context, account: component.context.account, signals: signals) |> deliverOnMainQueue).start() - } + }*/ private func updatePreloads() { - var validIds: [AnyHashable] = [] + /*var validIds: [AnyHashable] = [] if let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let currentIndex = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }) { for i in 0 ..< 2 { - var nextIndex: Int = currentIndex - 1 - i + var nextIndex: Int = currentIndex + 1 + i nextIndex = max(0, min(nextIndex, currentSlice.items.count - 1)) if nextIndex != currentIndex { let nextItem = currentSlice.items[nextIndex] @@ -2197,370 +2012,205 @@ private final class StoryContainerScreenComponent: Component { } for id in removeIds { self.preloadContexts.removeValue(forKey: id) - } + }*/ } func update(component: StoryContainerScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { let isFirstTime = self.component == nil let environment = environment[ViewControllerComponentContainer.Environment.self].value - - if self.component == nil { - self.focusedItemId = component.initialContent.focusedItemId - self.currentSlice = component.initialContent - - self.currentSliceDisposable?.dispose() - self.currentSliceDisposable = (component.initialContent.update( - component.initialContent, - component.initialContent.focusedItemId - ) - |> deliverOnMainQueue).start(next: { [weak self] contentSlice in - guard let self else { - return - } - self.currentSlice = contentSlice - self.state?.updated(transition: .immediate) - }) - } - - if self.topContentGradientLayer.colors == nil { - var locations: [NSNumber] = [] - var colors: [CGColor] = [] - let numStops = 4 - let baseAlpha: CGFloat = 0.5 - for i in 0 ..< numStops { - let step = 1.0 - CGFloat(i) / CGFloat(numStops - 1) - locations.append((1.0 - step) as NSNumber) - let alphaStep: CGFloat = pow(step, 1.5) - colors.append(UIColor.black.withAlphaComponent(alphaStep * baseAlpha).cgColor) - } - - self.topContentGradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) - self.topContentGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) - - self.topContentGradientLayer.locations = locations - self.topContentGradientLayer.colors = colors - self.topContentGradientLayer.type = .axial - } - if self.bottomContentGradientLayer.colors == nil { - var locations: [NSNumber] = [] - var colors: [CGColor] = [] - let numStops = 10 - let baseAlpha: CGFloat = 0.7 - for i in 0 ..< numStops { - let step = 1.0 - CGFloat(i) / CGFloat(numStops - 1) - locations.append((1.0 - step) as NSNumber) - let alphaStep: CGFloat = pow(step, 1.5) - colors.append(UIColor.black.withAlphaComponent(alphaStep * baseAlpha).cgColor) - } - - self.bottomContentGradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) - self.bottomContentGradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0) - - self.bottomContentGradientLayer.locations = locations - self.bottomContentGradientLayer.colors = colors - self.bottomContentGradientLayer.type = .axial - - self.contentDimLayer.backgroundColor = UIColor(white: 0.0, alpha: 0.3).cgColor - } - - if let focusedItemId = self.focusedItemId { - if let currentSlice = self.currentSlice { - if !currentSlice.items.contains(where: { $0.id == focusedItemId }) { - self.focusedItemId = currentSlice.items.first?.id - } - } else { - self.focusedItemId = nil - } - } - - self.updatePreloads() + self.environment = environment self.component = component self.state = state - self.environment = environment - var bottomContentInset: CGFloat - if !environment.safeInsets.bottom.isZero { - bottomContentInset = environment.safeInsets.bottom + 5.0 - } else { - bottomContentInset = 0.0 + if isFirstTime { + if let initialFocusedId = component.initialFocusedId, component.initialContent.contains(where: { $0.id == initialFocusedId }) { + self.focusedItemSet = initialFocusedId + } else { + self.focusedItemSet = component.initialContent.first?.id + } + self.itemSets = component.initialContent } - self.inputPanel.parentState = state - let inputPanelSize = self.inputPanel.update( - transition: transition, - component: AnyComponent(MessageInputPanelComponent( - externalState: self.inputPanelExternalState, - context: component.context, - theme: environment.theme, - strings: environment.strings, - presentController: { [weak self] c in - guard let self, let controller = self.environment?.controller() else { - return - } - controller.present(c, in: .window(.root)) - }, - sendMessageAction: { [weak self] in - guard let self else { - return - } - self.performSendMessageAction() - }, - setMediaRecordingActive: { [weak self] isActive, isVideo, sendAction in - guard let self else { - return - } - self.setMediaRecordingActive(isActive: isActive, isVideo: isVideo, sendAction: sendAction) - }, - attachmentAction: { [weak self] in - guard let self else { - return - } - self.presentAttachmentMenu(subject: .default) - }, - audioRecorder: self.audioRecorderValue, - videoRecordingStatus: self.videoRecorderValue?.audioStatus - )), - environment: {}, - containerSize: CGSize(width: availableSize.width, height: 200.0) - ) - - let footerPanelSize = self.footerPanel.update( - transition: transition, - component: AnyComponent(StoryFooterPanelComponent( - )), - environment: {}, - containerSize: CGSize(width: availableSize.width, height: 200.0) - ) - - let bottomContentInsetWithoutInput = bottomContentInset - - let inputPanelBottomInset: CGFloat - let inputPanelIsOverlay: Bool - if environment.inputHeight < bottomContentInset + inputPanelSize.height { - inputPanelBottomInset = bottomContentInset - bottomContentInset += inputPanelSize.height - inputPanelIsOverlay = false - } else { - bottomContentInset += 44.0 - inputPanelBottomInset = environment.inputHeight - inputPanelIsOverlay = true + var isProgressPaused = false + if self.itemSetPanState != nil { + isProgressPaused = true } - let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: environment.statusBarHeight), size: CGSize(width: availableSize.width, height: availableSize.height - environment.statusBarHeight - bottomContentInset)) - transition.setFrame(view: self.contentContainerView, frame: contentFrame) - transition.setCornerRadius(layer: self.contentContainerView.layer, cornerRadius: 14.0) + var contentDerivedBottomInset: CGFloat = environment.safeInsets.bottom - if self.closeButtonIconView.image == nil { - self.closeButtonIconView.image = UIImage(bundleImageName: "Media Gallery/Close")?.withRenderingMode(.alwaysTemplate) - self.closeButtonIconView.tintColor = .white - } - if let image = self.closeButtonIconView.image { - let closeButtonFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: 50.0, height: 64.0)) - transition.setFrame(view: self.closeButton, frame: closeButtonFrame) - transition.setFrame(view: self.closeButtonIconView, frame: CGRect(origin: CGPoint(x: floor((closeButtonFrame.width - image.size.width) * 0.5), y: floor((closeButtonFrame.height - image.size.height) * 0.5)), size: image.size)) - } - - var focusedItem: StoryContentItem? - if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { - focusedItem = item - } - - var currentRightInfoItem: InfoItem? - if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { - if let rightInfoComponent = item.rightInfoComponent { - if let rightInfoItem = self.rightInfoItem, rightInfoItem.component == item.rightInfoComponent { - currentRightInfoItem = rightInfoItem - } else { - currentRightInfoItem = InfoItem(component: rightInfoComponent) + var validIds: [AnyHashable] = [] + if let focusedItemSet = self.focusedItemSet, let focusedIndex = self.itemSets.firstIndex(where: { $0.id == focusedItemSet }) { + for i in max(0, focusedIndex - 1) ... min(focusedIndex + 1, self.itemSets.count - 1) { + var isItemVisible = false + if i == focusedIndex { + isItemVisible = true } - } - } - - if let rightInfoItem = self.rightInfoItem, currentRightInfoItem?.component != rightInfoItem.component { - self.rightInfoItem = nil - if let view = rightInfoItem.view.view { - view.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in - view?.removeFromSuperview() - }) - } - } - - var currentCenterInfoItem: InfoItem? - if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { - if let centerInfoComponent = item.centerInfoComponent { - if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == item.centerInfoComponent { - currentCenterInfoItem = centerInfoItem - } else { - currentCenterInfoItem = InfoItem(component: centerInfoComponent) - } - } - } - - if let centerInfoItem = self.centerInfoItem, currentCenterInfoItem?.component != centerInfoItem.component { - self.centerInfoItem = nil - if let view = centerInfoItem.view.view { - view.removeFromSuperview() - /*view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in - view?.removeFromSuperview() - })*/ - } - } - - if let currentRightInfoItem { - self.rightInfoItem = currentRightInfoItem - - let rightInfoItemSize = currentRightInfoItem.view.update( - transition: .immediate, - component: currentRightInfoItem.component, - environment: {}, - containerSize: CGSize(width: 36.0, height: 36.0) - ) - if let view = currentRightInfoItem.view.view { - var animateIn = false - if view.superview == nil { - self.addSubview(view) - animateIn = true - } - transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 6.0 - rightInfoItemSize.width, y: contentFrame.minY + 14.0), size: rightInfoItemSize)) - if animateIn, !isFirstTime { - view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - view.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - } - } - } - - if let currentCenterInfoItem { - self.centerInfoItem = currentCenterInfoItem - - let centerInfoItemSize = currentCenterInfoItem.view.update( - transition: .immediate, - component: currentCenterInfoItem.component, - environment: {}, - containerSize: CGSize(width: contentFrame.width, height: 44.0) - ) - if let view = currentCenterInfoItem.view.view { - var animateIn = false - if view.superview == nil { - view.isUserInteractionEnabled = false - self.addSubview(view) - animateIn = true - } - transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY + 10.0), size: centerInfoItemSize)) + let itemSet = self.itemSets[i] - if animateIn, !isFirstTime { - //view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if let itemSetPanState = self.itemSetPanState { + if self.visibleItemSetViews[itemSet.id] != nil { + isItemVisible = true + } + if itemSetPanState.fraction < 0.0 && i == focusedIndex - 1 { + isItemVisible = true + } + if itemSetPanState.fraction > 0.0 && i == focusedIndex + 1 { + isItemVisible = true + } } - } - } - - if let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let visibleItem = self.visibleItems[focusedItemId] { - let navigationStripSideInset: CGFloat = 8.0 - let navigationStripTopInset: CGFloat = 8.0 - - let index = currentSlice.items.first(where: { $0.id == self.focusedItemId })?.position ?? 0 - - let _ = self.navigationStrip.update( - transition: transition, - component: AnyComponent(MediaNavigationStripComponent( - index: max(0, min(currentSlice.totalCount - 1 - index, currentSlice.totalCount - 1)), - count: currentSlice.totalCount - )), - environment: { - MediaNavigationStripComponent.EnvironmentType( - currentProgress: visibleItem.currentProgress - ) - }, - containerSize: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0) - ) - if let navigationStripView = self.navigationStrip.view { - if navigationStripView.superview == nil { - navigationStripView.isUserInteractionEnabled = false - self.addSubview(navigationStripView) - } - transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: contentFrame.minX + navigationStripSideInset, y: contentFrame.minY + navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0))) - } - - if let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) { - var items: [StoryActionsComponent.Item] = [] - if !focusedItem.isMy { - items.append(StoryActionsComponent.Item( - kind: .like, - isActivated: focusedItem.hasLike - )) - } - items.append(StoryActionsComponent.Item( - kind: .share, - isActivated: false - )) - let inlineActionsSize = self.inlineActions.update( - transition: transition, - component: AnyComponent(StoryActionsComponent( - items: items, - action: { [weak self] item in - guard let self else { - return + if isItemVisible { + validIds.append(itemSet.id) + + let itemSetView: ItemSetView + var itemSetTransition = transition + if let current = self.visibleItemSetViews[itemSet.id] { + itemSetView = current + } else { + itemSetTransition = .immediate + itemSetView = ItemSetView() + self.visibleItemSetViews[itemSet.id] = itemSetView + } + let _ = itemSetView.view.update( + transition: itemSetTransition, + component: AnyComponent(StoryItemSetContainerComponent( + context: component.context, + externalState: itemSetView.externalState, + initialItemSlice: itemSet, + theme: environment.theme, + strings: environment.strings, + containerInsets: UIEdgeInsets(top: environment.statusBarHeight, left: 0.0, bottom: environment.inputHeight, right: 0.0), + safeInsets: environment.safeInsets, + inputHeight: environment.inputHeight, + isProgressPaused: isProgressPaused || i != focusedIndex, + audioRecorder: i == focusedIndex ? self.audioRecorderValue : nil, + videoRecorder: i == focusedIndex ? self.videoRecorderValue : nil, + presentController: { [weak self] c in + guard let self, let environment = self.environment else { + return + } + if c is UndoOverlayController { + environment.controller()?.present(c, in: .current) + } else { + environment.controller()?.present(c, in: .window(.root)) + } + }, + close: { [weak self] in + guard let self, let environment = self.environment else { + return + } + environment.controller()?.dismiss() + }, + navigateToItemSet: { [weak self] direction in + guard let self, let environment = self.environment else { + return + } + + if let focusedItemSet = self.focusedItemSet, let focusedIndex = self.itemSets.firstIndex(where: { $0.id == focusedItemSet }) { + var switchToIndex = focusedIndex + switch direction { + case .previous: + switchToIndex -= 1 + case .next: + switchToIndex += 1 + } + + switchToIndex = max(0, min(switchToIndex, self.itemSets.count - 1)) + if switchToIndex != focusedIndex { + self.focusedItemSet = self.itemSets[switchToIndex].id + self.state?.updated(transition: .immediate) + } else if switchToIndex == self.itemSets.count - 1 { + environment.controller()?.dismiss() + } + } else { + environment.controller()?.dismiss() + } + }, + controller: { [weak self] in + return self?.environment?.controller() } - self.performInlineAction(item: item) + )), + environment: {}, + containerSize: availableSize + ) + + if i == focusedIndex { + contentDerivedBottomInset = itemSetView.externalState.derivedBottomInset + } + + var itemFrame = CGRect(origin: CGPoint(), size: availableSize) + itemFrame.origin.x += CGFloat(i - focusedIndex) * (availableSize.width + 10.0) + if let itemSetPanState = self.itemSetPanState { + itemFrame.origin.x += -itemSetPanState.fraction * (availableSize.width + 10.0) + } + if let itemSetComponentView = itemSetView.view.view { + if itemSetView.superview == nil { + self.addSubview(itemSetView) } - )), - environment: {}, - containerSize: contentFrame.size - ) - if let inlineActionsView = self.inlineActions.view { - if inlineActionsView.superview == nil { - self.addSubview(inlineActionsView) + if itemSetComponentView.superview == nil { + //itemSetComponentView.layer.zPosition = availableSize.width * 0.5 + itemSetView.addSubview(itemSetComponentView) + } + + itemSetTransition.setPosition(view: itemSetView, position: itemFrame.center) + itemSetTransition.setBounds(view: itemSetView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) + + itemSetTransition.setPosition(view: itemSetComponentView, position: CGRect(origin: CGPoint(), size: itemFrame.size).center) + itemSetTransition.setBounds(view: itemSetComponentView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) + + /*var cubeTransform: CATransform3D + cubeTransform = CATransform3DIdentity + + var perspectiveTransform: CATransform3D = CATransform3DIdentity + let perspectiveDistance: CGFloat = 500.0 + perspectiveTransform.m34 = -1.0 / perspectiveDistance + let _ = perspectiveTransform + //itemSetView.layer.sublayerTransform = perspectiveTransform + + let yRotation: CGFloat = (self.itemSetPanState?.fraction ?? 0.0) * CGFloat.pi * 0.5 + + let rotationTransform = CATransform3DMakeRotation(yRotation, 0.0, 1.0, 0.0) + cubeTransform = CATransform3DConcat(cubeTransform, rotationTransform) + cubeTransform = CATransform3DTranslate(cubeTransform, (self.itemSetPanState?.fraction ?? 0.0) * availableSize.width * 0.5, 0.0, -availableSize.width * 0.5) + + //let transform = CATransform3DTranslate(CATransform3DIdentity, 0.0, 0.0, availableSize.width * 0.5) + + /*let perspectiveDistance: CGFloat = 500.0 + transform.m34 = -1.0 / perspectiveDistance + let cubeSize: CGFloat = availableSize.width + + // Translate the cube to its original position. + let halfSize = cubeSize / 2.0 + let translationTransform = CATransform3DMakeTranslation(0, 0, halfSize) + + // Apply the translation transformation. + transform = CATransform3DConcat(transform, translationTransform)*/ + //itemSetTransition.setTransform(view: itemSetComponentView, transform: transform) + itemSetTransition.setTransform(view: itemSetView, transform: cubeTransform)*/ } - transition.setFrame(view: inlineActionsView, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 10.0 - inlineActionsSize.width, y: contentFrame.maxY - 20.0 - inlineActionsSize.height), size: inlineActionsSize)) - - var inlineActionsAlpha: CGFloat = inputPanelIsOverlay ? 0.0 : 1.0 - if self.audioRecorderValue != nil { - inlineActionsAlpha = 0.0 - } - - transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha) } } } - - let gradientHeight: CGFloat = 74.0 - transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: gradientHeight))) - - let itemLayout = ItemLayout(size: CGSize(width: contentFrame.width, height: availableSize.height - environment.statusBarHeight - 44.0 - bottomContentInsetWithoutInput)) - self.itemLayout = itemLayout - - let inputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize) - if let inputPanelView = self.inputPanel.view { - if inputPanelView.superview == nil { - self.addSubview(inputPanelView) + var removedIds: [AnyHashable] = [] + for (id, itemSetView) in self.visibleItemSetViews { + if !validIds.contains(id) { + removedIds.append(id) + itemSetView.removeFromSuperview() } - transition.setFrame(view: inputPanelView, frame: inputPanelFrame) - transition.setAlpha(view: inputPanelView, alpha: focusedItem?.isMy == true ? 0.0 : 1.0) } - - let footerPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - inputPanelBottomInset - footerPanelSize.height), size: footerPanelSize) - if let footerPanelView = self.footerPanel.view { - if footerPanelView.superview == nil { - self.addSubview(footerPanelView) - } - transition.setFrame(view: footerPanelView, frame: footerPanelFrame) - transition.setAlpha(view: footerPanelView, alpha: focusedItem?.isMy == true ? 1.0 : 0.0) + for id in removedIds { + self.visibleItemSetViews.removeValue(forKey: id) } - let bottomGradientHeight = inputPanelSize.height + 32.0 - transition.setFrame(layer: self.bottomContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: availableSize.height - environment.inputHeight - bottomGradientHeight), size: CGSize(width: contentFrame.width, height: bottomGradientHeight))) - transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: inputPanelIsOverlay ? 1.0 : 0.0) - if let controller = environment.controller() { let subLayout = ContainerViewLayout( size: availableSize, metrics: environment.metrics, deviceMetrics: environment.deviceMetrics, - intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: availableSize.height - min(inputPanelFrame.minY, contentFrame.maxY), right: 0.0), + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: contentDerivedBottomInset, right: 0.0), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, @@ -2571,18 +2221,6 @@ private final class StoryContainerScreenComponent: Component { controller.presentationContext.containerLayoutUpdated(subLayout, transition: transition.containedViewLayoutTransition) } - transition.setFrame(layer: self.contentDimLayer, frame: contentFrame) - transition.setAlpha(layer: self.contentDimLayer, alpha: (inputPanelIsOverlay || self.inputPanelExternalState.isEditing) ? 1.0 : 0.0) - - self.ignoreScrolling = true - transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height))) - let contentSize = availableSize - if contentSize != self.scrollView.contentSize { - self.scrollView.contentSize = contentSize - } - self.ignoreScrolling = false - self.updateScrolling(transition: transition) - return availableSize } } @@ -2602,14 +2240,16 @@ public class StoryContainerScreen: ViewControllerComponentContainer { public init( context: AccountContext, - initialContent: StoryContentItemSlice + initialFocusedId: AnyHashable?, + initialContent: [StoryContentItemSlice] ) { self.context = context super.init(context: context, component: StoryContainerScreenComponent( context: context, + initialFocusedId: initialFocusedId, initialContent: initialContent - ), navigationBarAppearance: .none) + ), navigationBarAppearance: .none, theme: .dark) self.statusBar.statusBarStyle = .White self.navigationPresentation = .flatModal @@ -2660,3 +2300,110 @@ public class StoryContainerScreen: ViewControllerComponentContainer { } } } + +func allowedStoryReactions(context: AccountContext) -> Signal<[ReactionItem], NoError> { + let viewKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudTopReactions) + let topReactions = context.account.postbox.combinedView(keys: [viewKey]) + |> map { views -> [RecentReactionItem] in + guard let view = views.views[viewKey] as? OrderedItemListView else { + return [] + } + return view.items.compactMap { item -> RecentReactionItem? in + return item.contents.get(RecentReactionItem.self) + } + } + + return combineLatest( + context.engine.stickers.availableReactions(), + topReactions + ) + |> take(1) + |> map { availableReactions, topReactions -> [ReactionItem] in + guard let availableReactions = availableReactions else { + return [] + } + + var result: [ReactionItem] = [] + + var existingIds = Set() + + for topReaction in topReactions { + switch topReaction.content { + case let .builtin(value): + if let reaction = availableReactions.reactions.first(where: { $0.value == .builtin(value) }) { + guard let centerAnimation = reaction.centerAnimation else { + continue + } + guard let aroundAnimation = reaction.aroundAnimation else { + continue + } + + if existingIds.contains(reaction.value) { + continue + } + existingIds.insert(reaction.value) + + result.append(ReactionItem( + reaction: ReactionItem.Reaction(rawValue: reaction.value), + appearAnimation: reaction.appearAnimation, + stillAnimation: reaction.selectAnimation, + listAnimation: centerAnimation, + largeListAnimation: reaction.activateAnimation, + applicationAnimation: aroundAnimation, + largeApplicationAnimation: reaction.effectAnimation, + isCustom: false + )) + } else { + continue + } + case let .custom(file): + if existingIds.contains(.custom(file.fileId.id)) { + continue + } + existingIds.insert(.custom(file.fileId.id)) + + result.append(ReactionItem( + reaction: ReactionItem.Reaction(rawValue: .custom(file.fileId.id)), + appearAnimation: file, + stillAnimation: file, + listAnimation: file, + largeListAnimation: file, + applicationAnimation: nil, + largeApplicationAnimation: nil, + isCustom: true + )) + } + } + + for reaction in availableReactions.reactions { + guard let centerAnimation = reaction.centerAnimation else { + continue + } + guard let aroundAnimation = reaction.aroundAnimation else { + continue + } + if !reaction.isEnabled { + continue + } + + if existingIds.contains(reaction.value) { + continue + } + existingIds.insert(reaction.value) + + result.append(ReactionItem( + reaction: ReactionItem.Reaction(rawValue: reaction.value), + appearAnimation: reaction.appearAnimation, + stillAnimation: reaction.selectAnimation, + listAnimation: centerAnimation, + largeListAnimation: reaction.activateAnimation, + applicationAnimation: aroundAnimation, + largeApplicationAnimation: reaction.effectAnimation, + isCustom: false + )) + } + + return result + } +} + diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index 628e6d12cd..771bda5bd5 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -43,6 +43,7 @@ public final class StoryContentItem { public let rightInfoComponent: AnyComponent? public let targetMessageId: EngineMessage.Id? public let preload: Signal? + public let delete: (() -> Void)? public let hasLike: Bool public let isMy: Bool @@ -54,6 +55,7 @@ public final class StoryContentItem { rightInfoComponent: AnyComponent?, targetMessageId: EngineMessage.Id?, preload: Signal?, + delete: (() -> Void)?, hasLike: Bool, isMy: Bool ) { @@ -64,23 +66,27 @@ public final class StoryContentItem { self.rightInfoComponent = rightInfoComponent self.targetMessageId = targetMessageId self.preload = preload + self.delete = delete self.hasLike = hasLike self.isMy = isMy } } public final class StoryContentItemSlice { - public let focusedItemId: AnyHashable + public let id: AnyHashable + public let focusedItemId: AnyHashable? public let items: [StoryContentItem] public let totalCount: Int public let update: (StoryContentItemSlice, AnyHashable) -> Signal public init( - focusedItemId: AnyHashable, + id: AnyHashable, + focusedItemId: AnyHashable?, items: [StoryContentItem], totalCount: Int, update: @escaping (StoryContentItemSlice, AnyHashable) -> Signal ) { + self.id = id self.focusedItemId = focusedItemId self.items = items self.totalCount = totalCount diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift new file mode 100644 index 0000000000..46f11ea7b9 --- /dev/null +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -0,0 +1,1091 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AppBundle +import ComponentDisplayAdapters +import ReactionSelectionNode +import EntityKeyboard +import StoryFooterPanelComponent +import MessageInputPanelComponent +import TelegramPresentationData +import SwiftSignalKit +import AccountContext +import LegacyInstantVideoController +import UndoUI +import ContextUI + +public final class StoryItemSetContainerComponent: Component { + public final class ExternalState { + public fileprivate(set) var derivedBottomInset: CGFloat = 0.0 + + public init() { + } + } + + public enum NavigationDirection { + case previous + case next + } + + public let context: AccountContext + public let externalState: ExternalState + public let initialItemSlice: StoryContentItemSlice + public let theme: PresentationTheme + public let strings: PresentationStrings + public let containerInsets: UIEdgeInsets + public let safeInsets: UIEdgeInsets + public let inputHeight: CGFloat + public let isProgressPaused: Bool + public let audioRecorder: ManagedAudioRecorder? + public let videoRecorder: InstantVideoController? + public let presentController: (ViewController) -> Void + public let close: () -> Void + public let navigateToItemSet: (NavigationDirection) -> Void + public let controller: () -> ViewController? + + public init( + context: AccountContext, + externalState: ExternalState, + initialItemSlice: StoryContentItemSlice, + theme: PresentationTheme, + strings: PresentationStrings, + containerInsets: UIEdgeInsets, + safeInsets: UIEdgeInsets, + inputHeight: CGFloat, + isProgressPaused: Bool, + audioRecorder: ManagedAudioRecorder?, + videoRecorder: InstantVideoController?, + presentController: @escaping (ViewController) -> Void, + close: @escaping () -> Void, + navigateToItemSet: @escaping (NavigationDirection) -> Void, + controller: @escaping () -> ViewController? + ) { + self.context = context + self.externalState = externalState + self.initialItemSlice = initialItemSlice + self.theme = theme + self.strings = strings + self.containerInsets = containerInsets + self.safeInsets = safeInsets + self.inputHeight = inputHeight + self.isProgressPaused = isProgressPaused + self.audioRecorder = audioRecorder + self.videoRecorder = videoRecorder + self.presentController = presentController + self.close = close + self.navigateToItemSet = navigateToItemSet + self.controller = controller + } + + public static func ==(lhs: StoryItemSetContainerComponent, rhs: StoryItemSetContainerComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.initialItemSlice !== rhs.initialItemSlice { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.containerInsets != rhs.containerInsets { + return false + } + if lhs.safeInsets != rhs.safeInsets { + return false + } + if lhs.inputHeight != rhs.inputHeight { + return false + } + if lhs.isProgressPaused != rhs.isProgressPaused { + return false + } + if lhs.audioRecorder !== rhs.audioRecorder { + return false + } + if lhs.videoRecorder !== rhs.videoRecorder { + return false + } + return true + } + + private final class ScrollView: UIScrollView { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } + + private struct ItemLayout { + var size: CGSize + + init(size: CGSize) { + self.size = size + } + } + + private final class VisibleItem { + let externalState = StoryContentItem.ExternalState() + let view = ComponentView() + var currentProgress: Double = 0.0 + var requestedNext: Bool = false + + init() { + } + } + + private final class InfoItem { + let component: AnyComponent + let view = ComponentView() + + init(component: AnyComponent) { + self.component = component + } + } + + public final class View: UIView, UIScrollViewDelegate { + private let scrollView: ScrollView + + private let contentContainerView: UIView + private let topContentGradientLayer: SimpleGradientLayer + private let bottomContentGradientLayer: SimpleGradientLayer + private let contentDimLayer: SimpleLayer + + private let closeButton: HighlightableButton + private let closeButtonIconView: UIImageView + + private let navigationStrip = ComponentView() + private let inlineActions = ComponentView() + + private var centerInfoItem: InfoItem? + private var rightInfoItem: InfoItem? + + private let inputPanel = ComponentView() + private let footerPanel = ComponentView() + private let inputPanelExternalState = MessageInputPanelComponent.ExternalState() + + private var itemLayout: ItemLayout? + private var ignoreScrolling: Bool = false + + private var focusedItemId: AnyHashable? + private var currentSlice: StoryContentItemSlice? + private var currentSliceDisposable: Disposable? + + private var visibleItems: [AnyHashable: VisibleItem] = [:] + + private var preloadContexts: [AnyHashable: Disposable] = [:] + + private var reactionItems: [ReactionItem]? + private var reactionContextNode: ReactionContextNode? + + private weak var actionSheet: ActionSheetController? + private weak var contextController: ContextController? + + private var component: StoryItemSetContainerComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.scrollView = ScrollView() + + self.contentContainerView = UIView() + self.contentContainerView.clipsToBounds = true + + self.topContentGradientLayer = SimpleGradientLayer() + self.bottomContentGradientLayer = SimpleGradientLayer() + self.contentDimLayer = SimpleLayer() + + self.closeButton = HighlightableButton() + self.closeButtonIconView = UIImageView() + + super.init(frame: frame) + + self.backgroundColor = .black + + self.scrollView.delaysContentTouches = false + self.scrollView.canCancelContentTouches = true + self.scrollView.clipsToBounds = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.scrollView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = false + self.scrollView.alwaysBounceVertical = true + self.scrollView.scrollsToTop = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = true + + self.addSubview(self.contentContainerView) + self.layer.addSublayer(self.contentDimLayer) + self.layer.addSublayer(self.topContentGradientLayer) + self.layer.addSublayer(self.bottomContentGradientLayer) + + self.closeButton.addSubview(self.closeButtonIconView) + self.addSubview(self.closeButton) + self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside) + + self.contentContainerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.currentSliceDisposable?.dispose() + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state, let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let currentIndex = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }), let itemLayout = self.itemLayout { + if hasFirstResponder(self) { + self.reactionItems = nil + self.endEditing(true) + } else if self.reactionItems != nil { + self.reactionItems = nil + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) + } else { + let point = recognizer.location(in: self) + + var nextIndex: Int + if point.x < itemLayout.size.width * 0.25 { + nextIndex = currentIndex - 1 + } else { + nextIndex = currentIndex + 1 + } + nextIndex = max(0, min(nextIndex, currentSlice.items.count - 1)) + if nextIndex != currentIndex { + let focusedItemId = currentSlice.items[nextIndex].id + self.focusedItemId = focusedItemId + self.state?.updated(transition: .immediate) + + self.currentSliceDisposable?.dispose() + self.currentSliceDisposable = (currentSlice.update( + currentSlice, + focusedItemId + ) + |> deliverOnMainQueue).start(next: { [weak self] contentSlice in + guard let self else { + return + } + self.currentSlice = contentSlice + self.state?.updated(transition: .immediate) + }) + } else { + if point.x < itemLayout.size.width * 0.25 { + self.component?.navigateToItemSet(.previous) + } else { + self.component?.navigateToItemSet(.next) + } + } + } + } + } + + @objc private func closePressed() { + guard let component = self.component else { + return + } + component.close() + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + self.updateScrolling(transition: .immediate) + } + } + + public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + + private func updateScrolling(transition: Transition) { + guard let component = self.component, let itemLayout = self.itemLayout else { + return + } + + var validIds: [AnyHashable] = [] + if let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) { + validIds.append(focusedItemId) + + var itemTransition = transition + let visibleItem: VisibleItem + if let current = self.visibleItems[focusedItemId] { + visibleItem = current + } else { + itemTransition = .immediate + visibleItem = VisibleItem() + self.visibleItems[focusedItemId] = visibleItem + } + + let _ = visibleItem.view.update( + transition: itemTransition, + component: focusedItem.component, + environment: { + StoryContentItem.Environment( + externalState: visibleItem.externalState, + presentationProgressUpdated: { [weak self, weak visibleItem] progress in + guard let self = self else { + return + } + guard let visibleItem else { + return + } + visibleItem.currentProgress = progress + + if let navigationStripView = self.navigationStrip.view as? MediaNavigationStripComponent.View { + navigationStripView.updateCurrentItemProgress(value: progress, transition: .immediate) + } + if progress >= 1.0 && !visibleItem.requestedNext { + visibleItem.requestedNext = true + + if let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let currentIndex = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }) { + var nextIndex = currentIndex + 1 + nextIndex = max(0, min(nextIndex, currentSlice.items.count - 1)) + if nextIndex != currentIndex { + let focusedItemId = currentSlice.items[nextIndex].id + self.focusedItemId = focusedItemId + self.state?.updated(transition: .immediate) + + self.currentSliceDisposable?.dispose() + self.currentSliceDisposable = (currentSlice.update( + currentSlice, + focusedItemId + ) + |> deliverOnMainQueue).start(next: { [weak self] contentSlice in + guard let self else { + return + } + self.currentSlice = contentSlice + self.state?.updated(transition: .immediate) + }) + } else { + self.component?.navigateToItemSet(.next) + } + } + } + } + ) + }, + containerSize: itemLayout.size + ) + if let view = visibleItem.view.view { + if view.superview == nil { + view.isUserInteractionEnabled = false + self.contentContainerView.addSubview(view) + } + itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size)) + + if let view = view as? StoryContentItem.View { + view.setIsProgressPaused(self.inputPanelExternalState.isEditing || component.isProgressPaused || self.reactionItems != nil || self.actionSheet != nil || self.contextController != nil) + } + } + } + + var removeIds: [AnyHashable] = [] + for (id, visibleItem) in self.visibleItems { + if !validIds.contains(id) { + removeIds.append(id) + if let view = visibleItem.view.view { + view.removeFromSuperview() + } + } + } + for id in removeIds { + self.visibleItems.removeValue(forKey: id) + } + } + + private func updateIsProgressPaused() { + guard let component = self.component else { + return + } + for (_, visibleItem) in self.visibleItems { + if let view = visibleItem.view.view { + if let view = view as? StoryContentItem.View { + view.setIsProgressPaused(self.inputPanelExternalState.isEditing || component.isProgressPaused || self.reactionItems != nil || self.actionSheet != nil || self.contextController != nil) + } + } + } + } + + func update(component: StoryItemSetContainerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let isFirstTime = self.component == nil + + if self.component == nil { + self.focusedItemId = component.initialItemSlice.focusedItemId ?? component.initialItemSlice.items.first?.id + self.currentSlice = component.initialItemSlice + + self.currentSliceDisposable?.dispose() + if let focusedItemId = self.focusedItemId { + self.currentSliceDisposable = (component.initialItemSlice.update( + component.initialItemSlice, + focusedItemId + ) + |> deliverOnMainQueue).start(next: { [weak self] contentSlice in + guard let self else { + return + } + self.currentSlice = contentSlice + self.state?.updated(transition: .immediate) + }) + } + } + + if self.topContentGradientLayer.colors == nil { + var locations: [NSNumber] = [] + var colors: [CGColor] = [] + let numStops = 4 + let baseAlpha: CGFloat = 0.5 + for i in 0 ..< numStops { + let step = 1.0 - CGFloat(i) / CGFloat(numStops - 1) + locations.append((1.0 - step) as NSNumber) + let alphaStep: CGFloat = pow(step, 1.5) + colors.append(UIColor.black.withAlphaComponent(alphaStep * baseAlpha).cgColor) + } + + self.topContentGradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) + self.topContentGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) + + self.topContentGradientLayer.locations = locations + self.topContentGradientLayer.colors = colors + self.topContentGradientLayer.type = .axial + } + if self.bottomContentGradientLayer.colors == nil { + var locations: [NSNumber] = [] + var colors: [CGColor] = [] + let numStops = 10 + let baseAlpha: CGFloat = 0.7 + for i in 0 ..< numStops { + let step = 1.0 - CGFloat(i) / CGFloat(numStops - 1) + locations.append((1.0 - step) as NSNumber) + let alphaStep: CGFloat = pow(step, 1.5) + colors.append(UIColor.black.withAlphaComponent(alphaStep * baseAlpha).cgColor) + } + + self.bottomContentGradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) + self.bottomContentGradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0) + + self.bottomContentGradientLayer.locations = locations + self.bottomContentGradientLayer.colors = colors + self.bottomContentGradientLayer.type = .axial + + self.contentDimLayer.backgroundColor = UIColor(white: 0.0, alpha: 0.3).cgColor + } + + if let focusedItemId = self.focusedItemId { + if let currentSlice = self.currentSlice { + if !currentSlice.items.contains(where: { $0.id == focusedItemId }) { + self.focusedItemId = currentSlice.items.first?.id + } + } else { + self.focusedItemId = nil + } + } + + //self.updatePreloads() + + self.component = component + self.state = state + + var bottomContentInset: CGFloat + if !component.safeInsets.bottom.isZero { + bottomContentInset = component.safeInsets.bottom + 5.0 + } else { + bottomContentInset = 0.0 + } + + self.inputPanel.parentState = state + let inputPanelSize = self.inputPanel.update( + transition: transition, + component: AnyComponent(MessageInputPanelComponent( + externalState: self.inputPanelExternalState, + context: component.context, + theme: component.theme, + strings: component.strings, + presentController: { [weak self] c in + guard let self, let component = self.component else { + return + } + component.presentController(c) + }, + sendMessageAction: { [weak self] in + guard let self else { + return + } + let _ = self + //self.performSendMessageAction() + }, + setMediaRecordingActive: { [weak self] isActive, isVideo, sendAction in + guard let self else { + return + } + let _ = self + //self.setMediaRecordingActive(isActive: isActive, isVideo: isVideo, sendAction: sendAction) + }, + attachmentAction: { [weak self] in + guard let self else { + return + } + let _ = self + //self.presentAttachmentMenu(subject: .default) + }, + reactionAction: { [weak self] sourceView in + guard let self, let component = self.component else { + return + } + + let _ = (allowedStoryReactions(context: component.context) + |> deliverOnMainQueue).start(next: { [weak self] reactionItems in + guard let self, let component = self.component else { + return + } + + component.controller()?.forEachController { c in + if let c = c as? UndoOverlayController { + c.dismiss() + } + return true + } + + self.reactionItems = reactionItems + self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + }) + }, + audioRecorder: component.audioRecorder, + videoRecordingStatus: component.videoRecorder?.audioStatus + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 200.0) + ) + + let footerPanelSize = self.footerPanel.update( + transition: transition, + component: AnyComponent(StoryFooterPanelComponent( + deleteAction: { [weak self] in + guard let self, let component = self.component, let focusedItemId = self.focusedItemId else { + return + } + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + let actionSheet = ActionSheetController(presentationData: presentationData) + + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + + guard let self, let component = self.component else { + return + } + + if let currentSlice = self.currentSlice, let index = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }) { + let item = currentSlice.items[index] + + if currentSlice.items.count == 1 { + component.navigateToItemSet(.next) + } else { + var nextIndex: Int = index + 1 + if nextIndex >= currentSlice.items.count { + nextIndex = currentSlice.items.count - 1 + } + self.focusedItemId = currentSlice.items[nextIndex].id + + /*var updatedItems: [StoryContentItem] = [] + for item in currentSlice.items { + if item.id != focusedItemId { + updatedItems.append(StoryContentItem( + id: item.id, + position: updatedItems.count, + component: item.component, + centerInfoComponent: item.centerInfoComponent, + rightInfoComponent: item.rightInfoComponent, + targetMessageId: item.targetMessageId, + preload: item.preload, + delete: item.delete, + hasLike: item.hasLike, + isMy: item.isMy + )) + } + }*/ + + /*self.currentSlice = StoryContentItemSlice( + id: currentSlice.id, + focusedItemId: nil, + items: updatedItems, + totalCount: currentSlice.totalCount - 1, + update: currentSlice.update + )*/ + self.state?.updated(transition: .immediate) + } + + item.delete?() + } + }) + ]), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ]) + ]) + + actionSheet.dismissed = { [weak self] _ in + guard let self else { + return + } + self.actionSheet = nil + self.updateIsProgressPaused() + } + self.actionSheet = actionSheet + self.updateIsProgressPaused() + + component.presentController(actionSheet) + }, + moreAction: { [weak self] sourceView, gesture in + guard let self, let component = self.component, let controller = component.controller() else { + return + } + + var items: [ContextMenuItem] = [] + + items.append(.action(ContextMenuActionItem(text: "Who can see", textLayout: .secondLineWithValue("Everyone"), icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Channels"), color: theme.contextMenu.primaryColor) + }, action: { _, a in + a(.default) + }))) + + items.append(.separator) + + items.append(.action(ContextMenuActionItem(text: "Save to profile", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + a(.default) + + guard let self, let component = self.component else { + return + } + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + self.component?.presentController(UndoOverlayController( + presentationData: presentationData, + content: .info(title: "Story saved to your profile", text: "Saved stories can be viewed by others on your profile until you remove them.", timeout: nil), + elevatedLayout: false, + animateInAsReplacement: false, + action: { _ in return false } + )) + }))) + items.append(.action(ContextMenuActionItem(text: "Save image", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) + }, action: { _, a in + a(.default) + }))) + items.append(.action(ContextMenuActionItem(text: "Copy link", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) + }, action: { _, a in + a(.default) + }))) + items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) + }, action: { _, a in + a(.default) + }))) + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.dismissed = { [weak self] in + guard let self else { + return + } + self.contextController = nil + self.updateIsProgressPaused() + } + self.contextController = contextController + self.updateIsProgressPaused() + controller.presentInGlobalOverlay(contextController) + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 200.0) + ) + + let bottomContentInsetWithoutInput = bottomContentInset + + let inputPanelBottomInset: CGFloat + let inputPanelIsOverlay: Bool + if component.inputHeight < bottomContentInset + inputPanelSize.height { + inputPanelBottomInset = bottomContentInset + bottomContentInset += inputPanelSize.height + inputPanelIsOverlay = false + } else { + bottomContentInset += 44.0 + inputPanelBottomInset = component.inputHeight + inputPanelIsOverlay = true + } + + let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: component.containerInsets.top), size: CGSize(width: availableSize.width, height: availableSize.height - component.containerInsets.top - bottomContentInset)) + transition.setFrame(view: self.contentContainerView, frame: contentFrame) + transition.setCornerRadius(layer: self.contentContainerView.layer, cornerRadius: 14.0) + + if self.closeButtonIconView.image == nil { + self.closeButtonIconView.image = UIImage(bundleImageName: "Media Gallery/Close")?.withRenderingMode(.alwaysTemplate) + self.closeButtonIconView.tintColor = .white + } + if let image = self.closeButtonIconView.image { + let closeButtonFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: 50.0, height: 64.0)) + transition.setFrame(view: self.closeButton, frame: closeButtonFrame) + transition.setFrame(view: self.closeButtonIconView, frame: CGRect(origin: CGPoint(x: floor((closeButtonFrame.width - image.size.width) * 0.5), y: floor((closeButtonFrame.height - image.size.height) * 0.5)), size: image.size)) + } + + var focusedItem: StoryContentItem? + if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { + focusedItem = item + } + + var currentRightInfoItem: InfoItem? + if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { + if let rightInfoComponent = item.rightInfoComponent { + if let rightInfoItem = self.rightInfoItem, rightInfoItem.component == item.rightInfoComponent { + currentRightInfoItem = rightInfoItem + } else { + currentRightInfoItem = InfoItem(component: rightInfoComponent) + } + } + } + + if let rightInfoItem = self.rightInfoItem, currentRightInfoItem?.component != rightInfoItem.component { + self.rightInfoItem = nil + if let view = rightInfoItem.view.view { + view.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in + view?.removeFromSuperview() + }) + } + } + + var currentCenterInfoItem: InfoItem? + if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) { + if let centerInfoComponent = item.centerInfoComponent { + if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == item.centerInfoComponent { + currentCenterInfoItem = centerInfoItem + } else { + currentCenterInfoItem = InfoItem(component: centerInfoComponent) + } + } + } + + if let centerInfoItem = self.centerInfoItem, currentCenterInfoItem?.component != centerInfoItem.component { + self.centerInfoItem = nil + if let view = centerInfoItem.view.view { + view.removeFromSuperview() + /*view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in + view?.removeFromSuperview() + })*/ + } + } + + if let currentRightInfoItem { + self.rightInfoItem = currentRightInfoItem + + let rightInfoItemSize = currentRightInfoItem.view.update( + transition: .immediate, + component: currentRightInfoItem.component, + environment: {}, + containerSize: CGSize(width: 36.0, height: 36.0) + ) + if let view = currentRightInfoItem.view.view { + var animateIn = false + if view.superview == nil { + self.addSubview(view) + animateIn = true + } + transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 6.0 - rightInfoItemSize.width, y: contentFrame.minY + 14.0), size: rightInfoItemSize)) + + if animateIn, !isFirstTime { + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + view.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + } + } + } + + if let currentCenterInfoItem { + self.centerInfoItem = currentCenterInfoItem + + let centerInfoItemSize = currentCenterInfoItem.view.update( + transition: .immediate, + component: currentCenterInfoItem.component, + environment: {}, + containerSize: CGSize(width: contentFrame.width, height: 44.0) + ) + if let view = currentCenterInfoItem.view.view { + var animateIn = false + if view.superview == nil { + view.isUserInteractionEnabled = false + self.addSubview(view) + animateIn = true + } + transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY + 10.0), size: centerInfoItemSize)) + + if animateIn, !isFirstTime { + //view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + } + + let gradientHeight: CGFloat = 74.0 + transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: gradientHeight))) + + let itemLayout = ItemLayout(size: CGSize(width: contentFrame.width, height: availableSize.height - component.containerInsets.top - 44.0 - bottomContentInsetWithoutInput)) + self.itemLayout = itemLayout + + let inputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize) + if let inputPanelView = self.inputPanel.view { + if inputPanelView.superview == nil { + self.addSubview(inputPanelView) + } + transition.setFrame(view: inputPanelView, frame: inputPanelFrame) + transition.setAlpha(view: inputPanelView, alpha: focusedItem?.isMy == true ? 0.0 : 1.0) + } + + if let reactionItems = self.reactionItems { + let reactionContextNode: ReactionContextNode + var reactionContextNodeTransition = transition + if let current = self.reactionContextNode { + reactionContextNode = current + } else { + reactionContextNodeTransition = .immediate + reactionContextNode = ReactionContextNode( + context: component.context, + animationCache: component.context.animationCache, + presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme), + items: reactionItems.map(ReactionContextItem.reaction), + selectedItems: Set(), + getEmojiContent: { [weak self] animationCache, animationRenderer in + guard let self, let component = self.component else { + preconditionFailure() + } + + let mappedReactionItems: [EmojiComponentReactionItem] = reactionItems.map { reaction -> EmojiComponentReactionItem in + return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) + } + + return EmojiPagerContentComponent.emojiInputData( + context: component.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + isStandalone: false, + isStatusSelection: false, + isReactionSelection: true, + isEmojiSelection: false, + hasTrending: false, + topReactionItems: mappedReactionItems, + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: component.context.account.peerId, + selectedItems: Set() + ) + }, + isExpandedUpdated: { [weak self] transition in + guard let self else { + return + } + self.state?.updated(transition: Transition(transition)) + }, + requestLayout: { [weak self] transition in + guard let self else { + return + } + self.state?.updated(transition: Transition(transition)) + }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let self else { + return + } + self.state?.updated(transition: Transition(transition)) + } + ) + self.reactionContextNode = reactionContextNode + + reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in + guard let self, let component = self.component else { + return + } + self.reactionItems = nil + self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + component.presentController(UndoOverlayController( + presentationData: presentationData, + content: .succeed(text: "Reaction Sent"), + elevatedLayout: false, + animateInAsReplacement: false, + action: { _ in return false } + )) + } + } + + var animateReactionsIn = false + if reactionContextNode.view.superview == nil { + animateReactionsIn = true + self.addSubnode(reactionContextNode) + } + + let anchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 44.0 - 32.0, y: inputPanelFrame.minY), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0) + reactionContextNodeTransition.setFrame(view: reactionContextNode.view, frame: CGRect(origin: CGPoint(), size: availableSize)) + reactionContextNode.updateLayout(size: availableSize, insets: UIEdgeInsets(), anchorRect: anchorRect, isCoveredByInput: false, isAnimatingOut: false, transition: reactionContextNodeTransition.containedViewLayoutTransition) + + if animateReactionsIn { + reactionContextNode.animateIn(from: anchorRect) + } + } else { + if let reactionContextNode = self.reactionContextNode { + self.reactionContextNode = nil + transition.setAlpha(view: reactionContextNode.view, alpha: 0.0, completion: { [weak reactionContextNode] _ in + reactionContextNode?.view.removeFromSuperview() + }) + } + } + + let footerPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - inputPanelBottomInset - footerPanelSize.height), size: footerPanelSize) + if let footerPanelView = self.footerPanel.view { + if footerPanelView.superview == nil { + self.addSubview(footerPanelView) + } + transition.setFrame(view: footerPanelView, frame: footerPanelFrame) + transition.setAlpha(view: footerPanelView, alpha: focusedItem?.isMy == true ? 1.0 : 0.0) + } + + let bottomGradientHeight = inputPanelSize.height + 32.0 + transition.setFrame(layer: self.bottomContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: availableSize.height - component.inputHeight - bottomGradientHeight), size: CGSize(width: contentFrame.width, height: bottomGradientHeight))) + transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: inputPanelIsOverlay ? 1.0 : 0.0) + + transition.setFrame(layer: self.contentDimLayer, frame: contentFrame) + transition.setAlpha(layer: self.contentDimLayer, alpha: (inputPanelIsOverlay || self.inputPanelExternalState.isEditing) ? 1.0 : 0.0) + + self.ignoreScrolling = true + transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height))) + let contentSize = availableSize + if contentSize != self.scrollView.contentSize { + self.scrollView.contentSize = contentSize + } + self.ignoreScrolling = false + self.updateScrolling(transition: transition) + + if let currentSlice = self.currentSlice, let focusedItemId = self.focusedItemId, let visibleItem = self.visibleItems[focusedItemId] { + let navigationStripSideInset: CGFloat = 8.0 + let navigationStripTopInset: CGFloat = 8.0 + + let index = currentSlice.items.first(where: { $0.id == self.focusedItemId })?.position ?? 0 + + let _ = self.navigationStrip.update( + transition: transition, + component: AnyComponent(MediaNavigationStripComponent( + index: max(0, min(index, currentSlice.totalCount - 1)), + count: currentSlice.totalCount + )), + environment: { + MediaNavigationStripComponent.EnvironmentType( + currentProgress: visibleItem.currentProgress + ) + }, + containerSize: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0) + ) + if let navigationStripView = self.navigationStrip.view { + if navigationStripView.superview == nil { + navigationStripView.isUserInteractionEnabled = false + self.addSubview(navigationStripView) + } + transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: contentFrame.minX + navigationStripSideInset, y: contentFrame.minY + navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0))) + } + + if let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) { + var items: [StoryActionsComponent.Item] = [] + let _ = focusedItem + /*if !focusedItem.isMy { + items.append(StoryActionsComponent.Item( + kind: .like, + isActivated: focusedItem.hasLike + )) + }*/ + items.append(StoryActionsComponent.Item( + kind: .share, + isActivated: false + )) + + let inlineActionsSize = self.inlineActions.update( + transition: transition, + component: AnyComponent(StoryActionsComponent( + items: items, + action: { [weak self] item in + guard let self else { + return + } + let _ = self + //self.performInlineAction(item: item) + } + )), + environment: {}, + containerSize: contentFrame.size + ) + if let inlineActionsView = self.inlineActions.view { + if inlineActionsView.superview == nil { + self.addSubview(inlineActionsView) + } + transition.setFrame(view: inlineActionsView, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 10.0 - inlineActionsSize.width, y: contentFrame.maxY - 20.0 - inlineActionsSize.height), size: inlineActionsSize)) + + var inlineActionsAlpha: CGFloat = inputPanelIsOverlay ? 0.0 : 1.0 + if component.audioRecorder != nil || component.videoRecorder != nil { + inlineActionsAlpha = 0.0 + } + if self.reactionItems != nil { + inlineActionsAlpha = 0.0 + } + + transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha) + } + } + } + + component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrame.minY, contentFrame.maxY) + + return contentSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceView: UIView + var keepInPlace: Bool { + return true + } + + init(controller: ViewController, sourceView: UIView) { + self.controller = controller + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryAuthorInfoComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryAuthorInfoComponent.swift index 914b7f2375..d58df3f96f 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryAuthorInfoComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryAuthorInfoComponent.swift @@ -8,20 +8,29 @@ import TelegramStringFormatting final class StoryAuthorInfoComponent: Component { let context: AccountContext - let message: EngineMessage + let peer: EnginePeer? + let timestamp: Int32 + + init(context: AccountContext, peer: EnginePeer?, timestamp: Int32) { + self.context = context + self.peer = peer + self.timestamp = timestamp + } - init(context: AccountContext, message: EngineMessage) { - self.context = context - self.message = message + convenience init(context: AccountContext, message: EngineMessage) { + self.init(context: context, peer: message.author, timestamp: message.timestamp) } static func ==(lhs: StoryAuthorInfoComponent, rhs: StoryAuthorInfoComponent) -> Bool { if lhs.context !== rhs.context { return false } - if lhs.message != rhs.message { + if lhs.peer != rhs.peer { return false } + if lhs.timestamp != rhs.timestamp { + return false + } return true } @@ -49,8 +58,8 @@ final class StoryAuthorInfoComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) - let title = component.message.author?.debugDisplayTitle ?? "" - let subtitle = humanReadableStringForTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: component.message.timestamp).string + let title = component.peer?.debugDisplayTitle ?? "" + let subtitle = humanReadableStringForTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: component.timestamp).string let titleSize = self.title.update( transition: .immediate, diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift index 741761fd8f..61260b85d0 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift @@ -8,7 +8,7 @@ import TelegramCore import StoryContainerScreen public enum StoryChatContent { - public static func messages( + /*public static func messages( context: AccountContext, messageId: EngineMessage.Id ) -> Signal { @@ -70,6 +70,7 @@ public enum StoryChatContent { )) } return StoryContentItemSlice( + id: AnyHashable(entry.) focusedItemId: AnyHashable(messageId), items: items, totalCount: totalCount, @@ -88,5 +89,73 @@ public enum StoryChatContent { } ) } - } + }*/ + + public static func stories(context: AccountContext, storyList: StoryListContext, focusItem: Int64?) -> Signal<[StoryContentItemSlice], NoError> { + return storyList.state + |> map { state -> [StoryContentItemSlice] in + var itemSlices: [StoryContentItemSlice] = [] + + for itemSet in state.itemSets { + var items: [StoryContentItem] = [] + + for item in itemSet.items { + items.append(StoryContentItem( + id: AnyHashable(item.id), + position: items.count, + component: AnyComponent(StoryItemContentComponent( + context: context, + item: item + )), + centerInfoComponent: AnyComponent(StoryAuthorInfoComponent( + context: context, + peer: itemSet.peer, + timestamp: item.timestamp + )), + rightInfoComponent: itemSet.peer.flatMap { author -> AnyComponent in + return AnyComponent(StoryAvatarInfoComponent( + context: context, + peer: author + )) + }, + targetMessageId: nil, + preload: nil, + delete: { [weak storyList] in + storyList?.delete(id: item.id) + }, + hasLike: false, + isMy: itemSet.peerId == context.account.peerId + )) + } + + var sliceFocusedItemId: AnyHashable? + if let focusItem, items.contains(where: { ($0.id.base as? Int64) == focusItem }) { + sliceFocusedItemId = AnyHashable(focusItem) + } + + itemSlices.append(StoryContentItemSlice( + id: AnyHashable(itemSet.peerId), + focusedItemId: sliceFocusedItemId, + items: items, + totalCount: items.count, + update: { requestedItemSet, itemId in + var focusItem: Int64? + if let id = itemId.base as? Int64 { + focusItem = id + } + return StoryChatContent.stories(context: context, storyList: storyList, focusItem: focusItem) + |> mapToSignal { result -> Signal in + if let foundItemSet = result.first(where: { $0.id == requestedItemSet.id }) { + return .single(foundItemSet) + } else { + return .never() + } + } + } + )) + } + + return itemSlices + } + } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryItemContentComponent.swift new file mode 100644 index 0000000000..40f619a2f4 --- /dev/null +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryItemContentComponent.swift @@ -0,0 +1,419 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AccountContext +import TelegramCore +import AsyncDisplayKit +import PhotoResources +import SwiftSignalKit +import UniversalMediaPlayer +import TelegramUniversalVideoContent +import StoryContainerScreen +import HierarchyTrackingLayer + +final class StoryItemContentComponent: Component { + typealias EnvironmentType = StoryContentItem.Environment + + let context: AccountContext + let item: StoryListContext.Item + + init(context: AccountContext, item: StoryListContext.Item) { + self.context = context + self.item = item + } + + static func ==(lhs: StoryItemContentComponent, rhs: StoryItemContentComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.item != rhs.item { + return false + } + return true + } + + /*static func preload(context: AccountContext, message: EngineMessage) -> Signal { + var messageMedia: EngineMedia? + for media in message.media { + switch media { + case let image as TelegramMediaImage: + messageMedia = .image(image) + case let file as TelegramMediaFile: + messageMedia = .file(file) + default: + break + } + } + + guard let messageMedia else { + return .complete() + } + + var fetchSignal: Signal? + switch messageMedia { + case let .image(image): + if let representation = image.representations.last { + fetchSignal = fetchedMediaResource( + mediaBox: context.account.postbox.mediaBox, + userLocation: .peer(message.id.peerId), + userContentType: .image, + reference: ImageMediaReference.message(message: MessageReference(message._asMessage()), media: image).resourceReference(representation.resource) + ) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + } + case let .file(file): + fetchSignal = fetchedMediaResource( + mediaBox: context.account.postbox.mediaBox, + userLocation: .peer(message.id.peerId), + userContentType: .image, + reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource) + ) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + default: + break + } + + return fetchSignal ?? .complete() + }*/ + + final class View: StoryContentItem.View { + private let imageNode: TransformImageNode + private var videoNode: UniversalVideoNode? + + private var currentMessageMedia: EngineMedia? + private var fetchDisposable: Disposable? + + private var component: StoryItemContentComponent? + private weak var state: EmptyComponentState? + private var environment: StoryContentItem.Environment? + + private var isProgressPaused: Bool = false + private var currentProgressTimer: SwiftSignalKit.Timer? + private var currentProgressTimerValue: Double = 0.0 + private var videoProgressDisposable: Disposable? + + private var videoPlaybackStatus: MediaPlayerStatus? + + private let hierarchyTrackingLayer: HierarchyTrackingLayer + + override init(frame: CGRect) { + self.hierarchyTrackingLayer = HierarchyTrackingLayer() + self.imageNode = TransformImageNode() + + super.init(frame: frame) + + self.layer.addSublayer(self.hierarchyTrackingLayer) + + self.addSubnode(self.imageNode) + + self.hierarchyTrackingLayer.isInHierarchyUpdated = { [weak self] value in + guard let self else { + return + } + self.updateIsProgressPaused() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.fetchDisposable?.dispose() + self.currentProgressTimer?.invalidate() + self.videoProgressDisposable?.dispose() + } + + private func performActionAfterImageContentLoaded(update: Bool) { + guard let component = self.component, let currentMessageMedia = self.currentMessageMedia else { + return + } + + if case let .file(file) = currentMessageMedia { + if self.videoNode == nil { + let videoNode = UniversalVideoNode( + postbox: component.context.account.postbox, + audioSession: component.context.sharedContext.mediaManager.audioSession, + manager: component.context.sharedContext.mediaManager.universalVideoManager, + decoration: StoryVideoDecoration(), + content: NativeVideoContent( + id: .message(0, file.fileId), + userLocation: .other, + fileReference: .standalone(media: file), + imageReference: nil, + loopVideo: true, + enableSound: true, + tempFilePath: nil, + captureProtected: false, + storeAfterDownload: nil + ), + priority: .gallery + ) + + self.videoNode = videoNode + self.addSubnode(videoNode) + + videoNode.ownsContentNodeUpdated = { [weak self] value in + guard let self else { + return + } + if value { + self.videoNode?.seek(0.0) + self.videoNode?.playOnceWithSound(playAndRecord: false) + } + } + videoNode.canAttachContent = true + if update { + self.state?.updated(transition: .immediate) + } + } + } + } + + override func setIsProgressPaused(_ isProgressPaused: Bool) { + if self.isProgressPaused != isProgressPaused { + self.isProgressPaused = isProgressPaused + self.updateIsProgressPaused() + } + } + + private func updateIsProgressPaused() { + if let videoNode = self.videoNode { + if !self.isProgressPaused && self.hierarchyTrackingLayer.isInHierarchy { + videoNode.play() + } else { + videoNode.pause() + } + } + + self.updateVideoPlaybackProgress() + self.updateProgressTimer() + } + + private func updateProgressTimer() { + let needsTimer = !self.isProgressPaused && self.hierarchyTrackingLayer.isInHierarchy + + if needsTimer { + if self.currentProgressTimer == nil { + self.currentProgressTimer = SwiftSignalKit.Timer( + timeout: 1.0 / 60.0, + repeat: true, + completion: { [weak self] in + guard let self, !self.isProgressPaused, self.hierarchyTrackingLayer.isInHierarchy else { + return + } + + if self.videoNode != nil { + self.updateVideoPlaybackProgress() + } else { + let currentProgressTimerLimit: Double = 5.0 + var currentProgressTimerValue = self.currentProgressTimerValue + 1.0 / 60.0 + currentProgressTimerValue = max(0.0, min(currentProgressTimerLimit, currentProgressTimerValue)) + self.currentProgressTimerValue = currentProgressTimerValue + + self.environment?.presentationProgressUpdated(currentProgressTimerValue / currentProgressTimerLimit) + } + }, queue: .mainQueue() + ) + self.currentProgressTimer?.start() + } + } else { + if let currentProgressTimer = self.currentProgressTimer { + self.currentProgressTimer = nil + currentProgressTimer.invalidate() + } + } + } + + private func updateVideoPlaybackProgress() { + guard let videoPlaybackStatus = self.videoPlaybackStatus else { + return + } + var isPlaying = false + var timestampAndDuration: (timestamp: Double?, duration: Double)? + switch videoPlaybackStatus.status { + case .playing: + isPlaying = true + default: + break + } + if case .buffering(true, _, _, _) = videoPlaybackStatus.status { + timestampAndDuration = (nil, videoPlaybackStatus.duration) + } else if Double(0.0).isLess(than: videoPlaybackStatus.duration) { + timestampAndDuration = (videoPlaybackStatus.timestamp, videoPlaybackStatus.duration) + } + + var currentProgress: Double = 0.0 + + if let (maybeTimestamp, duration) = timestampAndDuration, let timestamp = maybeTimestamp, duration > 0.01, let videoPlaybackStatus = self.videoPlaybackStatus { + var actualTimestamp: Double + if videoPlaybackStatus.generationTimestamp.isZero || !isPlaying { + actualTimestamp = timestamp + } else { + let currentTimestamp = CACurrentMediaTime() + actualTimestamp = timestamp + (currentTimestamp - videoPlaybackStatus.generationTimestamp) * videoPlaybackStatus.baseRate + } + + var progress = CGFloat(actualTimestamp / duration) + if progress.isNaN || !progress.isFinite { + progress = 0.0 + } + progress = min(1.0, progress) + + currentProgress = progress + } + + let clippedProgress = max(0.0, min(1.0, currentProgress)) + self.environment?.presentationProgressUpdated(clippedProgress) + } + + func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + self.environment = environment[StoryContentItem.Environment.self].value + + var messageMedia: EngineMedia? + switch component.item.media { + case let .image(image): + messageMedia = .image(image) + case let .file(file): + messageMedia = .file(file) + default: + break + } + + var reloadMedia = false + if self.currentMessageMedia?.id != messageMedia?.id { + self.currentMessageMedia = messageMedia + reloadMedia = true + } + + if reloadMedia, let messageMedia { + var signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? + var fetchSignal: Signal? + switch messageMedia { + case let .image(image): + signal = chatMessagePhoto( + postbox: component.context.account.postbox, + userLocation: .other, + photoReference: .standalone(media: image), + synchronousLoad: true, + highQuality: true + ) + if let representation = image.representations.last { + fetchSignal = fetchedMediaResource( + mediaBox: component.context.account.postbox.mediaBox, + userLocation: .other, + userContentType: .image, + reference: ImageMediaReference.standalone(media: image).resourceReference(representation.resource) + ) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + } + case let .file(file): + signal = chatMessageVideo( + postbox: component.context.account.postbox, + userLocation: .other, + videoReference: .standalone(media: file), + synchronousLoad: true + ) + fetchSignal = fetchedMediaResource( + mediaBox: component.context.account.postbox.mediaBox, + userLocation: .other, + userContentType: .image, + reference: FileMediaReference.standalone(media: file).resourceReference(file.resource) + ) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + default: + break + } + + if let signal { + var wasSynchronous = true + self.imageNode.setSignal(signal |> afterCompleted { [weak self] in + Queue.mainQueue().async { + guard let self else { + return + } + + self.performActionAfterImageContentLoaded(update: !wasSynchronous) + } + }, attemptSynchronously: true) + wasSynchronous = false + } + + self.fetchDisposable?.dispose() + self.fetchDisposable = nil + if let fetchSignal { + self.fetchDisposable = fetchSignal.start() + } + } + + if let messageMedia { + var dimensions: CGSize? + switch messageMedia { + case let .image(image): + dimensions = image.representations.last?.dimensions.cgSize + case let .file(file): + dimensions = file.dimensions?.cgSize + default: + break + } + + if let dimensions { + let apply = self.imageNode.asyncLayout()(TransformImageArguments( + corners: ImageCorners(), + imageSize: dimensions.aspectFilled(availableSize), + boundingSize: availableSize, + intrinsicInsets: UIEdgeInsets() + )) + apply() + + if let videoNode = self.videoNode { + let videoSize = dimensions.aspectFilled(availableSize) + videoNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) * 0.5), y: floor((availableSize.height - videoSize.height) * 0.5)), size: videoSize) + videoNode.updateLayout(size: videoSize, transition: .immediate) + } + } + self.imageNode.frame = CGRect(origin: CGPoint(), size: availableSize) + } + + if let videoNode = self.videoNode { + if self.videoProgressDisposable == nil { + self.videoProgressDisposable = (videoNode.status + |> deliverOnMainQueue).start(next: { [weak self] status in + guard let self, let status else { + return + } + + self.videoPlaybackStatus = status + self.updateVideoPlaybackProgress() + }) + } + } + self.updateProgressTimer() + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryMessageContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryMessageContentComponent.swift index 7cedad7d43..efb2eaf3f4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryMessageContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryMessageContentComponent.swift @@ -233,20 +233,21 @@ final class StoryMessageContentComponent: Component { } private func updateVideoPlaybackProgress() { + guard let videoPlaybackStatus = self.videoPlaybackStatus else { + return + } var isPlaying = false var timestampAndDuration: (timestamp: Double?, duration: Double)? - if let videoPlaybackStatus = self.videoPlaybackStatus { - switch videoPlaybackStatus.status { - case .playing: - isPlaying = true - default: - break - } - if case .buffering(true, _, _, _) = videoPlaybackStatus.status { - timestampAndDuration = (nil, videoPlaybackStatus.duration) - } else if Double(0.0).isLess(than: videoPlaybackStatus.duration) { - timestampAndDuration = (videoPlaybackStatus.timestamp, videoPlaybackStatus.duration) - } + switch videoPlaybackStatus.status { + case .playing: + isPlaying = true + default: + break + } + if case .buffering(true, _, _, _) = videoPlaybackStatus.status { + timestampAndDuration = (nil, videoPlaybackStatus.duration) + } else if Double(0.0).isLess(than: videoPlaybackStatus.duration) { + timestampAndDuration = (videoPlaybackStatus.timestamp, videoPlaybackStatus.duration) } var currentProgress: Double = 0.0 diff --git a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/BUILD b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/BUILD index a1e64e32ef..ebdf475252 100644 --- a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/BUILD +++ b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/BUILD @@ -14,6 +14,7 @@ swift_library( "//submodules/ComponentFlow", "//submodules/AppBundle", "//submodules/Components/BundleIconComponent", + "//submodules/TelegramUI/Components/ChatListHeaderComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift index b1ff6892ed..dad8b06200 100644 --- a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift @@ -4,10 +4,18 @@ import Display import ComponentFlow import AppBundle import BundleIconComponent +import ChatListHeaderComponent public final class StoryFooterPanelComponent: Component { + public let deleteAction: () -> Void + public let moreAction: (UIView, ContextGesture?) -> Void + public init( + deleteAction: @escaping () -> Void, + moreAction: @escaping (UIView, ContextGesture?) -> Void ) { + self.deleteAction = deleteAction + self.moreAction = moreAction } public static func ==(lhs: StoryFooterPanelComponent, rhs: StoryFooterPanelComponent) -> Bool { @@ -17,7 +25,7 @@ public final class StoryFooterPanelComponent: Component { public final class View: UIView { private let viewStatsText = ComponentView() private let deleteButton = ComponentView() - private let moreButton = ComponentView() + private var moreButton: MoreHeaderButton? private var component: StoryFooterPanelComponent? private weak var state: EmptyComponentState? @@ -31,9 +39,89 @@ public final class StoryFooterPanelComponent: Component { } func update(component: StoryFooterPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + let baseHeight: CGFloat = 44.0 let size = CGSize(width: availableSize.width, height: baseHeight) + let viewStatsTextSize = self.viewStatsText.update( + transition: .immediate, + component: AnyComponent(Text(text: "No views yet", font: Font.regular(15.0), color: .white)), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: size.height) + ) + let viewStatsTextFrame = CGRect(origin: CGPoint(x: 16.0, y: floor((size.height - viewStatsTextSize.height) * 0.5)), size: viewStatsTextSize) + if let viewStatsTextView = self.viewStatsText.view { + if viewStatsTextView.superview == nil { + viewStatsTextView.layer.anchorPoint = CGPoint() + self.addSubview(viewStatsTextView) + } + transition.setPosition(view: viewStatsTextView, position: viewStatsTextFrame.origin) + transition.setBounds(view: viewStatsTextView, bounds: CGRect(origin: CGPoint(), size: viewStatsTextFrame.size)) + } + + var rightContentOffset: CGFloat = availableSize.width - 12.0 + + let deleteButtonSize = self.deleteButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(BundleIconComponent( + name: "Chat/Input/Accessory Panels/MessageSelectionTrash", + tintColor: .white + )), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.deleteAction() + } + ).minSize(CGSize(width: 44.0, height: baseHeight))), + environment: {}, + containerSize: CGSize(width: 44.0, height: baseHeight) + ) + if let deleteButtonView = self.deleteButton.view { + if deleteButtonView.superview == nil { + self.addSubview(deleteButtonView) + } + transition.setFrame(view: deleteButtonView, frame: CGRect(origin: CGPoint(x: rightContentOffset - deleteButtonSize.width, y: floor((size.height - deleteButtonSize.height) * 0.5)), size: deleteButtonSize)) + rightContentOffset -= deleteButtonSize.width - 8.0 + } + + let moreButton: MoreHeaderButton + if let current = self.moreButton { + moreButton = current + } else { + if let moreButton = self.moreButton { + moreButton.removeFromSupernode() + self.moreButton = nil + } + + moreButton = MoreHeaderButton(color: .white) + moreButton.isUserInteractionEnabled = true + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: .white))) + moreButton.onPressed = { [weak self] in + guard let self, let component = self.component, let moreButton = self.moreButton else { + return + } + moreButton.play() + component.moreAction(moreButton.view, nil) + } + moreButton.contextAction = { [weak self] sourceNode, gesture in + guard let self, let component = self.component, let moreButton = self.moreButton else { + return + } + moreButton.play() + component.moreAction(moreButton.view, gesture) + } + self.moreButton = moreButton + self.addSubnode(moreButton) + } + + let buttonSize = CGSize(width: 32.0, height: 44.0) + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: .white))) + transition.setFrame(view: moreButton.view, frame: CGRect(origin: CGPoint(x: rightContentOffset - buttonSize.width, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize)) + return size } } diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/Contents.json new file mode 100644 index 0000000000..dcbff79c27 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "InputReactionIcon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/InputReactionIcon.svg b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/InputReactionIcon.svg new file mode 100644 index 0000000000..0e358991b8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconReaction.imageset/InputReactionIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 469c594ba0..68434232f4 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -102,7 +102,8 @@ private enum ChatListSearchEntry: Comparable, Identifiable { hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: [], - autoremoveTimeout: nil + autoremoveTimeout: nil, + hasNewStories: false )), editing: false, hasActiveRevealControls: false, @@ -267,6 +268,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe }, openPremiumIntro: { }, openChatFolderUpdates: { }, hideChatFolderUpdates: { + }, openStories: { _ in }) interaction.searchTextHighightState = searchQuery self.interaction = interaction diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 544d08d859..d98b628fad 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -268,6 +268,7 @@ public final class TelegramRootController: NavigationController { case let .asset(asset): item = TGMediaAsset(phAsset: asset) } + let context = self.context legacyStoryMediaEditor(context: self.context, item: item, getCaptionPanelView: { return nil }, completion: { result in dismissCameraImpl?() switch result { @@ -276,7 +277,16 @@ public final class TelegramRootController: NavigationController { case let .video(path): _ = path case let .asset(asset): - _ = asset + let options = PHImageRequestOptions() + options.deliveryMode = .highQualityFormat + options.isNetworkAccessAllowed = true + PHImageManager.default().requestImageData(for: asset, options:options, resultHandler: { data, _, _, _ in + if let data, let image = UIImage(data: data) { + Queue.mainQueue().async { + let _ = context.engine.messages.uploadStory(media: .image(dimensions: PixelDimensions(image.size), data: data)).start() + } + } + }) } }, present: { c, a in presentImpl?(c)