diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 5fe63c407b..bdb918f337 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -853,6 +853,9 @@ public protocol QuickReplySetupScreenInitialData: AnyObject { public protocol AutomaticBusinessMessageSetupScreenInitialData: AnyObject { } +public protocol ChatbotSetupScreenInitialData: AnyObject { +} + public protocol SharedAccountContext: AnyObject { var sharedContainerPath: String { get } var basePath: String { get } @@ -940,7 +943,8 @@ public protocol SharedAccountContext: AnyObject { func makeArchiveSettingsController(context: AccountContext) -> ViewController func makeFilterSettingsController(context: AccountContext, modal: Bool, scrollToTags: Bool, dismissed: (() -> Void)?) -> ViewController func makeBusinessSetupScreen(context: AccountContext) -> ViewController - func makeChatbotSetupScreen(context: AccountContext) -> ViewController + func makeChatbotSetupScreen(context: AccountContext, initialData: ChatbotSetupScreenInitialData) -> ViewController + func makeChatbotSetupScreenInitialData(context: AccountContext) -> Signal func makeBusinessLocationSetupScreen(context: AccountContext, initialValue: TelegramBusinessLocation?, completion: @escaping (TelegramBusinessLocation?) -> Void) -> ViewController func makeBusinessHoursSetupScreen(context: AccountContext, initialValue: TelegramBusinessHours?, completion: @escaping (TelegramBusinessHours?) -> Void) -> ViewController func makeAutomaticBusinessMessageSetupScreen(context: AccountContext, initialData: AutomaticBusinessMessageSetupScreenInitialData, isAwayMode: Bool) -> ViewController diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 66dde6dd3d..76e5b5c349 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -1080,10 +1080,14 @@ public enum ChatHistoryListSource { case customView(historyView: Signal<(MessageHistoryView, ViewUpdateType), NoError>) } +public enum ChatQuickReplyShortcutType { + case generic + case greeting + case away +} + public enum ChatCustomContentsKind: Equatable { - case greetingMessageInput - case awayMessageInput - case quickReplyMessageInput(shortcut: String) + case quickReplyMessageInput(shortcut: String, shortcutType: ChatQuickReplyShortcutType) } public protocol ChatCustomContentsProtocol: AnyObject { diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index dc3a040e4c..db2253d677 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -220,7 +220,7 @@ private final class AttachButtonComponent: CombinedComponent { case .quickReply: //TODO:localize name = "Reply" - imageName = "Chat/Attach Menu/Location" + imageName = "Chat/Attach Menu/Reply" } let tintColor = component.isSelected ? component.theme.rootController.tabBar.selectedIconColor : component.theme.rootController.tabBar.iconColor diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 2644249b50..b1198e523a 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -44,6 +44,7 @@ private final class ChatListFilterPresetControllerArguments { let linkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void let updateTagColor: (PeerNameColor?) -> Void + let openTagColorPremium: () -> Void init( context: AccountContext, @@ -63,7 +64,8 @@ private final class ChatListFilterPresetControllerArguments { removeLink: @escaping (ExportedChatFolderLink) -> Void, linkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, - updateTagColor: @escaping (PeerNameColor?) -> Void + updateTagColor: @escaping (PeerNameColor?) -> Void, + openTagColorPremium: @escaping () -> Void ) { self.context = context self.updateState = updateState @@ -83,6 +85,7 @@ private final class ChatListFilterPresetControllerArguments { self.linkContextAction = linkContextAction self.peerContextAction = peerContextAction self.updateTagColor = updateTagColor + self.openTagColorPremium = openTagColorPremium } } @@ -231,8 +234,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { case inviteLinkCreate(hasLinks: Bool) case inviteLink(Int, ExportedChatFolderLink) case inviteLinkInfo(text: String) - case tagColorHeader(name: String, color: PeerNameColors.Colors) - case tagColor(colors: PeerNameColors, currentColor: PeerNameColor?) + case tagColorHeader(name: String, color: PeerNameColors.Colors?, isPremium: Bool) + case tagColor(colors: PeerNameColors, currentColor: PeerNameColor?, isPremium: Bool) case tagColorFooter var section: ItemListSectionId { @@ -458,20 +461,35 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(presentationData.theme), title: text, sectionId: self.section, editing: false, action: { arguments.expandSection(.exclude) }) - case let .tagColorHeader(name, color): + case let .tagColorHeader(name, color, isPremium): //TODO:localize - return ItemListSectionHeaderItem(presentationData: presentationData, text: "FOLDER COLOR", badge: name.uppercased(), badgeStyle: ItemListSectionHeaderItem.BadgeStyle( - background: color.main.withMultipliedAlpha(0.1), - foreground: color.main - ), sectionId: self.section) - case let .tagColor(colors, color): + var badge: String? + var badgeStyle: ItemListSectionHeaderItem.BadgeStyle? + var accessoryText: ItemListSectionHeaderAccessoryText? + if isPremium, let color { + badge = name.uppercased() + badgeStyle = ItemListSectionHeaderItem.BadgeStyle( + background: color.main.withMultipliedAlpha(0.1), + foreground: color.main + ) + } else if color != nil { + accessoryText = ItemListSectionHeaderAccessoryText(value: "PREMIUM EXPIRED", color: .generic) + } + return ItemListSectionHeaderItem(presentationData: presentationData, text: "FOLDER COLOR", badge: badge, badgeStyle: badgeStyle, accessoryText: accessoryText, sectionId: self.section) + case let .tagColor(colors, color, isPremium): return PeerNameColorItem( theme: presentationData.theme, colors: colors, isProfile: true, - currentColor: color, + displayEmptyColor: true, + currentColor: isPremium ? color : nil, + isLocked: !isPremium, updated: { color in - arguments.updateTagColor(color) + if isPremium { + arguments.updateTagColor(color) + } else { + arguments.openTagColorPremium() + } }, sectionId: self.section ) @@ -502,6 +520,7 @@ private struct ChatListFilterPresetControllerState: Equatable { var name: String var changedName: Bool var color: PeerNameColor? + var colorUpdated: Bool = false var includeCategories: ChatListFilterPeerCategories var excludeMuted: Bool var excludeRead: Bool @@ -616,11 +635,19 @@ private func chatListFilterPresetControllerEntries(context: AccountContext, pres entries.append(.excludePeerInfo(presentationData.strings.ChatListFolder_ExcludeSectionInfo)) } - let tagColor = state.color ?? .blue - let resolvedColor = context.peerNameColors.getProfile(tagColor, dark: presentationData.theme.overallDarkAppearance, subject: .palette) + let tagColor: PeerNameColor? + if state.colorUpdated { + tagColor = state.color + } else { + tagColor = state.color ?? .blue + } + var resolvedColor: PeerNameColors.Colors? + if let tagColor { + resolvedColor = context.peerNameColors.getProfile(tagColor, dark: presentationData.theme.overallDarkAppearance, subject: .palette) + } - entries.append(.tagColorHeader(name: state.name, color: resolvedColor)) - entries.append(.tagColor(colors: context.peerNameColors, currentColor: tagColor)) + entries.append(.tagColorHeader(name: state.name, color: resolvedColor, isPremium: isPremium)) + entries.append(.tagColor(colors: context.peerNameColors, currentColor: tagColor, isPremium: isPremium)) entries.append(.tagColorFooter) var hasLinks = false @@ -1018,7 +1045,8 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi } else { initialName = "" } - let initialState = ChatListFilterPresetControllerState(name: initialName, changedName: initialPreset != nil, color: initialPreset?.data?.color, includeCategories: initialPreset?.data?.categories ?? [], excludeMuted: initialPreset?.data?.excludeMuted ?? false, excludeRead: initialPreset?.data?.excludeRead ?? false, excludeArchived: initialPreset?.data?.excludeArchived ?? false, additionallyIncludePeers: initialPreset?.data?.includePeers.peers ?? [], additionallyExcludePeers: initialPreset?.data?.excludePeers ?? [], expandedSections: []) + var initialState = ChatListFilterPresetControllerState(name: initialName, changedName: initialPreset != nil, color: initialPreset?.data?.color, includeCategories: initialPreset?.data?.categories ?? [], excludeMuted: initialPreset?.data?.excludeMuted ?? false, excludeRead: initialPreset?.data?.excludeRead ?? false, excludeArchived: initialPreset?.data?.excludeArchived ?? false, additionallyIncludePeers: initialPreset?.data?.includePeers.peers ?? [], additionallyExcludePeers: initialPreset?.data?.excludePeers ?? [], expandedSections: []) + initialState.colorUpdated = true let updatedCurrentPreset: Signal if let initialPreset { @@ -1543,8 +1571,19 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi updateState { state in var state = state state.color = color + state.colorUpdated = true return state } + }, + openTagColorPremium: { + //TODO:localize + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: "Subscribe to **Telegram Premium** to select folder color.", customUndoText: presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in + if case .undo = action { + pushControllerImpl?(PremiumIntroScreen(context: context, source: .folders)) + } + return false + }), nil) } ) diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 3f3c7de424..3b496c6b11 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -22,8 +22,9 @@ private final class ChatListFilterPresetListControllerArguments { let setItemWithRevealedOptions: (Int32?, Int32?) -> Void let removePreset: (Int32) -> Void let updateDisplayTags: (Bool) -> Void + let updateDisplayTagsLocked: () -> Void - init(context: AccountContext, addSuggestedPressed: @escaping (String, ChatListFilterData) -> Void, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void, updateDisplayTags: @escaping (Bool) -> Void) { + init(context: AccountContext, addSuggestedPressed: @escaping (String, ChatListFilterData) -> Void, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void, updateDisplayTags: @escaping (Bool) -> Void, updateDisplayTagsLocked: @escaping () -> Void) { self.context = context self.addSuggestedPressed = addSuggestedPressed self.openPreset = openPreset @@ -31,6 +32,7 @@ private final class ChatListFilterPresetListControllerArguments { self.setItemWithRevealedOptions = setItemWithRevealedOptions self.removePreset = removePreset self.updateDisplayTags = updateDisplayTags + self.updateDisplayTagsLocked = updateDisplayTagsLocked } } @@ -93,10 +95,10 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { case suggestedPreset(index: PresetIndex, title: String, label: String, preset: ChatListFilterData) case suggestedAddCustom(String) case listHeader(String) - case preset(index: PresetIndex, title: String, label: String, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool, isAllChats: Bool, isDisabled: Bool) + case preset(index: PresetIndex, title: String, label: String, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool, isAllChats: Bool, isDisabled: Bool, displayTags: Bool) case addItem(text: String, isEditing: Bool) case listFooter(String) - case displayTags(Bool) + case displayTags(Bool?) case displayTagsFooter var section: ItemListSectionId { @@ -120,7 +122,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { return 100 case .addItem: return 101 - case let .preset(index, _, _, _, _, _, _, _, _): + case let .preset(index, _, _, _, _, _, _, _, _, _): return 102 + index.value case .listFooter: return 1001 @@ -149,7 +151,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { return .suggestedAddCustom case .listHeader: return .listHeader - case let .preset(_, _, _, preset, _, _, _, _, _): + case let .preset(_, _, _, preset, _, _, _, _, _, _): return .preset(preset.id) case .addItem: return .addItem @@ -183,8 +185,16 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { }) case let .listHeader(text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section) - case let .preset(_, title, label, preset, canBeReordered, canBeDeleted, isEditing, isAllChats, isDisabled): - return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title, label: label, editing: ChatListFilterPresetListItemEditing(editable: true, editing: isEditing, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, isAllChats: isAllChats, isDisabled: isDisabled, sectionId: self.section, action: { + case let .preset(_, title, label, preset, canBeReordered, canBeDeleted, isEditing, isAllChats, isDisabled, displayTags): + var resolvedColor: UIColor? + if displayTags, case let .filter(_, _, _, data) = preset { + let tagColor = data.color + if let tagColor { + resolvedColor = arguments.context.peerNameColors.getProfile(tagColor, dark: presentationData.theme.overallDarkAppearance, subject: .palette).main + } + } + + return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title, label: label, tagColor: resolvedColor, editing: ChatListFilterPresetListItemEditing(editable: true, editing: isEditing, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, isAllChats: isAllChats, isDisabled: isDisabled, sectionId: self.section, action: { if isDisabled { arguments.addNew() } else { @@ -203,8 +213,14 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .displayTags(value): //TODO:localize - return ItemListSwitchItem(presentationData: presentationData, title: "Show Folder Tags", value: value, sectionId: self.section, style: .blocks, updated: { value in - arguments.updateDisplayTags(value) + return ItemListSwitchItem(presentationData: presentationData, title: "Show Folder Tags", value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in + if value != nil { + arguments.updateDisplayTags(updatedValue) + } else { + arguments.updateDisplayTagsLocked() + } + }, activatedWhileDisabled: { + arguments.updateDisplayTagsLocked() }, tag: ChatListFilterPresetListEntryTag.displayTags) case .displayTagsFooter: //TODO:localize @@ -262,15 +278,20 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present entries.append(.addItem(text: presentationData.strings.ChatListFilterList_CreateFolder, isEditing: state.isEditing)) + var effectiveDisplayTags: Bool? + if isPremium { + effectiveDisplayTags = displayTags + } + if !filters.isEmpty || suggestedFilters.isEmpty { var folderCount = 0 for (filter, chatCount) in filtersWithAppliedOrder(filters: filters, order: updatedFilterOrder) { if case .allChats = filter { - entries.append(.preset(index: PresetIndex(value: entries.count), title: "", label: "", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: false, isEditing: state.isEditing, isAllChats: true, isDisabled: false)) + entries.append(.preset(index: PresetIndex(value: entries.count), title: "", label: "", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: false, isEditing: state.isEditing, isAllChats: true, isDisabled: false, displayTags: effectiveDisplayTags == true)) } if case let .filter(_, title, _, _) = filter { folderCount += 1 - entries.append(.preset(index: PresetIndex(value: entries.count), title: title, label: chatCount == 0 ? "" : "\(chatCount)", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: true, isEditing: state.isEditing, isAllChats: false, isDisabled: !isPremium && folderCount > limits.maxFoldersCount)) + entries.append(.preset(index: PresetIndex(value: entries.count), title: title, label: chatCount == 0 ? "" : "\(chatCount)", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: true, isEditing: state.isEditing, isAllChats: false, isDisabled: !isPremium && folderCount > limits.maxFoldersCount, displayTags: effectiveDisplayTags == true)) } } @@ -287,7 +308,7 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present } } - entries.append(.displayTags(displayTags)) + entries.append(.displayTags(effectiveDisplayTags)) entries.append(.displayTagsFooter) return entries @@ -536,6 +557,15 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch }) }, updateDisplayTags: { value in context.engine.peers.updateChatListFiltersDisplayTags(isEnabled: value) + }, updateDisplayTagsLocked: { + //TODO:localize + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: "Subscribe to **Telegram Premium** to show folder tags.", customUndoText: presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in + if case .undo = action { + pushControllerImpl?(PremiumIntroScreen(context: context, source: .folders)) + } + return false }) + ) }) let featuredFilters = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFiltersFeaturedState]) @@ -673,7 +703,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch } controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ChatListFilterPresetListEntry]) -> Signal in let fromEntry = entries[fromIndex] - guard case let .preset(_, _, _, fromPreset, _, _, _, _, _) = fromEntry else { + guard case let .preset(_, _, _, fromPreset, _, _, _, _, _, _) = fromEntry else { return .single(false) } var referenceFilter: ChatListFilter? @@ -681,7 +711,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch var afterAll = false if toIndex < entries.count { switch entries[toIndex] { - case let .preset(_, _, _, preset, _, _, _, _, _): + case let .preset(_, _, _, preset, _, _, _, _, _, _): referenceFilter = preset default: if entries[toIndex] < fromEntry { diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index 609c9ae333..70d2b0d5df 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -19,6 +19,7 @@ final class ChatListFilterPresetListItem: ListViewItem, ItemListItem { let preset: ChatListFilter let title: String let label: String + let tagColor: UIColor? let editing: ChatListFilterPresetListItemEditing let canBeReordered: Bool let canBeDeleted: Bool @@ -34,6 +35,7 @@ final class ChatListFilterPresetListItem: ListViewItem, ItemListItem { preset: ChatListFilter, title: String, label: String, + tagColor: UIColor?, editing: ChatListFilterPresetListItemEditing, canBeReordered: Bool, canBeDeleted: Bool, @@ -48,6 +50,7 @@ final class ChatListFilterPresetListItem: ListViewItem, ItemListItem { self.preset = preset self.title = title self.label = label + self.tagColor = tagColor self.editing = editing self.canBeReordered = canBeReordered self.canBeDeleted = canBeDeleted @@ -125,6 +128,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN private let labelNode: TextNode private let arrowNode: ASImageNode private let sharedIconNode: ASImageNode + private var tagIconView: UIImageView? private let activateArea: AccessibilityAreaNode @@ -173,7 +177,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN self.sharedIconNode.displayWithoutProcessing = true self.sharedIconNode.displaysAsynchronously = false self.sharedIconNode.isLayerBacked = true - + self.activateArea = AccessibilityAreaNode() self.highlightedBackgroundNode = ASDisplayNode() @@ -214,6 +218,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN } else { updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme) } + updatedSharedIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat List/SharedFolderListIcon"), color: item.presentationData.theme.list.disclosureArrowColor) } @@ -412,7 +417,29 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN if case let .filter(_, _, _, data) = item.preset, data.isShared { isShared = true } - strongSelf.sharedIconNode.isHidden = !isShared + strongSelf.sharedIconNode.isHidden = !isShared || item.tagColor != nil + + if let tagColor = item.tagColor { + let tagIconView: UIImageView + if let current = strongSelf.tagIconView { + tagIconView = current + } else { + tagIconView = UIImageView(image: generateStretchableFilledCircleImage(diameter: 24.0, color: .white)?.withRenderingMode(.alwaysTemplate)) + strongSelf.tagIconView = tagIconView + strongSelf.containerNode.view.addSubview(tagIconView) + } + tagIconView.tintColor = tagColor + + let tagIconFrame = CGRect(origin: CGPoint(x: strongSelf.arrowNode.frame.minX - 2.0 - 24.0, y: floorToScreenPixels((layout.contentSize.height - 24.0) / 2.0)), size: CGSize(width: 24.0, height: 24.0)) + tagIconView.frame = tagIconFrame + + transition.updateAlpha(layer: tagIconView.layer, alpha: reorderControlSizeAndApply != nil ? 0.0 : 1.0) + } else { + if let tagIconView = strongSelf.tagIconView { + strongSelf.tagIconView = nil + tagIconView.removeFromSuperview() + } + } strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 0.0), size: CGSize(width: params.width - params.rightInset - 56.0 - (leftInset + revealOffset + editingOffset), height: layout.contentSize.height)) @@ -508,6 +535,12 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN var sharedIconFrame = self.sharedIconNode.frame sharedIconFrame.origin.x = arrowFrame.minX + 2.0 - sharedIconFrame.width transition.updateFrame(node: self.sharedIconNode, frame: sharedIconFrame) + + if let tagIconView = self.tagIconView { + var tagIconFrame = tagIconView.frame + tagIconFrame.origin.x = arrowFrame.minX - 2.0 - tagIconFrame.width + transition.updateFrame(view: tagIconView, frame: tagIconFrame) + } } override func revealOptionsInteractivelyOpened() { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 0971935516..19c729f770 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2151,11 +2151,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { useInlineAuthorPrefix = true } if !itemTags.isEmpty { - if case let .chat(peer) = contentPeer, peer.peerId == item.context.account.peerId { - } else { - useInlineAuthorPrefix = true - } - forumTopicData = nil topForumTopicItems = [] } @@ -3909,10 +3904,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if !itemTags.isEmpty { let itemTagListFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.maxY - 12.0), size: CGSize(width: contentRect.width, height: 20.0)) + var itemTagListTransition = transition let itemTagList: ComponentView if let current = strongSelf.itemTagList { itemTagList = current } else { + itemTagListTransition = .immediate itemTagList = ComponentView() strongSelf.itemTagList = itemTagList } @@ -3931,7 +3928,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { itemTagListView.isUserInteractionEnabled = false strongSelf.mainContentContainerNode.view.addSubview(itemTagListView) } - itemTagListView.frame = itemTagListFrame + + itemTagListTransition.updateFrame(view: itemTagListView, frame: itemTagListFrame) } } else { if let itemTagList = strongSelf.itemTagList { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 82c0cfe1fc..574aaef0ee 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -4220,13 +4220,15 @@ func chatListItemTags(location: ChatListControllerLocation, accountPeerId: Engin var result: [ChatListItemContent.Tag] = [] for case let .filter(id, title, _, data) in chatListFilters { - let predicate = chatListFilterPredicate(filter: data, accountPeerId: accountPeerId) - if predicate.includes(peer: peer._asPeer(), groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: hasUnseenMentions) { - result.append(ChatListItemContent.Tag( - id: id, - title: title, - colorId: data.color?.rawValue ?? PeerNameColor.blue.rawValue - )) + if data.color != nil { + let predicate = chatListFilterPredicate(filter: data, accountPeerId: accountPeerId) + if predicate.includes(peer: peer._asPeer(), groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: hasUnseenMentions) { + result.append(ChatListItemContent.Tag( + id: id, + title: title, + colorId: data.color?.rawValue ?? PeerNameColor.blue.rawValue + )) + } } } return result diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 16af2a2c07..c226bb6144 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -2019,7 +2019,7 @@ open class TextNode: ASDisplayNode { var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length - 1, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) + addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) } } } @@ -2134,7 +2134,7 @@ open class TextNode: ASDisplayNode { var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length - 1, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) + addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 63a56524bb..d5d9c5fbd6 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -2171,7 +2171,14 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { push(accountContext.sharedContext.makeAutomaticBusinessMessageSetupScreen(context: accountContext, initialData: initialData, isAwayMode: true)) }) case .chatbots: - push(accountContext.sharedContext.makeChatbotSetupScreen(context: accountContext)) + let _ = (accountContext.sharedContext.makeChatbotSetupScreenInitialData(context: accountContext) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak accountContext] initialData in + guard let accountContext else { + return + } + push(accountContext.sharedContext.makeChatbotSetupScreen(context: accountContext, initialData: initialData)) + }) } } else { var demoSubject: PremiumDemoScreen.Subject @@ -3683,7 +3690,7 @@ private final class EmojiActionIconComponent: Component { animationRenderer: component.context.animationRenderer, content: component.fileId.flatMap { .animation( content: .customEmoji(fileId: $0), - size: size, + size: CGSize(width: size.width * 2.0, height: size.height * 2.0), placeholderColor: .lightGray, themeColor: component.color, loopMode: .forever diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 763c13f933..75f2845fe8 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -80,6 +80,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[706514033] = { return Api.Boost.parse_boost($0) } dict[-1778593322] = { return Api.BotApp.parse_botApp($0) } dict[1571189943] = { return Api.BotApp.parse_botAppNotModified($0) } + dict[-1989921868] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) } dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) } dict[-1180016534] = { return Api.BotCommandScope.parse_botCommandScopeChatAdmins($0) } dict[1877059713] = { return Api.BotCommandScope.parse_botCommandScopeChats($0) } @@ -101,12 +102,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-944407322] = { return Api.BotMenuButton.parse_botMenuButton($0) } dict[1113113093] = { return Api.BotMenuButton.parse_botMenuButtonCommands($0) } dict[1966318984] = { return Api.BotMenuButton.parse_botMenuButtonDefault($0) } - dict[-1697809899] = { return Api.BusinessAwayMessage.parse_businessAwayMessage($0) } + dict[467254972] = { return Api.BusinessAwayMessage.parse_businessAwayMessage($0) } dict[-910564679] = { return Api.BusinessAwayMessageSchedule.parse_businessAwayMessageScheduleAlways($0) } dict[-867328308] = { return Api.BusinessAwayMessageSchedule.parse_businessAwayMessageScheduleCustom($0) } dict[-1007487743] = { return Api.BusinessAwayMessageSchedule.parse_businessAwayMessageScheduleOutsideWorkHours($0) } - dict[-1600596660] = { return Api.BusinessGreetingMessage.parse_businessGreetingMessage($0) } + dict[-451302485] = { return Api.BusinessGreetingMessage.parse_businessGreetingMessage($0) } dict[-1403249929] = { return Api.BusinessLocation.parse_businessLocation($0) } + dict[554733559] = { return Api.BusinessRecipients.parse_businessRecipients($0) } dict[302717625] = { return Api.BusinessWeeklyOpen.parse_businessWeeklyOpen($0) } dict[-1936543592] = { return Api.BusinessWorkHours.parse_businessWorkHours($0) } dict[1462101002] = { return Api.CdnConfig.parse_cdnConfig($0) } @@ -206,6 +208,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1713193015] = { return Api.ChatReactions.parse_chatReactionsSome($0) } dict[-1390068360] = { return Api.CodeSettings.parse_codeSettings($0) } dict[-870702050] = { return Api.Config.parse_config($0) } + dict[-404121113] = { return Api.ConnectedBot.parse_connectedBot($0) } dict[341499403] = { return Api.Contact.parse_contact($0) } dict[383348795] = { return Api.ContactStatus.parse_contactStatus($0) } dict[2104790276] = { return Api.DataJSON.parse_dataJSON($0) } @@ -305,8 +308,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-459324] = { return Api.InputBotInlineResult.parse_inputBotInlineResultDocument($0) } dict[1336154098] = { return Api.InputBotInlineResult.parse_inputBotInlineResultGame($0) } dict[-1462213465] = { return Api.InputBotInlineResult.parse_inputBotInlineResultPhoto($0) } - dict[-831530424] = { return Api.InputBusinessAwayMessage.parse_inputBusinessAwayMessage($0) } - dict[2102015497] = { return Api.InputBusinessGreetingMessage.parse_inputBusinessGreetingMessage($0) } + dict[-307493900] = { return Api.InputBusinessAwayMessage.parse_inputBusinessAwayMessage($0) } + dict[26528571] = { return Api.InputBusinessGreetingMessage.parse_inputBusinessGreetingMessage($0) } + dict[1871393450] = { return Api.InputBusinessRecipients.parse_inputBusinessRecipients($0) } dict[-212145112] = { return Api.InputChannel.parse_inputChannel($0) } dict[-292807034] = { return Api.InputChannel.parse_inputChannelEmpty($0) } dict[1536380829] = { return Api.InputChannel.parse_inputChannelFromMessage($0) } @@ -875,15 +879,19 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-75283823] = { return Api.TopPeerCategoryPeers.parse_topPeerCategoryPeers($0) } dict[397910539] = { return Api.Update.parse_updateAttachMenuBots($0) } dict[-335171433] = { return Api.Update.parse_updateAutoSaveSettings($0) } + dict[-1964652166] = { return Api.Update.parse_updateBotBusinessConnect($0) } dict[-1177566067] = { return Api.Update.parse_updateBotCallbackQuery($0) } dict[-1873947492] = { return Api.Update.parse_updateBotChatBoost($0) } dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) } dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) } + dict[-1590796039] = { return Api.Update.parse_updateBotDeleteBusinessMessage($0) } + dict[1420915171] = { return Api.Update.parse_updateBotEditBusinessMessage($0) } dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) } dict[317794823] = { return Api.Update.parse_updateBotInlineSend($0) } dict[347625491] = { return Api.Update.parse_updateBotMenuButton($0) } dict[-1407069234] = { return Api.Update.parse_updateBotMessageReaction($0) } dict[164329305] = { return Api.Update.parse_updateBotMessageReactions($0) } + dict[-2142069794] = { return Api.Update.parse_updateBotNewBusinessMessage($0) } dict[-1934976362] = { return Api.Update.parse_updateBotPrecheckoutQuery($0) } dict[-1246823043] = { return Api.Update.parse_updateBotShippingQuery($0) } dict[-997782967] = { return Api.Update.parse_updateBotStopped($0) } @@ -1047,6 +1055,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1275039392] = { return Api.account.Authorizations.parse_authorizations($0) } dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) } dict[1279133341] = { return Api.account.AutoSaveSettings.parse_autoSaveSettings($0) } + dict[400029819] = { return Api.account.ConnectedBots.parse_connectedBots($0) } dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) } dict[731303195] = { return Api.account.EmailVerified.parse_emailVerified($0) } dict[-507835039] = { return Api.account.EmailVerified.parse_emailVerifiedLogin($0) } @@ -1383,6 +1392,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.BotApp: _1.serialize(buffer, boxed) + case let _1 as Api.BotBusinessConnection: + _1.serialize(buffer, boxed) case let _1 as Api.BotCommand: _1.serialize(buffer, boxed) case let _1 as Api.BotCommandScope: @@ -1403,6 +1414,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.BusinessLocation: _1.serialize(buffer, boxed) + case let _1 as Api.BusinessRecipients: + _1.serialize(buffer, boxed) case let _1 as Api.BusinessWeeklyOpen: _1.serialize(buffer, boxed) case let _1 as Api.BusinessWorkHours: @@ -1453,6 +1466,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.Config: _1.serialize(buffer, boxed) + case let _1 as Api.ConnectedBot: + _1.serialize(buffer, boxed) case let _1 as Api.Contact: _1.serialize(buffer, boxed) case let _1 as Api.ContactStatus: @@ -1559,6 +1574,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.InputBusinessGreetingMessage: _1.serialize(buffer, boxed) + case let _1 as Api.InputBusinessRecipients: + _1.serialize(buffer, boxed) case let _1 as Api.InputChannel: _1.serialize(buffer, boxed) case let _1 as Api.InputChatPhoto: @@ -1945,6 +1962,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.account.AutoSaveSettings: _1.serialize(buffer, boxed) + case let _1 as Api.account.ConnectedBots: + _1.serialize(buffer, boxed) case let _1 as Api.account.ContentSettings: _1.serialize(buffer, boxed) case let _1 as Api.account.EmailVerified: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index f223f6a5e6..106159d9de 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -1040,6 +1040,58 @@ public extension Api { } } +public extension Api { + enum BotBusinessConnection: TypeConstructorDescription { + case botBusinessConnection(flags: Int32, connectionId: String, userId: Int64, dcId: Int32, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date): + if boxed { + buffer.appendInt32(-1989921868) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(connectionId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(dcId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date): + return ("botBusinessConnection", [("flags", flags as Any), ("connectionId", connectionId as Any), ("userId", userId as Any), ("dcId", dcId as Any), ("date", date as Any)]) + } + } + + public static func parse_botBusinessConnection(_ reader: BufferReader) -> BotBusinessConnection? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = 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.BotBusinessConnection.botBusinessConnection(flags: _1!, connectionId: _2!, userId: _3!, dcId: _4!, date: _5!) + } + else { + return nil + } + } + + } +} public extension Api { enum BotCommand: TypeConstructorDescription { case botCommand(command: String, description: String) diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 302e1d26cc..aaea1b49c7 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -590,52 +590,44 @@ public extension Api { } public extension Api { enum BusinessAwayMessage: TypeConstructorDescription { - case businessAwayMessage(flags: Int32, shortcutId: Int32, schedule: Api.BusinessAwayMessageSchedule, users: [Int64]?) + case businessAwayMessage(shortcutId: Int32, schedule: Api.BusinessAwayMessageSchedule, recipients: Api.BusinessRecipients) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .businessAwayMessage(let flags, let shortcutId, let schedule, let users): + case .businessAwayMessage(let shortcutId, let schedule, let recipients): if boxed { - buffer.appendInt32(-1697809899) + buffer.appendInt32(467254972) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(shortcutId, buffer: buffer, boxed: false) schedule.serialize(buffer, true) - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users!.count)) - for item in users! { - serializeInt64(item, buffer: buffer, boxed: false) - }} + recipients.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .businessAwayMessage(let flags, let shortcutId, let schedule, let users): - return ("businessAwayMessage", [("flags", flags as Any), ("shortcutId", shortcutId as Any), ("schedule", schedule as Any), ("users", users as Any)]) + case .businessAwayMessage(let shortcutId, let schedule, let recipients): + return ("businessAwayMessage", [("shortcutId", shortcutId as Any), ("schedule", schedule as Any), ("recipients", recipients as Any)]) } } public static func parse_businessAwayMessage(_ reader: BufferReader) -> BusinessAwayMessage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.BusinessAwayMessageSchedule? + var _2: Api.BusinessAwayMessageSchedule? if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.BusinessAwayMessageSchedule + _2 = Api.parse(reader, signature: signature) as? Api.BusinessAwayMessageSchedule + } + var _3: Api.BusinessRecipients? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.BusinessRecipients } - var _4: [Int64]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BusinessAwayMessage.businessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, users: _4) + if _c1 && _c2 && _c3 { + return Api.BusinessAwayMessage.businessAwayMessage(shortcutId: _1!, schedule: _2!, recipients: _3!) } else { return nil @@ -710,21 +702,16 @@ public extension Api { } public extension Api { enum BusinessGreetingMessage: TypeConstructorDescription { - case businessGreetingMessage(flags: Int32, shortcutId: Int32, users: [Int64]?, noActivityDays: Int32) + case businessGreetingMessage(shortcutId: Int32, recipients: Api.BusinessRecipients, noActivityDays: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .businessGreetingMessage(let flags, let shortcutId, let users, let noActivityDays): + case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): if boxed { - buffer.appendInt32(-1600596660) + buffer.appendInt32(-451302485) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(shortcutId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users!.count)) - for item in users! { - serializeInt64(item, buffer: buffer, boxed: false) - }} + recipients.serialize(buffer, true) serializeInt32(noActivityDays, buffer: buffer, boxed: false) break } @@ -732,28 +719,25 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .businessGreetingMessage(let flags, let shortcutId, let users, let noActivityDays): - return ("businessGreetingMessage", [("flags", flags as Any), ("shortcutId", shortcutId as Any), ("users", users as Any), ("noActivityDays", noActivityDays as Any)]) + case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + return ("businessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) } } public static func parse_businessGreetingMessage(_ reader: BufferReader) -> BusinessGreetingMessage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Int64]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } } - var _4: Int32? - _4 = reader.readInt32() + var _2: Api.BusinessRecipients? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BusinessRecipients + } + var _3: Int32? + _3 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BusinessGreetingMessage.businessGreetingMessage(flags: _1!, shortcutId: _2!, users: _3, noActivityDays: _4!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.BusinessGreetingMessage.businessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) } else { return nil @@ -808,6 +792,52 @@ public extension Api { } } +public extension Api { + enum BusinessRecipients: TypeConstructorDescription { + case businessRecipients(flags: Int32, users: [Int64]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .businessRecipients(let flags, let users): + if boxed { + buffer.appendInt32(554733559) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users!.count)) + for item in users! { + serializeInt64(item, buffer: buffer, boxed: false) + }} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .businessRecipients(let flags, let users): + return ("businessRecipients", [("flags", flags as Any), ("users", users as Any)]) + } + } + + public static func parse_businessRecipients(_ reader: BufferReader) -> BusinessRecipients? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Int64]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil + if _c1 && _c2 { + return Api.BusinessRecipients.businessRecipients(flags: _1!, users: _2) + } + else { + return nil + } + } + + } +} public extension Api { enum BusinessWeeklyOpen: TypeConstructorDescription { case businessWeeklyOpen(startMinute: Int32, endMinute: Int32) diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 1d2611a4b9..e7175c51f5 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -508,15 +508,19 @@ public extension Api { indirect enum Update: TypeConstructorDescription { case updateAttachMenuBots case updateAutoSaveSettings + case updateBotBusinessConnect(connection: Api.BotBusinessConnection, qts: Int32) case updateBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, peer: Api.Peer, msgId: Int32, chatInstance: Int64, data: Buffer?, gameShortName: String?) case updateBotChatBoost(peer: Api.Peer, boost: Api.Boost, qts: Int32) case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32) case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand]) + case updateBotDeleteBusinessMessage(connectionId: String, messages: [Int32], qts: Int32) + case updateBotEditBusinessMessage(connectionId: String, message: Api.Message, qts: Int32) case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?) case updateBotMenuButton(botId: Int64, button: Api.BotMenuButton) case updateBotMessageReaction(peer: Api.Peer, msgId: Int32, date: Int32, actor: Api.Peer, oldReactions: [Api.Reaction], newReactions: [Api.Reaction], qts: Int32) case updateBotMessageReactions(peer: Api.Peer, msgId: Int32, date: Int32, reactions: [Api.ReactionCount], qts: Int32) + case updateBotNewBusinessMessage(connectionId: String, message: Api.Message, qts: Int32) case updateBotPrecheckoutQuery(flags: Int32, queryId: Int64, userId: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, currency: String, totalAmount: Int64) case updateBotShippingQuery(queryId: Int64, userId: Int64, payload: Buffer, shippingAddress: Api.PostAddress) case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) @@ -651,6 +655,13 @@ public extension Api { buffer.appendInt32(-335171433) } + break + case .updateBotBusinessConnect(let connection, let qts): + if boxed { + buffer.appendInt32(-1964652166) + } + connection.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) break case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): if boxed { @@ -696,6 +707,26 @@ public extension Api { item.serialize(buffer, true) } break + case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts): + if boxed { + buffer.appendInt32(-1590796039) + } + serializeString(connectionId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateBotEditBusinessMessage(let connectionId, let message, let qts): + if boxed { + buffer.appendInt32(1420915171) + } + serializeString(connectionId, buffer: buffer, boxed: false) + message.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): if boxed { buffer.appendInt32(1232025500) @@ -760,6 +791,14 @@ public extension Api { } serializeInt32(qts, buffer: buffer, boxed: false) break + case .updateBotNewBusinessMessage(let connectionId, let message, let qts): + if boxed { + buffer.appendInt32(-2142069794) + } + serializeString(connectionId, buffer: buffer, boxed: false) + message.serialize(buffer, true) + serializeInt32(qts, buffer: buffer, boxed: false) + break case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): if boxed { buffer.appendInt32(-1934976362) @@ -1778,6 +1817,8 @@ public extension Api { return ("updateAttachMenuBots", []) case .updateAutoSaveSettings: return ("updateAutoSaveSettings", []) + case .updateBotBusinessConnect(let connection, let qts): + return ("updateBotBusinessConnect", [("connection", connection as Any), ("qts", qts as Any)]) case .updateBotCallbackQuery(let flags, let queryId, let userId, let peer, let msgId, let chatInstance, let data, let gameShortName): return ("updateBotCallbackQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("chatInstance", chatInstance as Any), ("data", data as Any), ("gameShortName", gameShortName as Any)]) case .updateBotChatBoost(let peer, let boost, let qts): @@ -1786,6 +1827,10 @@ public extension Api { return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)]) case .updateBotCommands(let peer, let botId, let commands): return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)]) + case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts): + return ("updateBotDeleteBusinessMessage", [("connectionId", connectionId as Any), ("messages", messages as Any), ("qts", qts as Any)]) + case .updateBotEditBusinessMessage(let connectionId, let message, let qts): + return ("updateBotEditBusinessMessage", [("connectionId", connectionId as Any), ("message", message as Any), ("qts", qts as Any)]) case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): return ("updateBotInlineQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("peerType", peerType as Any), ("offset", offset as Any)]) case .updateBotInlineSend(let flags, let userId, let query, let geo, let id, let msgId): @@ -1796,6 +1841,8 @@ public extension Api { return ("updateBotMessageReaction", [("peer", peer as Any), ("msgId", msgId as Any), ("date", date as Any), ("actor", actor as Any), ("oldReactions", oldReactions as Any), ("newReactions", newReactions as Any), ("qts", qts as Any)]) case .updateBotMessageReactions(let peer, let msgId, let date, let reactions, let qts): return ("updateBotMessageReactions", [("peer", peer as Any), ("msgId", msgId as Any), ("date", date as Any), ("reactions", reactions as Any), ("qts", qts as Any)]) + case .updateBotNewBusinessMessage(let connectionId, let message, let qts): + return ("updateBotNewBusinessMessage", [("connectionId", connectionId as Any), ("message", message as Any), ("qts", qts as Any)]) case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): return ("updateBotPrecheckoutQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("info", info as Any), ("shippingOptionId", shippingOptionId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any)]) case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): @@ -2045,6 +2092,22 @@ public extension Api { public static func parse_updateAutoSaveSettings(_ reader: BufferReader) -> Update? { return Api.Update.updateAutoSaveSettings } + public static func parse_updateBotBusinessConnect(_ reader: BufferReader) -> Update? { + var _1: Api.BotBusinessConnection? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.BotBusinessConnection + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateBotBusinessConnect(connection: _1!, qts: _2!) + } + else { + return nil + } + } public static func parse_updateBotCallbackQuery(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() @@ -2151,6 +2214,44 @@ public extension Api { return nil } } + public static func parse_updateBotDeleteBusinessMessage(_ reader: BufferReader) -> Update? { + var _1: String? + _1 = parseString(reader) + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.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.Update.updateBotDeleteBusinessMessage(connectionId: _1!, messages: _2!, qts: _3!) + } + else { + return nil + } + } + public static func parse_updateBotEditBusinessMessage(_ reader: BufferReader) -> Update? { + var _1: String? + _1 = parseString(reader) + var _2: Api.Message? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Message + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateBotEditBusinessMessage(connectionId: _1!, message: _2!, qts: _3!) + } + else { + return nil + } + } public static func parse_updateBotInlineQuery(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() @@ -2294,6 +2395,25 @@ public extension Api { return nil } } + public static func parse_updateBotNewBusinessMessage(_ reader: BufferReader) -> Update? { + var _1: String? + _1 = parseString(reader) + var _2: Api.Message? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Message + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateBotNewBusinessMessage(connectionId: _1!, message: _2!, qts: _3!) + } + else { + return nil + } + } public static func parse_updateBotPrecheckoutQuery(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 55a953bc11..1cac2119e8 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -424,6 +424,58 @@ public extension Api.account { } } +public extension Api.account { + enum ConnectedBots: TypeConstructorDescription { + case connectedBots(connectedBots: [Api.ConnectedBot], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .connectedBots(let connectedBots, let users): + if boxed { + buffer.appendInt32(400029819) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(connectedBots.count)) + for item in connectedBots { + 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 .connectedBots(let connectedBots, let users): + return ("connectedBots", [("connectedBots", connectedBots as Any), ("users", users as Any)]) + } + } + + public static func parse_connectedBots(_ reader: BufferReader) -> ConnectedBots? { + var _1: [Api.ConnectedBot]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ConnectedBot.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.ConnectedBots.connectedBots(connectedBots: _1!, users: _2!) + } + else { + return nil + } + } + + } +} public extension Api.account { enum ContentSettings: TypeConstructorDescription { case contentSettings(flags: Int32) @@ -1238,55 +1290,3 @@ public extension Api.account { } } -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 - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 95534e002c..a204a5cc8e 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,55 @@ +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) diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index 55cab5fd70..43cd7ddc88 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -328,6 +328,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getBotBusinessConnection(connectionId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1990746736) + serializeString(connectionId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getBotBusinessConnection", parameters: [("connectionId", String(describing: connectionId))]), 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.account { static func getChannelDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -373,6 +388,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getConnectedBots() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1319421967) + + return (FunctionDescription(name: "account.getConnectedBots", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ConnectedBots? in + let reader = BufferReader(buffer) + var result: Api.account.ConnectedBots? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.ConnectedBots + } + return result + }) + } +} public extension Api.functions.account { static func getContactSignUpNotification() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -1343,6 +1373,23 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func updateConnectedBot(flags: Int32, bot: Api.InputUser, recipients: Api.InputBusinessRecipients) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1674751363) + serializeInt32(flags, buffer: buffer, boxed: false) + bot.serialize(buffer, true) + recipients.serialize(buffer, true) + return (FunctionDescription(name: "account.updateConnectedBot", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("recipients", String(describing: recipients))]), 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.account { static func updateDeviceLocked(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 9a71f34213..f1b43a4ae1 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -662,6 +662,52 @@ public extension Api { } } +public extension Api { + enum ConnectedBot: TypeConstructorDescription { + case connectedBot(flags: Int32, botId: Int64, recipients: Api.BusinessRecipients) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .connectedBot(let flags, let botId, let recipients): + if boxed { + buffer.appendInt32(-404121113) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + recipients.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .connectedBot(let flags, let botId, let recipients): + return ("connectedBot", [("flags", flags as Any), ("botId", botId as Any), ("recipients", recipients as Any)]) + } + } + + public static func parse_connectedBot(_ reader: BufferReader) -> ConnectedBot? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Api.BusinessRecipients? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.BusinessRecipients + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.ConnectedBot.connectedBot(flags: _1!, botId: _2!, recipients: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum Contact: TypeConstructorDescription { case contact(userId: Int64, mutual: Api.Bool) diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index 16de0844ae..03c3e6512f 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -264,52 +264,44 @@ public extension Api { } public extension Api { enum InputBusinessAwayMessage: TypeConstructorDescription { - case inputBusinessAwayMessage(flags: Int32, shortcutId: Int32, schedule: Api.BusinessAwayMessageSchedule, users: [Api.InputUser]?) + case inputBusinessAwayMessage(shortcutId: Int32, schedule: Api.BusinessAwayMessageSchedule, recipients: Api.InputBusinessRecipients) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inputBusinessAwayMessage(let flags, let shortcutId, let schedule, let users): + case .inputBusinessAwayMessage(let shortcutId, let schedule, let recipients): if boxed { - buffer.appendInt32(-831530424) + buffer.appendInt32(-307493900) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(shortcutId, buffer: buffer, boxed: false) schedule.serialize(buffer, true) - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users!.count)) - for item in users! { - item.serialize(buffer, true) - }} + recipients.serialize(buffer, true) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inputBusinessAwayMessage(let flags, let shortcutId, let schedule, let users): - return ("inputBusinessAwayMessage", [("flags", flags as Any), ("shortcutId", shortcutId as Any), ("schedule", schedule as Any), ("users", users as Any)]) + case .inputBusinessAwayMessage(let shortcutId, let schedule, let recipients): + return ("inputBusinessAwayMessage", [("shortcutId", shortcutId as Any), ("schedule", schedule as Any), ("recipients", recipients as Any)]) } } public static func parse_inputBusinessAwayMessage(_ reader: BufferReader) -> InputBusinessAwayMessage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.BusinessAwayMessageSchedule? + var _2: Api.BusinessAwayMessageSchedule? if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.BusinessAwayMessageSchedule + _2 = Api.parse(reader, signature: signature) as? Api.BusinessAwayMessageSchedule + } + var _3: Api.InputBusinessRecipients? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.InputBusinessRecipients } - var _4: [Api.InputUser]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) - } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessAwayMessage.inputBusinessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, users: _4) + if _c1 && _c2 && _c3 { + return Api.InputBusinessAwayMessage.inputBusinessAwayMessage(shortcutId: _1!, schedule: _2!, recipients: _3!) } else { return nil @@ -320,21 +312,16 @@ public extension Api { } public extension Api { enum InputBusinessGreetingMessage: TypeConstructorDescription { - case inputBusinessGreetingMessage(flags: Int32, shortcutId: Int32, users: [Api.InputUser]?, noActivityDays: Int32) + case inputBusinessGreetingMessage(shortcutId: Int32, recipients: Api.InputBusinessRecipients, noActivityDays: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inputBusinessGreetingMessage(let flags, let shortcutId, let users, let noActivityDays): + case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): if boxed { - buffer.appendInt32(2102015497) + buffer.appendInt32(26528571) } - serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(shortcutId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users!.count)) - for item in users! { - item.serialize(buffer, true) - }} + recipients.serialize(buffer, true) serializeInt32(noActivityDays, buffer: buffer, boxed: false) break } @@ -342,28 +329,71 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inputBusinessGreetingMessage(let flags, let shortcutId, let users, let noActivityDays): - return ("inputBusinessGreetingMessage", [("flags", flags as Any), ("shortcutId", shortcutId as Any), ("users", users as Any), ("noActivityDays", noActivityDays as Any)]) + case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + return ("inputBusinessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) } } public static func parse_inputBusinessGreetingMessage(_ reader: BufferReader) -> InputBusinessGreetingMessage? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.InputUser]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) - } } - var _4: Int32? - _4 = reader.readInt32() + var _2: Api.InputBusinessRecipients? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputBusinessRecipients + } + var _3: Int32? + _3 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(flags: _1!, shortcutId: _2!, users: _3, noActivityDays: _4!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum InputBusinessRecipients: TypeConstructorDescription { + case inputBusinessRecipients(flags: Int32, users: [Api.InputUser]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputBusinessRecipients(let flags, let users): + if boxed { + buffer.appendInt32(1871393450) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {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 .inputBusinessRecipients(let flags, let users): + return ("inputBusinessRecipients", [("flags", flags as Any), ("users", users as Any)]) + } + } + + public static func parse_inputBusinessRecipients(_ reader: BufferReader) -> InputBusinessRecipients? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.InputUser]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil + if _c1 && _c2 { + return Api.InputBusinessRecipients.inputBusinessRecipients(flags: _1!, users: _2) } else { return nil diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 330d2ab0b3..28e635f2fc 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1464,7 +1464,7 @@ public final class AccountViewTracker { if i < slice.count { let value = result[i] transaction.updatePeerCachedData(peerIds: Set([slice[i].0]), update: { _, cachedData in - var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, themeEmoticon: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, premiumGiftOptions: [], voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil) + var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, themeEmoticon: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, premiumGiftOptions: [], voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil, greetingMessage: nil, awayMessage: nil, connectedBot: nil) var flags = cachedData.flags if case .boolTrue = value { flags.insert(.premiumRequired) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index d33c041fa2..59579261c2 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -477,6 +477,10 @@ public final class CachedUserData: CachedPeerData { public let flags: CachedUserFlags public let businessHours: TelegramBusinessHours? public let businessLocation: TelegramBusinessLocation? + public let greetingMessage: TelegramBusinessGreetingMessage? + public let awayMessage: TelegramBusinessAwayMessage? + public let connectedBot: TelegramAccountConnectedBot? + public let peerIds: Set public let messageIds: Set public let associatedHistoryMessageId: MessageId? = nil @@ -507,9 +511,12 @@ public final class CachedUserData: CachedPeerData { self.businessLocation = nil self.peerIds = Set() self.messageIds = Set() + self.greetingMessage = nil + self.awayMessage = nil + self.connectedBot = nil } - public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?) { + public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?) { self.about = about self.botInfo = botInfo self.editableBotInfo = editableBotInfo @@ -533,6 +540,9 @@ public final class CachedUserData: CachedPeerData { self.flags = flags self.businessHours = businessHours self.businessLocation = businessLocation + self.greetingMessage = greetingMessage + self.awayMessage = awayMessage + self.connectedBot = connectedBot self.peerIds = Set() @@ -588,6 +598,10 @@ public final class CachedUserData: CachedPeerData { self.businessHours = decoder.decodeCodable(TelegramBusinessHours.self, forKey: "bhrs") self.businessLocation = decoder.decodeCodable(TelegramBusinessLocation.self, forKey: "bloc") + + self.greetingMessage = decoder.decodeCodable(TelegramBusinessGreetingMessage.self, forKey: "bgreet") + self.awayMessage = decoder.decodeCodable(TelegramBusinessAwayMessage.self, forKey: "baway") + self.connectedBot = decoder.decodeCodable(TelegramAccountConnectedBot.self, forKey: "bbot") } public func encode(_ encoder: PostboxEncoder) { @@ -660,6 +674,24 @@ public final class CachedUserData: CachedPeerData { } else { encoder.encodeNil(forKey: "bloc") } + + if let greetingMessage = self.greetingMessage { + encoder.encodeCodable(greetingMessage, forKey: "bgreet") + } else { + encoder.encodeNil(forKey: "bgreet") + } + + if let awayMessage = self.awayMessage { + encoder.encodeCodable(awayMessage, forKey: "baway") + } else { + encoder.encodeNil(forKey: "baway") + } + + if let connectedBot = self.connectedBot { + encoder.encodeCodable(connectedBot, forKey: "bbot") + } else { + encoder.encodeNil(forKey: "bbot") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -679,99 +711,120 @@ public final class CachedUserData: CachedPeerData { if other.businessLocation != self.businessLocation { return false } + if other.greetingMessage != self.greetingMessage { + return false + } + if other.awayMessage != self.awayMessage { + return false + } + if other.connectedBot != self.connectedBot { + return false + } return other.about == self.about && other.botInfo == self.botInfo && other.editableBotInfo == self.editableBotInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.themeEmoticon == other.themeEmoticon && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.fallbackPhoto == other.fallbackPhoto && self.premiumGiftOptions == other.premiumGiftOptions && self.voiceMessagesAvailable == other.voiceMessagesAvailable && self.flags == other.flags && self.wallpaper == other.wallpaper } public func withUpdatedAbout(_ about: String?) -> CachedUserData { - return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedEditableBotInfo(_ editableBotInfo: EditableBotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedPhoto(_ photo: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedPersonalPhoto(_ personalPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedFallbackPhoto(_ fallbackPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedPremiumGiftOptions(_ premiumGiftOptions: [CachedPremiumGiftOption]) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedWallpaper(_ wallpaper: TelegramWallpaper?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedFlags(_ flags: CachedUserFlags) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedBusinessHours(_ businessHours: TelegramBusinessHours?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) } public func withUpdatedBusinessLocation(_ businessLocation: TelegramBusinessLocation?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) + } + + public func withUpdatedGreetingMessage(_ greetingMessage: TelegramBusinessGreetingMessage?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot) + } + + public func withUpdatedAwayMessage(_ awayMessage: TelegramBusinessAwayMessage?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: awayMessage, connectedBot: self.connectedBot) + } + + public func withUpdatedConnectedBot(_ connectedBot: TelegramAccountConnectedBot?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: connectedBot) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift index 46cfb507e1..7d83a01d9b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift @@ -193,5 +193,17 @@ public extension TelegramEngine { public func keepCachedTimeZoneListUpdated() -> Signal { return _internal_keepCachedTimeZoneListUpdated(account: self.account) } + + public func updateBusinessGreetingMessage(greetingMessage: TelegramBusinessGreetingMessage?) -> Signal { + return _internal_updateBusinessGreetingMessage(account: self.account, greetingMessage: greetingMessage) + } + + public func updateBusinessAwayMessage(awayMessage: TelegramBusinessAwayMessage?) -> Signal { + return _internal_updateBusinessAwayMessage(account: self.account, awayMessage: awayMessage) + } + + public func setAccountConnectedBot(bot: TelegramAccountConnectedBot?) -> Signal { + return _internal_setAccountConnectedBot(account: self.account, bot: bot) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index 52c7fbb6b0..a036a33a41 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -1500,5 +1500,89 @@ public extension TelegramEngine.EngineData.Item { } } } + + public struct BusinessGreetingMessage: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = TelegramBusinessGreetingMessage? + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.greetingMessage + } else { + return nil + } + } + } + + public struct BusinessAwayMessage: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = TelegramBusinessAwayMessage? + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.awayMessage + } else { + return nil + } + } + } + + public struct BusinessConnectedBot: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = TelegramAccountConnectedBot? + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.connectedBot + } else { + return nil + } + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift index a6b052fb01..81d67f73ab 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift @@ -362,3 +362,486 @@ func _internal_applySentQuickReplyMessage(transaction: Transaction, shortcut: St transaction.setPreferencesEntry(key: PreferencesKeys.shortcutMessages(), value: PreferencesEntry(state)) } } + +public final class TelegramBusinessRecipients: Codable, Equatable { + public struct Categories: OptionSet { + public var rawValue: Int32 + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + public static let existingChats = Categories(rawValue: 1 << 0) + public static let newChats = Categories(rawValue: 1 << 1) + public static let contacts = Categories(rawValue: 1 << 2) + public static let nonContacts = Categories(rawValue: 1 << 3) + } + + private enum CodingKeys: String, CodingKey { + case categories + case additionalPeers + case exclude + } + + public let categories: Categories + public let additionalPeers: Set + public let exclude: Bool + + public init(categories: Categories, additionalPeers: Set, exclude: Bool) { + self.categories = categories + self.additionalPeers = additionalPeers + self.exclude = exclude + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.categories = Categories(rawValue: try container.decode(Int32.self, forKey: .categories)) + self.additionalPeers = Set(try container.decode([PeerId].self, forKey: .additionalPeers)) + self.exclude = try container.decode(Bool.self, forKey: .exclude) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.categories.rawValue, forKey: .categories) + try container.encode(Array(self.additionalPeers).sorted(), forKey: .additionalPeers) + try container.encode(self.exclude, forKey: .exclude) + } + + public static func ==(lhs: TelegramBusinessRecipients, rhs: TelegramBusinessRecipients) -> Bool { + if lhs === rhs { + return true + } + + if lhs.categories != rhs.categories { + return false + } + if lhs.additionalPeers != rhs.additionalPeers { + return false + } + if lhs.exclude != rhs.exclude { + return false + } + + return true + } +} + +public final class TelegramBusinessGreetingMessage: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case shortcutId + case recipients + case inactivityDays + } + + public let shortcutId: Int32 + public let recipients: TelegramBusinessRecipients + public let inactivityDays: Int + + public init(shortcutId: Int32, recipients: TelegramBusinessRecipients, inactivityDays: Int) { + self.shortcutId = shortcutId + self.recipients = recipients + self.inactivityDays = inactivityDays + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.shortcutId = try container.decode(Int32.self, forKey: .shortcutId) + self.recipients = try container.decode(TelegramBusinessRecipients.self, forKey: .recipients) + self.inactivityDays = Int(try container.decode(Int32.self, forKey: .inactivityDays)) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.shortcutId, forKey: .shortcutId) + try container.encode(self.recipients, forKey: .recipients) + try container.encode(Int32(clamping: self.inactivityDays), forKey: .inactivityDays) + } + + public static func ==(lhs: TelegramBusinessGreetingMessage, rhs: TelegramBusinessGreetingMessage) -> Bool { + if lhs === rhs { + return true + } + + if lhs.shortcutId != rhs.shortcutId { + return false + } + if lhs.recipients != rhs.recipients { + return false + } + if lhs.inactivityDays != rhs.inactivityDays { + return false + } + + return true + } +} + +extension TelegramBusinessGreetingMessage { + convenience init(apiGreetingMessage: Api.BusinessGreetingMessage) { + switch apiGreetingMessage { + case let .businessGreetingMessage(shortcutId, recipients, noActivityDays): + self.init( + shortcutId: shortcutId, + recipients: TelegramBusinessRecipients(apiValue: recipients), + inactivityDays: Int(noActivityDays) + ) + } + } +} + +public final class TelegramBusinessAwayMessage: Codable, Equatable { + public enum Schedule: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case discriminator + case customBeginTimestamp + case customEndTimestamp + } + + case always + case outsideWorkingHours + case custom(beginTimestamp: Int32, endTimestamp: Int32) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + switch try container.decode(Int32.self, forKey: .discriminator) { + case 0: + self = .always + case 1: + self = .outsideWorkingHours + case 2: + self = .custom(beginTimestamp: try container.decode(Int32.self, forKey: .customBeginTimestamp), endTimestamp: try container.decode(Int32.self, forKey: .customEndTimestamp)) + default: + self = .always + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + switch self { + case .always: + try container.encode(0 as Int32, forKey: .discriminator) + case .outsideWorkingHours: + try container.encode(1 as Int32, forKey: .discriminator) + case let .custom(beginTimestamp, endTimestamp): + try container.encode(2 as Int32, forKey: .discriminator) + try container.encode(beginTimestamp, forKey: .customBeginTimestamp) + try container.encode(endTimestamp, forKey: .customEndTimestamp) + } + } + } + + private enum CodingKeys: String, CodingKey { + case shortcutId + case recipients + case schedule + } + + public let shortcutId: Int32 + public let recipients: TelegramBusinessRecipients + public let schedule: Schedule + + public init(shortcutId: Int32, recipients: TelegramBusinessRecipients, schedule: Schedule) { + self.shortcutId = shortcutId + self.recipients = recipients + self.schedule = schedule + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.shortcutId = try container.decode(Int32.self, forKey: .shortcutId) + self.recipients = try container.decode(TelegramBusinessRecipients.self, forKey: .recipients) + self.schedule = try container.decode(Schedule.self, forKey: .schedule) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.shortcutId, forKey: .shortcutId) + try container.encode(self.recipients, forKey: .recipients) + try container.encode(self.schedule, forKey: .schedule) + } + + public static func ==(lhs: TelegramBusinessAwayMessage, rhs: TelegramBusinessAwayMessage) -> Bool { + if lhs === rhs { + return true + } + + if lhs.shortcutId != rhs.shortcutId { + return false + } + if lhs.recipients != rhs.recipients { + return false + } + if lhs.schedule != rhs.schedule { + return false + } + + return true + } +} + +extension TelegramBusinessAwayMessage { + convenience init(apiAwayMessage: Api.BusinessAwayMessage) { + switch apiAwayMessage { + case let .businessAwayMessage(shortcutId, schedule, recipients): + let mappedSchedule: Schedule + switch schedule { + case .businessAwayMessageScheduleAlways: + mappedSchedule = .always + case .businessAwayMessageScheduleOutsideWorkHours: + mappedSchedule = .outsideWorkingHours + case let .businessAwayMessageScheduleCustom(startDate, endDate): + mappedSchedule = .custom(beginTimestamp: startDate, endTimestamp: endDate) + } + + self.init( + shortcutId: shortcutId, + recipients: TelegramBusinessRecipients(apiValue: recipients), + schedule: mappedSchedule + ) + } + } +} + +extension TelegramBusinessRecipients { + convenience init(apiValue: Api.BusinessRecipients) { + switch apiValue { + case let .businessRecipients(flags, users): + var categories: Categories = [] + if (flags & (1 << 0)) != 0 { + categories.insert(.existingChats) + } + if (flags & (1 << 1)) != 0 { + categories.insert(.newChats) + } + if (flags & (1 << 2)) != 0 { + categories.insert(.contacts) + } + if (flags & (1 << 3)) != 0 { + categories.insert(.nonContacts) + } + + self.init( + categories: categories, + additionalPeers: Set((users ?? []).map(PeerId.init)), + exclude: (flags & (1 << 5)) != 0 + ) + } + } + + func apiInputValue(additionalPeers: [Peer]) -> Api.InputBusinessRecipients { + var users: [Api.InputUser]? + if !additionalPeers.isEmpty { + users = additionalPeers.compactMap(apiInputUser) + } + + var flags: Int32 = 0 + + if self.categories.contains(.existingChats) { + flags |= 1 << 0 + } + if self.categories.contains(.newChats) { + flags |= 1 << 1 + } + if self.categories.contains(.contacts) { + flags |= 1 << 2 + } + if self.categories.contains(.nonContacts) { + flags |= 1 << 3 + } + if self.exclude { + flags |= 1 << 5 + } + if users != nil { + flags |= 1 << 4 + } + + return .inputBusinessRecipients(flags: flags, users: users) + } +} + +func _internal_updateBusinessGreetingMessage(account: Account, greetingMessage: TelegramBusinessGreetingMessage?) -> Signal { + let remoteApply = account.postbox.transaction { transaction -> [Peer] in + guard let greetingMessage else { + return [] + } + return greetingMessage.recipients.additionalPeers.compactMap(transaction.getPeer) + } + |> mapToSignal { additionalPeers in + var mappedMessage: Api.InputBusinessGreetingMessage? + if let greetingMessage { + mappedMessage = .inputBusinessGreetingMessage( + shortcutId: greetingMessage.shortcutId, + recipients: greetingMessage.recipients.apiInputValue(additionalPeers: additionalPeers), + noActivityDays: Int32(clamping: greetingMessage.inactivityDays) + ) + } + + var flags: Int32 = 0 + if mappedMessage != nil { + flags |= 1 << 0 + } + + return account.network.request(Api.functions.account.updateBusinessGreetingMessage(flags: flags, message: mappedMessage)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } + + return account.postbox.transaction { transaction in + transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, current in + var current = (current as? CachedUserData) ?? CachedUserData() + current = current.withUpdatedGreetingMessage(greetingMessage) + return current + }) + } + |> ignoreValues + |> then(remoteApply) +} + +func _internal_updateBusinessAwayMessage(account: Account, awayMessage: TelegramBusinessAwayMessage?) -> Signal { + let remoteApply = account.postbox.transaction { transaction -> [Peer] in + guard let awayMessage else { + return [] + } + return awayMessage.recipients.additionalPeers.compactMap(transaction.getPeer) + } + |> mapToSignal { additionalPeers in + var mappedMessage: Api.InputBusinessAwayMessage? + if let awayMessage { + let mappedSchedule: Api.BusinessAwayMessageSchedule + switch awayMessage.schedule { + case .always: + mappedSchedule = .businessAwayMessageScheduleAlways + case .outsideWorkingHours: + mappedSchedule = .businessAwayMessageScheduleOutsideWorkHours + case let .custom(beginTimestamp, endTimestamp): + mappedSchedule = .businessAwayMessageScheduleCustom(startDate: beginTimestamp, endDate: endTimestamp) + } + + mappedMessage = .inputBusinessAwayMessage( + shortcutId: awayMessage.shortcutId, + schedule: mappedSchedule, + recipients: awayMessage.recipients.apiInputValue(additionalPeers: additionalPeers) + ) + } + + var flags: Int32 = 0 + if mappedMessage != nil { + flags |= 1 << 0 + } + + return account.network.request(Api.functions.account.updateBusinessAwayMessage(flags: flags, message: mappedMessage)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } + + return account.postbox.transaction { transaction in + transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, current in + var current = (current as? CachedUserData) ?? CachedUserData() + current = current.withUpdatedAwayMessage(awayMessage) + return current + }) + } + |> ignoreValues + |> then(remoteApply) +} + +/* + connectedBot flags:# can_reply:flags.0?true bot_id:long recipients:BusinessRecipients = ConnectedBot; + + account.connectedBots connected_bots:Vector users:Vector = account.ConnectedBots; + + ---functions--- + + account.setConnectedBot flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessRecipients = Updates; + + account.getConnectedBots = account.ConnectedBots; + + */ + +public final class TelegramAccountConnectedBot: Codable, Equatable { + public let id: PeerId + public let recipients: TelegramBusinessRecipients + public let canReply: Bool + + public init(id: PeerId, recipients: TelegramBusinessRecipients, canReply: Bool) { + self.id = id + self.recipients = recipients + self.canReply = canReply + } + + public static func ==(lhs: TelegramAccountConnectedBot, rhs: TelegramAccountConnectedBot) -> Bool { + if lhs === rhs { + return true + } + if lhs.id != rhs.id { + return false + } + if lhs.recipients != rhs.recipients { + return false + } + if lhs.canReply != rhs.canReply { + return false + } + return true + } +} + +public func _internal_setAccountConnectedBot(account: Account, bot: TelegramAccountConnectedBot?) -> Signal { + let remoteApply = account.postbox.transaction { transaction -> (Peer?, [Peer]) in + guard let bot else { + return (nil, []) + } + return (transaction.getPeer(bot.id), bot.recipients.additionalPeers.compactMap(transaction.getPeer)) + } + |> mapToSignal { botUser, additionalPeers in + var flags: Int32 = 0 + var mappedBot: Api.InputUser = .inputUserEmpty + var mappedRecipients: Api.InputBusinessRecipients = .inputBusinessRecipients(flags: 0, users: nil) + + if let bot, let inputBotUser = botUser.flatMap(apiInputUser) { + mappedBot = inputBotUser + if bot.canReply { + flags |= 1 << 0 + } + mappedRecipients = bot.recipients.apiInputValue(additionalPeers: additionalPeers) + } + + return account.network.request(Api.functions.account.updateConnectedBot(flags: flags, bot: mappedBot, recipients: mappedRecipients)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + if let result { + account.stateManager.addUpdates(result) + } + return .complete() + } + } + + return account.postbox.transaction { transaction in + transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, current in + var current = (current as? CachedUserData) ?? CachedUserData() + current = current.withUpdatedConnectedBot(bot) + return current + }) + } + |> ignoreValues + |> then(remoteApply) +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 0cd5c6b4a1..775b5d7c2e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -196,18 +196,28 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } else { editableBotInfo = .single(nil) } + + var additionalConnectedBots: Signal = .single(nil) + if rawPeerId == accountPeerId { + additionalConnectedBots = network.request(Api.functions.account.getConnectedBots()) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + } return combineLatest( network.request(Api.functions.users.getFullUser(id: inputUser)) |> retryRequest, - editableBotInfo + editableBotInfo, + additionalConnectedBots ) - |> mapToSignal { result, editableBotInfo -> Signal in + |> mapToSignal { result, editableBotInfo, additionalConnectedBots -> Signal in return postbox.transaction { transaction -> Bool in switch result { case let .userFull(fullUser, chats, users): var accountUser: Api.User? - let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + var parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for user in users { if user.peerId == accountPeerId { accountUser = user @@ -215,6 +225,26 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } let _ = accountUser + var mappedConnectedBot: TelegramAccountConnectedBot? + + if let additionalConnectedBots { + switch additionalConnectedBots { + case let .connectedBots(connectedBots, users): + parsedPeers = parsedPeers.union(with: AccumulatedPeers(transaction: transaction, chats: [], users: users)) + + if let apiBot = connectedBots.first { + switch apiBot { + case let .connectedBot(flags, botId, recipients): + mappedConnectedBot = TelegramAccountConnectedBot( + id: PeerId(botId), + recipients: TelegramBusinessRecipients(apiValue: recipients), + canReply: (flags & (1 << 0)) != 0 + ) + } + } + } + } + switch fullUser { case let .userFull(_, _, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) @@ -228,7 +258,7 @@ 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, stories, businessWorkHours, businessLocation, _, _): + case let .userFull(userFullFlags, _, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userPremiumGiftOptions, userWallpaper, stories, businessWorkHours, businessLocation, greetingMessage, awayMessage): let _ = stories let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:)) let isBlocked = (userFullFlags & (1 << 0)) != 0 @@ -296,6 +326,16 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee mappedBusinessLocation = TelegramBusinessLocation(apiLocation: businessLocation) } + var mappedGreetingMessage: TelegramBusinessGreetingMessage? + if let greetingMessage { + mappedGreetingMessage = TelegramBusinessGreetingMessage(apiGreetingMessage: greetingMessage) + } + + var mappedAwayMessage: TelegramBusinessAwayMessage? + if let awayMessage { + mappedAwayMessage = TelegramBusinessAwayMessage(apiAwayMessage: awayMessage) + } + return previous.withUpdatedAbout(userFullAbout) .withUpdatedBotInfo(botInfo) .withUpdatedEditableBotInfo(editableBotInfo) @@ -319,6 +359,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedFlags(flags) .withUpdatedBusinessHours(mappedBusinessHours) .withUpdatedBusinessLocation(mappedBusinessLocation) + .withUpdatedGreetingMessage(mappedGreetingMessage) + .withUpdatedAwayMessage(mappedAwayMessage) + .withUpdatedConnectedBot(mappedConnectedBot) } }) } diff --git a/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift index 9dadf28dc5..f4476df226 100644 --- a/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift @@ -9,6 +9,13 @@ import TextFieldComponent import AccountContext public final class ListMultilineTextFieldItemComponent: Component { + public final class ExternalState { + public fileprivate(set) var hasText: Bool = false + + public init() { + } + } + public final class ResetText: Equatable { public let value: String @@ -21,6 +28,7 @@ public final class ListMultilineTextFieldItemComponent: Component { } } + public let externalState: ExternalState? public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings @@ -31,9 +39,11 @@ public final class ListMultilineTextFieldItemComponent: Component { public let autocorrectionType: UITextAutocorrectionType public let characterLimit: Int? public let updated: ((String) -> Void)? + public let textUpdateTransition: Transition public let tag: AnyObject? public init( + externalState: ExternalState? = nil, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, @@ -44,8 +54,10 @@ public final class ListMultilineTextFieldItemComponent: Component { autocorrectionType: UITextAutocorrectionType = .default, characterLimit: Int? = nil, updated: ((String) -> Void)?, + textUpdateTransition: Transition = .immediate, tag: AnyObject? = nil ) { + self.externalState = externalState self.context = context self.theme = theme self.strings = strings @@ -56,10 +68,14 @@ public final class ListMultilineTextFieldItemComponent: Component { self.autocorrectionType = autocorrectionType self.characterLimit = characterLimit self.updated = updated + self.textUpdateTransition = textUpdateTransition self.tag = tag } public static func ==(lhs: ListMultilineTextFieldItemComponent, rhs: ListMultilineTextFieldItemComponent) -> Bool { + if lhs.externalState !== rhs.externalState { + return false + } if lhs.context !== rhs.context { return false } @@ -140,7 +156,7 @@ public final class ListMultilineTextFieldItemComponent: Component { @objc private func textDidChange() { if !self.isUpdating { - self.state?.updated(transition: .immediate) + self.state?.updated(transition: self.component?.textUpdateTransition ?? .immediate) } self.component?.updated?(self.currentText) } @@ -242,6 +258,8 @@ public final class ListMultilineTextFieldItemComponent: Component { self.separatorInset = 16.0 + component.externalState?.hasText = self.textFieldExternalState.hasText + return size } } diff --git a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift index 72cd2487a1..3f6e7c4265 100644 --- a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift @@ -9,8 +9,21 @@ import PlainButtonComponent import BundleIconComponent public final class ListTextFieldItemComponent: Component { + public final class ResetText: Equatable { + public let value: String + + public init(value: String) { + self.value = value + } + + public static func ==(lhs: ResetText, rhs: ResetText) -> Bool { + return lhs === rhs + } + } + public let theme: PresentationTheme public let initialText: String + public let resetText: ResetText? public let placeholder: String public let autocapitalizationType: UITextAutocapitalizationType public let autocorrectionType: UITextAutocorrectionType @@ -20,6 +33,7 @@ public final class ListTextFieldItemComponent: Component { public init( theme: PresentationTheme, initialText: String, + resetText: ResetText? = nil, placeholder: String, autocapitalizationType: UITextAutocapitalizationType = .sentences, autocorrectionType: UITextAutocorrectionType = .default, @@ -28,6 +42,7 @@ public final class ListTextFieldItemComponent: Component { ) { self.theme = theme self.initialText = initialText + self.resetText = resetText self.placeholder = placeholder self.autocapitalizationType = autocapitalizationType self.autocorrectionType = autocorrectionType @@ -42,6 +57,9 @@ public final class ListTextFieldItemComponent: Component { if lhs.initialText != rhs.initialText { return false } + if lhs.resetText !== rhs.resetText { + return false + } if lhs.placeholder != rhs.placeholder { return false } @@ -144,6 +162,9 @@ public final class ListTextFieldItemComponent: Component { self.textField.delegate = self self.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged) } + if let resetText = component.resetText, previousComponent?.resetText !== component.resetText { + self.textField.text = resetText.value + } if self.textField.autocapitalizationType != component.autocapitalizationType { self.textField.autocapitalizationType = component.autocapitalizationType diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD index 0e2891e840..b7f87546eb 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD @@ -47,6 +47,7 @@ swift_library( "//submodules/TelegramUI/Components/TimeSelectionActionSheet", "//submodules/TelegramUI/Components/ChatListHeaderComponent", "//submodules/AttachmentUI", + "//submodules/SearchBarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift index 8e5298fe9b..d4d24fd7e1 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift @@ -183,11 +183,7 @@ final class AutomaticBusinessMessageSetupChatContents: ChatCustomContentsProtoco let initialShortcut: String switch kind { - case .awayMessageInput: - initialShortcut = "_away" - case .greetingMessageInput: - initialShortcut = "_greeting" - case let .quickReplyMessageInput(shortcut): + case let .quickReplyMessageInput(shortcut, _): initialShortcut = shortcut } @@ -217,9 +213,12 @@ final class AutomaticBusinessMessageSetupChatContents: ChatCustomContentsProtoco } func quickReplyUpdateShortcut(value: String) { - self.kind = .quickReplyMessageInput(shortcut: value) - self.impl.with { impl in - impl.quickReplyUpdateShortcut(value: value) + switch self.kind { + case let .quickReplyMessageInput(_, shortcutType): + self.kind = .quickReplyMessageInput(shortcut: value, shortcutType: shortcutType) + self.impl.with { impl in + impl.quickReplyUpdateShortcut(value: value) + } } } } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift index e30630f5a3..37ec12c4b4 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift @@ -76,7 +76,7 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { } } - private struct AdditionalPeerList { + struct AdditionalPeerList { enum Category: Int { case newChats = 0 case existingChats = 1 @@ -148,6 +148,8 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { private var replyToMessages: Bool = true + private var inactivityDays: Int = 7 + override init(frame: CGRect) { self.scrollView = ScrollView() self.scrollView.showsVerticalScrollIndicator = true @@ -182,6 +184,66 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { } func attemptNavigation(complete: @escaping () -> Void) -> Bool { + guard let component = self.component else { + return true + } + + var mappedCategories: TelegramBusinessRecipients.Categories = [] + if self.additionalPeerList.categories.contains(.existingChats) { + mappedCategories.insert(.existingChats) + } + if self.additionalPeerList.categories.contains(.newChats) { + mappedCategories.insert(.newChats) + } + if self.additionalPeerList.categories.contains(.contacts) { + mappedCategories.insert(.contacts) + } + if self.additionalPeerList.categories.contains(.nonContacts) { + mappedCategories.insert(.nonContacts) + } + let recipients = TelegramBusinessRecipients( + categories: mappedCategories, + additionalPeers: Set(self.additionalPeerList.peers.map(\.peer.id)), + exclude: self.hasAccessToAllChatsByDefault + ) + + switch component.mode { + case .greeting: + var greetingMessage: TelegramBusinessGreetingMessage? + if self.isOn, let currentShortcut = self.currentShortcut { + greetingMessage = TelegramBusinessGreetingMessage( + shortcutId: currentShortcut.id, + recipients: recipients, + inactivityDays: self.inactivityDays + ) + } + let _ = component.context.engine.accountData.updateBusinessGreetingMessage(greetingMessage: greetingMessage).startStandalone() + case .away: + var awayMessage: TelegramBusinessAwayMessage? + if self.isOn, let currentShortcut = self.currentShortcut { + let mappedSchedule: TelegramBusinessAwayMessage.Schedule + switch self.schedule { + case .always: + mappedSchedule = .always + case .outsideBusinessHours: + mappedSchedule = .outsideWorkingHours + case .custom: + if let customScheduleStart = self.customScheduleStart, let customScheduleEnd = self.customScheduleEnd { + mappedSchedule = .custom(beginTimestamp: Int32(customScheduleStart.timeIntervalSince1970), endTimestamp: Int32(customScheduleEnd.timeIntervalSince1970)) + } else { + //TODO:localize + return false + } + } + awayMessage = TelegramBusinessAwayMessage( + shortcutId: currentShortcut.id, + recipients: recipients, + schedule: mappedSchedule + ) + } + let _ = component.context.engine.accountData.updateBusinessAwayMessage(awayMessage: awayMessage).startStandalone() + } + return true } @@ -236,14 +298,14 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { let additionalCategories: [ChatListNodeAdditionalCategory] = [ ChatListNodeAdditionalCategory( id: self.hasAccessToAllChatsByDefault ? AdditionalCategoryId.existingChats.rawValue : AdditionalCategoryId.newChats.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), cornerRadius: 12.0, color: .purple), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .purple), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: self.hasAccessToAllChatsByDefault ? "Chat List/Filters/Chats" : "Chat List/Filters/NewChats"), color: .white), cornerRadius: 12.0, color: .purple), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: self.hasAccessToAllChatsByDefault ? "Chat List/Filters/Chats" : "Chat List/Filters/NewChats"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .purple), title: self.hasAccessToAllChatsByDefault ? "Existing Chats" : "New Chats" ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.contacts.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), cornerRadius: 12.0, color: .blue), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), cornerRadius: 12.0, color: .blue), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), title: "Contacts" ), ChatListNodeAdditionalCategory( @@ -354,16 +416,19 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { } let shortcutName: String + let shortcutType: ChatQuickReplyShortcutType switch component.mode { case .greeting: shortcutName = "hello" + shortcutType = .greeting case .away: shortcutName = "away" + shortcutType = .away } let contents = AutomaticBusinessMessageSetupChatContents( context: component.context, - kind: .quickReplyMessageInput(shortcut: shortcutName), + kind: .quickReplyMessageInput(shortcut: shortcutName, shortcutType: shortcutType), shortcutId: self.currentShortcut?.id ) let chatController = component.context.sharedContext.makeChatController( @@ -471,13 +536,58 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { if self.component == nil { self.accountPeer = component.initialData.accountPeer + var initialRecipients: TelegramBusinessRecipients? + let shortcutName: String switch component.mode { case .greeting: shortcutName = "hello" + + if let greetingMessage = component.initialData.greetingMessage { + self.isOn = true + initialRecipients = greetingMessage.recipients + + self.inactivityDays = greetingMessage.inactivityDays + } case .away: shortcutName = "away" + + if let awayMessage = component.initialData.awayMessage { + self.isOn = true + initialRecipients = awayMessage.recipients + } } + + if let initialRecipients { + var mappedCategories = Set() + if initialRecipients.categories.contains(.existingChats) { + mappedCategories.insert(.existingChats) + } + if initialRecipients.categories.contains(.newChats) { + mappedCategories.insert(.newChats) + } + if initialRecipients.categories.contains(.contacts) { + mappedCategories.insert(.contacts) + } + if initialRecipients.categories.contains(.nonContacts) { + mappedCategories.insert(.nonContacts) + } + + var additionalPeers: [AdditionalPeerList.Peer] = [] + for peerId in initialRecipients.additionalPeers { + if let peer = component.initialData.additionalPeers[peerId] { + additionalPeers.append(peer) + } + } + + self.additionalPeerList = AdditionalPeerList( + categories: mappedCategories, + peers: additionalPeers + ) + + self.hasAccessToAllChatsByDefault = initialRecipients.exclude + } + self.currentShortcut = component.initialData.shortcutMessageList.items.first(where: { $0.shortcut == shortcutName }) self.currentShortcutDisposable = (component.context.engine.accountData.shortcutMessageList() @@ -532,9 +642,6 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { let sideInset: CGFloat = 16.0 + environment.safeInsets.left let sectionSpacing: CGFloat = 32.0 - let _ = bottomContentInset - let _ = sectionSpacing - var contentHeight: CGFloat = 0.0 contentHeight += environment.navigationHeight @@ -543,21 +650,22 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: component.mode == .greeting ? "HandWaveEmoji" : "ZzzEmoji"), - loop: true + loop: false )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) ) - let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 2.0), size: iconSize) - if let iconView = self.icon.view { + let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 8.0), size: iconSize) + if let iconView = self.icon.view as? LottieComponent.View { if iconView.superview == nil { self.scrollView.addSubview(iconView) + iconView.playOnce() } transition.setPosition(view: iconView, position: iconFrame.center) iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) } - contentHeight += 129.0 + contentHeight += 124.0 //TODO:localize let subtitleString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(component.mode == .greeting ? "Greet customers when they message you the first time or after a period of no activity." : "Automatically reply with a message when you are away.", attributes: MarkdownAttributes( @@ -1163,6 +1271,19 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { otherSectionsHeight += sectionSpacing if case .greeting = component.mode { + var selectedInactivityIndex = 0 + let valueList: [Int] = [ + 7, + 14, + 21, + 28 + ] + for i in 0 ..< valueList.count { + if valueList[i] <= self.inactivityDays { + selectedInactivityIndex = i + } + } + let periodSectionSize = self.periodSection.update( transition: transition, component: AnyComponent(ListSectionComponent( @@ -1192,12 +1313,14 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { "21 days", "28 days" ], - selectedIndex: 0, + selectedIndex: selectedInactivityIndex, selectedIndexUpdated: { [weak self] index in guard let self else { return } - let _ = self + let index = max(0, min(valueList.count - 1, index)) + self.inactivityDays = valueList[index] + self.state?.updated(transition: .immediate) } ))) ] @@ -1268,15 +1391,24 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentContainer { public final class InitialData: AutomaticBusinessMessageSetupScreenInitialData { - let accountPeer: EnginePeer? - let shortcutMessageList: ShortcutMessageList + fileprivate let accountPeer: EnginePeer? + fileprivate let shortcutMessageList: ShortcutMessageList + fileprivate let greetingMessage: TelegramBusinessGreetingMessage? + fileprivate let awayMessage: TelegramBusinessAwayMessage? + fileprivate let additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer] - init( + fileprivate init( accountPeer: EnginePeer?, - shortcutMessageList: ShortcutMessageList + shortcutMessageList: ShortcutMessageList, + greetingMessage: TelegramBusinessGreetingMessage?, + awayMessage: TelegramBusinessAwayMessage?, + additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer] ) { self.accountPeer = accountPeer self.shortcutMessageList = shortcutMessageList + self.greetingMessage = greetingMessage + self.awayMessage = awayMessage + self.additionalPeers = additionalPeers } } @@ -1334,16 +1466,48 @@ public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentC public static func initialData(context: AccountContext) -> Signal { return combineLatest( context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId) + TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), + TelegramEngine.EngineData.Item.Peer.BusinessGreetingMessage(id: context.account.peerId), + TelegramEngine.EngineData.Item.Peer.BusinessAwayMessage(id: context.account.peerId) ), context.engine.accountData.shortcutMessageList() |> take(1) ) - |> map { accountPeer, shortcutMessageList -> AutomaticBusinessMessageSetupScreenInitialData in - return InitialData( - accountPeer: accountPeer, - shortcutMessageList: shortcutMessageList + |> mapToSignal { data, shortcutMessageList -> Signal in + let (accountPeer, greetingMessage, awayMessage) = data + + var additionalPeerIds = Set() + if let greetingMessage { + additionalPeerIds.formUnion(greetingMessage.recipients.additionalPeers) + } + if let awayMessage { + additionalPeerIds.formUnion(awayMessage.recipients.additionalPeers) + } + + return context.engine.data.get( + EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))), + EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.IsContact.init(id:))) ) + |> map { peers, isContacts -> AutomaticBusinessMessageSetupScreenInitialData in + var additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer] = [:] + for id in additionalPeerIds { + guard let peer = peers[id], let peer else { + continue + } + additionalPeers[id] = AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer( + peer: peer, + isContact: isContacts[id] ?? false + ) + } + + return InitialData( + accountPeer: accountPeer, + shortcutMessageList: shortcutMessageList, + greetingMessage: greetingMessage, + awayMessage: awayMessage, + additionalPeers: additionalPeers + ) + } } } } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index bd555555bd..3dbc6c29a1 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -22,6 +22,7 @@ import ChatListHeaderComponent import PlainButtonComponent import MultilineTextComponent import AttachmentUI +import SearchBarNode final class QuickReplySetupScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -453,6 +454,8 @@ final class QuickReplySetupScreenComponent: Component { var options: ListViewDeleteAndInsertOptions = [.Synchronous, .LowLatency] if animated { options.insert(.AnimateInsertion) + } else { + options.insert(.PreferSynchronousResourceLoading) } self.transaction( @@ -476,6 +479,8 @@ final class QuickReplySetupScreenComponent: Component { private let navigationBarView = ComponentView() private var navigationHeight: CGFloat? + private var searchBarNode: SearchBarNode? + private var selectionPanel: ComponentView? private var isUpdating: Bool = false @@ -492,10 +497,14 @@ final class QuickReplySetupScreenComponent: Component { private var isEditing: Bool = false private var isSearchDisplayControllerActive: Bool = false + private var searchQuery: String = "" + private let searchQueryComponentSeparationCharacterSet: CharacterSet private var accountPeer: EnginePeer? override init(frame: CGRect) { + self.searchQueryComponentSeparationCharacterSet = CharacterSet(charactersIn: " _.:/") + super.init(frame: frame) } @@ -528,9 +537,18 @@ final class QuickReplySetupScreenComponent: Component { } if let shortcut { + let shortcutType: ChatQuickReplyShortcutType + if shortcut == "hello" { + shortcutType = .greeting + } else if shortcut == "away" { + shortcutType = .away + } else { + shortcutType = .generic + } + let contents = AutomaticBusinessMessageSetupChatContents( context: component.context, - kind: .quickReplyMessageInput(shortcut: shortcut), + kind: .quickReplyMessageInput(shortcut: shortcut, shortcutType: shortcutType), shortcutId: shortcutId ) let chatController = component.context.sharedContext.makeChatController( @@ -779,9 +797,8 @@ final class QuickReplySetupScreenComponent: Component { return } - let _ = self - //self.isSearchDisplayControllerActive = true - //self.state?.updated(transition: .spring(duration: 0.4)) + self.isSearchDisplayControllerActive = true + self.state?.updated(transition: .spring(duration: 0.4)) }, openStatusSetup: { _ in }, @@ -952,6 +969,84 @@ final class QuickReplySetupScreenComponent: Component { ) self.navigationHeight = navigationHeight + var removedSearchBar: SearchBarNode? + if self.isSearchDisplayControllerActive { + let searchBarNode: SearchBarNode + var searchBarTransition = transition + if let current = self.searchBarNode { + searchBarNode = current + } else { + searchBarTransition = .immediate + let searchBarTheme = SearchBarNodeTheme(theme: environment.theme, hasSeparator: false) + searchBarNode = SearchBarNode( + theme: searchBarTheme, + strings: environment.strings, + fieldStyle: .modern, + displayBackground: false + ) + searchBarNode.placeholderString = NSAttributedString(string: environment.strings.Common_Search, font: Font.regular(17.0), textColor: searchBarTheme.placeholder) + self.searchBarNode = searchBarNode + searchBarNode.cancel = { [weak self] in + guard let self else { + return + } + self.isSearchDisplayControllerActive = false + self.state?.updated(transition: .spring(duration: 0.4)) + } + searchBarNode.textUpdated = { [weak self] query, _ in + guard let self else { + return + } + if self.searchQuery != query { + self.searchQuery = query.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) + self.state?.updated(transition: .immediate) + } + } + DispatchQueue.main.async { [weak self, weak searchBarNode] in + guard let self, let searchBarNode, self.searchBarNode === searchBarNode else { + return + } + searchBarNode.activate() + + if let controller = self.environment?.controller() as? QuickReplySetupScreen { + controller.requestAttachmentMenuExpansion() + } + } + } + + var searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight - 54.0 + 2.0), size: CGSize(width: availableSize.width, height: 54.0)) + if isModal { + searchBarFrame.origin.y += 2.0 + } + searchBarNode.updateLayout(boundingSize: searchBarFrame.size, leftInset: environment.safeInsets.left + 6.0, rightInset: environment.safeInsets.right, transition: searchBarTransition.containedViewLayoutTransition) + searchBarTransition.setFrame(view: searchBarNode.view, frame: searchBarFrame) + if searchBarNode.view.superview == nil { + self.addSubview(searchBarNode.view) + + if case let .curve(duration, curve) = transition.animation, let navigationBarView = self.navigationBarView.view as? ChatListNavigationBar.View, let placeholderNode = navigationBarView.searchContentNode?.placeholderNode { + let timingFunction: String + switch curve { + case .easeInOut: + timingFunction = CAMediaTimingFunctionName.easeOut.rawValue + case .linear: + timingFunction = CAMediaTimingFunctionName.linear.rawValue + case .spring: + timingFunction = kCAMediaTimingFunctionSpring + case .custom: + timingFunction = kCAMediaTimingFunctionSpring + } + + searchBarNode.animateIn(from: placeholderNode, duration: duration, timingFunction: timingFunction) + } + } + } else { + self.searchQuery = "" + if let searchBarNode = self.searchBarNode { + self.searchBarNode = nil + removedSearchBar = searchBarNode + } + } + if !self.selectedIds.isEmpty { let selectionPanel: ComponentView var selectionPanelTransition = transition @@ -1058,11 +1153,25 @@ final class QuickReplySetupScreenComponent: Component { if let shortcutMessageList = self.shortcutMessageList, let accountPeer = self.accountPeer { switch component.mode { case .manage: - entries.append(.add) + if self.searchQuery.isEmpty { + entries.append(.add) + } case .select: break } for item in shortcutMessageList.items { + if !self.searchQuery.isEmpty { + var matches = false + inner: for nameComponent in item.shortcut.lowercased().components(separatedBy: self.searchQueryComponentSeparationCharacterSet) { + if nameComponent.lowercased().hasPrefix(self.searchQuery) { + matches = true + break inner + } + } + if !matches { + continue + } + } entries.append(.item(item: item, accountPeer: accountPeer, sortIndex: entries.count, isEditing: self.isEditing, isSelected: self.selectedIds.contains(item.id))) } } @@ -1081,6 +1190,17 @@ final class QuickReplySetupScreenComponent: Component { navigationBarComponentView.applyCurrentScroll(transition: transition) } + if let removedSearchBar { + if !transition.animation.isImmediate, let navigationBarView = self.navigationBarView.view as? ChatListNavigationBar.View, let placeholderNode = + navigationBarView.searchContentNode?.placeholderNode { + removedSearchBar.transitionOut(to: placeholderNode, transition: transition.containedViewLayoutTransition, completion: { [weak removedSearchBar] in + removedSearchBar?.view.removeFromSuperview() + }) + } else { + removedSearchBar.view.removeFromSuperview() + } + } + return availableSize } } diff --git a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift index 3e8acde476..334ac82839 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift @@ -446,15 +446,16 @@ final class BusinessHoursSetupScreenComponent: Component { transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: "BusinessHoursEmoji"), - loop: true + loop: false )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) ) let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 10.0), size: iconSize) - if let iconView = self.icon.view { + if let iconView = self.icon.view as? LottieComponent.View { if iconView.superview == nil { self.scrollView.addSubview(iconView) + iconView.playOnce() } transition.setPosition(view: iconView, position: iconFrame.center) iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) diff --git a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift index d2a203000e..7754e8382b 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift @@ -66,6 +66,7 @@ final class BusinessLocationSetupScreenComponent: Component { private let subtitle = ComponentView() private let addressSection = ComponentView() private let mapSection = ComponentView() + private let deleteSection = ComponentView() private var isUpdating: Bool = false @@ -73,6 +74,7 @@ final class BusinessLocationSetupScreenComponent: Component { private(set) weak var state: EmptyComponentState? private var environment: EnvironmentType? + private let addressTextInputState = ListMultilineTextFieldItemComponent.ExternalState() private let textFieldTag = NSObject() private var resetAddressText: String? @@ -271,6 +273,13 @@ final class BusinessLocationSetupScreenComponent: Component { self.component = component self.state = state + let alphaTransition: Transition + if !transition.animation.isImmediate { + alphaTransition = .easeInOut(duration: 0.25) + } else { + alphaTransition = .immediate + } + if themeUpdated { self.backgroundColor = environment.theme.list.blocksBackgroundColor } @@ -309,15 +318,16 @@ final class BusinessLocationSetupScreenComponent: Component { transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: "MapEmoji"), - loop: true + loop: false )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) ) let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 11.0), size: iconSize) - if let iconView = self.icon.view { + if let iconView = self.icon.view as? LottieComponent.View { if iconView.superview == nil { self.scrollView.addSubview(iconView) + iconView.playOnce() } transition.setPosition(view: iconView, position: iconFrame.center) iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) @@ -375,6 +385,7 @@ final class BusinessLocationSetupScreenComponent: Component { //TODO:localize var addressSectionItems: [AnyComponentWithIdentity] = [] addressSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListMultilineTextFieldItemComponent( + externalState: self.addressTextInputState, context: component.context, theme: environment.theme, strings: environment.strings, @@ -388,6 +399,7 @@ final class BusinessLocationSetupScreenComponent: Component { characterLimit: 64, updated: { _ in }, + textUpdateTransition: .spring(duration: 0.4), tag: self.textFieldTag )))) self.resetAddressText = nil @@ -481,6 +493,66 @@ final class BusinessLocationSetupScreenComponent: Component { } contentHeight += mapSectionSize.height + var deleteSectionHeight: CGFloat = 0.0 + + deleteSectionHeight += sectionSpacing + //TODO:localize + let deleteSectionSize = self.deleteSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: nil, + footer: nil, + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(VStack([ + AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Delete Location", + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemDestructiveColor + )), + maximumNumberOfLines: 1 + ))), + ], alignment: .left, spacing: 2.0)), + accessory: nil, + action: { [weak self] _ in + guard let self else { + return + } + + self.resetAddressText = "" + self.mapCoordinates = nil + self.mapCoordinatesManuallySet = false + self.state?.updated(transition: .spring(duration: 0.4)) + } + ))) + ], + displaySeparators: false + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) + ) + let deleteSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight + deleteSectionHeight), size: deleteSectionSize) + if let deleteSectionView = self.deleteSection.view { + if deleteSectionView.superview == nil { + self.scrollView.addSubview(deleteSectionView) + } + transition.setFrame(view: deleteSectionView, frame: deleteSectionFrame) + + if self.mapCoordinates != nil || self.addressTextInputState.hasText { + alphaTransition.setAlpha(view: deleteSectionView, alpha: 1.0) + } else { + alphaTransition.setAlpha(view: deleteSectionView, alpha: 0.0) + } + } + deleteSectionHeight += deleteSectionSize.height + + if self.mapCoordinates != nil || self.addressTextInputState.hasText { + contentHeight += deleteSectionHeight + } + contentHeight += bottomContentInset contentHeight += environment.safeInsets.bottom diff --git a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift index ee471bd9d6..8e6a60cab9 100644 --- a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift @@ -41,11 +41,14 @@ final class ChatbotSetupScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let initialData: ChatbotSetupScreen.InitialData init( - context: AccountContext + context: AccountContext, + initialData: ChatbotSetupScreen.InitialData ) { self.context = context + self.initialData = initialData } static func ==(lhs: ChatbotSetupScreenComponent, rhs: ChatbotSetupScreenComponent) -> Bool { @@ -78,7 +81,7 @@ final class ChatbotSetupScreenComponent: Component { } } - private struct AdditionalPeerList { + struct AdditionalPeerList { enum Category: Int { case newChats = 0 case existingChats = 1 @@ -128,6 +131,7 @@ final class ChatbotSetupScreenComponent: Component { private var botResolutionState: BotResolutionState? private var botResolutionDisposable: Disposable? + private var resetQueryText: String? private var hasAccessToAllChatsByDefault: Bool = true private var additionalPeerList = AdditionalPeerList( @@ -170,6 +174,39 @@ final class ChatbotSetupScreenComponent: Component { } func attemptNavigation(complete: @escaping () -> Void) -> Bool { + guard let component = self.component else { + return true + } + + var mappedCategories: TelegramBusinessRecipients.Categories = [] + if self.additionalPeerList.categories.contains(.existingChats) { + mappedCategories.insert(.existingChats) + } + if self.additionalPeerList.categories.contains(.newChats) { + mappedCategories.insert(.newChats) + } + if self.additionalPeerList.categories.contains(.contacts) { + mappedCategories.insert(.contacts) + } + if self.additionalPeerList.categories.contains(.nonContacts) { + mappedCategories.insert(.nonContacts) + } + let recipients = TelegramBusinessRecipients( + categories: mappedCategories, + additionalPeers: Set(self.additionalPeerList.peers.map(\.peer.id)), + exclude: self.hasAccessToAllChatsByDefault + ) + + if let botResolutionState = self.botResolutionState, case let .found(peer, isInstalled) = botResolutionState.state, isInstalled { + let _ = component.context.engine.accountData.setAccountConnectedBot(bot: TelegramAccountConnectedBot( + id: peer.id, + recipients: recipients, + canReply: self.replyToMessages + )).startStandalone() + } else { + let _ = component.context.engine.accountData.setAccountConnectedBot(bot: nil).startStandalone() + } + return true } @@ -273,14 +310,14 @@ final class ChatbotSetupScreenComponent: Component { let additionalCategories: [ChatListNodeAdditionalCategory] = [ ChatListNodeAdditionalCategory( id: self.hasAccessToAllChatsByDefault ? AdditionalCategoryId.existingChats.rawValue : AdditionalCategoryId.newChats.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), cornerRadius: 12.0, color: .purple), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .purple), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: self.hasAccessToAllChatsByDefault ? "Chat List/Filters/Chats" : "Chat List/Filters/NewChats"), color: .white), cornerRadius: 12.0, color: .purple), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: self.hasAccessToAllChatsByDefault ? "Chat List/Filters/Chats" : "Chat List/Filters/NewChats"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .purple), title: self.hasAccessToAllChatsByDefault ? "Existing Chats" : "New Chats" ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.contacts.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), cornerRadius: 12.0, color: .blue), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), cornerRadius: 12.0, color: .blue), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), title: "Contacts" ), ChatListNodeAdditionalCategory( @@ -391,6 +428,45 @@ final class ChatbotSetupScreenComponent: Component { self.isUpdating = false } + if self.component == nil { + if let bot = component.initialData.bot, let botPeer = component.initialData.botPeer, let addressName = botPeer.addressName { + self.botResolutionState = BotResolutionState(query: addressName, state: .found(peer: botPeer, isInstalled: true)) + self.resetQueryText = addressName.lowercased() + + self.replyToMessages = bot.canReply + + let initialRecipients = bot.recipients + + var mappedCategories = Set() + if initialRecipients.categories.contains(.existingChats) { + mappedCategories.insert(.existingChats) + } + if initialRecipients.categories.contains(.newChats) { + mappedCategories.insert(.newChats) + } + if initialRecipients.categories.contains(.contacts) { + mappedCategories.insert(.contacts) + } + if initialRecipients.categories.contains(.nonContacts) { + mappedCategories.insert(.nonContacts) + } + + var additionalPeers: [AdditionalPeerList.Peer] = [] + for peerId in initialRecipients.additionalPeers { + if let peer = component.initialData.additionalPeers[peerId] { + additionalPeers.append(peer) + } + } + + self.additionalPeerList = AdditionalPeerList( + categories: mappedCategories, + peers: additionalPeers + ) + + self.hasAccessToAllChatsByDefault = initialRecipients.exclude + } + } + let environment = environment[EnvironmentType.self].value let themeUpdated = self.environment?.theme !== environment.theme self.environment = environment @@ -439,15 +515,16 @@ final class ChatbotSetupScreenComponent: Component { transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: "BotEmoji"), - loop: true + loop: false )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) ) - let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 2.0), size: iconSize) - if let iconView = self.icon.view { + let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 8.0), size: iconSize) + if let iconView = self.icon.view as? LottieComponent.View { if iconView.superview == nil { self.scrollView.addSubview(iconView) + iconView.playOnce() } transition.setPosition(view: iconView, position: iconFrame.center) iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) @@ -456,7 +533,7 @@ final class ChatbotSetupScreenComponent: Component { contentHeight += 129.0 //TODO:localize - let subtitleString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("Add a bot to your account to help you automatically process and respond to the messages you receive. [Learn More]()", attributes: MarkdownAttributes( + let subtitleString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("Add a bot to your account to help you automatically process and respond to the messages you receive. [Learn More >]()", attributes: MarkdownAttributes( body: MarkdownAttributeSet(font: Font.regular(15.0), textColor: environment.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: Font.semibold(15.0), textColor: environment.theme.list.freeTextColor), link: MarkdownAttributeSet(font: Font.regular(15.0), textColor: environment.theme.list.itemAccentColor), @@ -491,7 +568,8 @@ final class ChatbotSetupScreenComponent: Component { guard let self, let component = self.component else { return } - let _ = component + //TODO:localize + component.context.sharedContext.applicationBindings.openUrl("https://telegram.org") } )), environment: {}, @@ -508,10 +586,13 @@ final class ChatbotSetupScreenComponent: Component { contentHeight += subtitleSize.height contentHeight += 27.0 + let resetQueryText = self.resetQueryText + self.resetQueryText = nil var nameSectionItems: [AnyComponentWithIdentity] = [] nameSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListTextFieldItemComponent( theme: environment.theme, initialText: "", + resetText: resetQueryText.flatMap { ListTextFieldItemComponent.ResetText(value: $0) }, placeholder: "Bot Username", autocapitalizationType: .none, autocorrectionType: .no, @@ -918,13 +999,30 @@ final class ChatbotSetupScreenComponent: Component { } public final class ChatbotSetupScreen: ViewControllerComponentContainer { + public final class InitialData: ChatbotSetupScreenInitialData { + fileprivate let bot: TelegramAccountConnectedBot? + fileprivate let botPeer: EnginePeer? + fileprivate let additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer] + + fileprivate init( + bot: TelegramAccountConnectedBot?, + botPeer: EnginePeer?, + additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer] + ) { + self.bot = bot + self.botPeer = botPeer + self.additionalPeers = additionalPeers + } + } + private let context: AccountContext - public init(context: AccountContext) { + public init(context: AccountContext, initialData: InitialData) { self.context = context super.init(context: context, component: ChatbotSetupScreenComponent( - context: context + context: context, + initialData: initialData ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil) let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -961,4 +1059,48 @@ public final class ChatbotSetupScreen: ViewControllerComponentContainer { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) } + + public static func initialData(context: AccountContext) -> Signal { + return context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.BusinessConnectedBot(id: context.account.peerId) + ) + |> mapToSignal { connectedBot -> Signal in + guard let connectedBot else { + return .single( + InitialData( + bot: nil, + botPeer: nil, + additionalPeers: [:] + ) + ) + } + + var additionalPeerIds = Set() + additionalPeerIds.formUnion(connectedBot.recipients.additionalPeers) + + return context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: connectedBot.id), + EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))), + EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.IsContact.init(id:))) + ) + |> map { botPeer, peers, isContacts -> ChatbotSetupScreenInitialData in + var additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer] = [:] + for id in additionalPeerIds { + guard let peer = peers[id], let peer else { + continue + } + additionalPeers[id] = ChatbotSetupScreenComponent.AdditionalPeerList.Peer( + peer: peer, + isContact: isContacts[id] ?? false + ) + } + + return InitialData( + bot: connectedBot, + botPeer: botPeer, + additionalPeers: additionalPeers + ) + } + } + } } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift index 9c0e7060b8..c262788162 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift @@ -13,33 +13,35 @@ import AccountContext import ListItemComponentAdaptor private class PeerNameColorIconItem { - let index: PeerNameColor - let colors: PeerNameColors.Colors + let index: PeerNameColor? + let colors: PeerNameColors.Colors? let isDark: Bool let selected: Bool - let action: (PeerNameColor) -> Void + let isLocked: Bool + let action: (PeerNameColor?) -> Void - public init(index: PeerNameColor, colors: PeerNameColors.Colors, isDark: Bool, selected: Bool, action: @escaping (PeerNameColor) -> Void) { + public init(index: PeerNameColor?, colors: PeerNameColors.Colors?, isDark: Bool, selected: Bool, isLocked: Bool, action: @escaping (PeerNameColor?) -> Void) { self.index = index self.colors = colors self.isDark = isDark self.selected = selected + self.isLocked = isLocked self.action = action } } -private func generateRingImage(nameColor: PeerNameColors.Colors, size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { +private func generateRingImage(color: UIColor, size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { return generateImage(size, rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) - context.setStrokeColor(nameColor.main.cgColor) + context.setStrokeColor(color.cgColor) context.setLineWidth(2.0) context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0)) }) } -public func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: Bool, bounds: CGSize = CGSize(width: 40.0, height: 40.0), size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { +public func generatePeerNameColorImage(nameColor: PeerNameColors.Colors?, isDark: Bool, isLocked: Bool = false, bounds: CGSize = CGSize(width: 40.0, height: 40.0), size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? { return generateImage(bounds, rotatedContext: { contextSize, context in let bounds = CGRect(origin: CGPoint(), size: contextSize) context.clear(bounds) @@ -48,7 +50,7 @@ public func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: context.addEllipse(in: circleBounds) context.clip() - if let secondColor = nameColor.secondary { + if let nameColor, let secondColor = nameColor.secondary { var firstColor = nameColor.main var secondColor = secondColor if isDark, nameColor.tertiary == nil { @@ -84,9 +86,32 @@ public func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: context.setFillColor(firstColor.cgColor) context.fillPath() } - } else { + } else if let nameColor { context.setFillColor(nameColor.main.cgColor) context.fill(circleBounds) + } else { + context.setFillColor(UIColor(rgb: 0x798896).cgColor) + context.fill(circleBounds) + } + + if isLocked { + if let image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon") { + let scaleFactor: CGFloat = 1.58 + let imageSize = CGSize(width: floor(image.size.width * scaleFactor), height: floor(image.size.height * scaleFactor)) + var imageFrame = CGRect(origin: CGPoint(x: circleBounds.minX + floor((circleBounds.width - imageSize.width) * 0.5), y: circleBounds.minY + floor((circleBounds.height - imageSize.height) * 0.5)), size: imageSize) + imageFrame.origin.y += -0.5 + + context.translateBy(x: imageFrame.midX, y: imageFrame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY) + + if let cgImage = image.cgImage { + context.clip(to: imageFrame, mask: cgImage) + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + context.fill(imageFrame) + } + } } }) } @@ -187,8 +212,8 @@ private final class PeerNameColorIconItemNode : ASDisplayNode { self.item = item if updatedAccentColor { - self.fillNode.image = generatePeerNameColorImage(nameColor: item.colors, isDark: item.isDark, bounds: size, size: size) - self.ringNode.image = generateRingImage(nameColor: item.colors, size: size) + self.fillNode.image = generatePeerNameColorImage(nameColor: item.colors, isDark: item.isDark, isLocked: item.selected && item.isLocked, bounds: size, size: size) + self.ringNode.image = generateRingImage(color: item.colors?.main ?? UIColor(rgb: 0x798896), size: size) } let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0) @@ -213,14 +238,18 @@ public final class PeerNameColorItem: ListViewItem, ItemListItem, ListItemCompon public let theme: PresentationTheme public let colors: PeerNameColors public let isProfile: Bool + public let displayEmptyColor: Bool + public let isLocked: Bool public let currentColor: PeerNameColor? - public let updated: (PeerNameColor) -> Void + public let updated: (PeerNameColor?) -> Void public let tag: ItemListItemTag? - public init(theme: PresentationTheme, colors: PeerNameColors, isProfile: Bool, currentColor: PeerNameColor?, updated: @escaping (PeerNameColor) -> Void, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId) { + public init(theme: PresentationTheme, colors: PeerNameColors, isProfile: Bool, displayEmptyColor: Bool = false, currentColor: PeerNameColor?, isLocked: Bool = false, updated: @escaping (PeerNameColor?) -> Void, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId) { self.theme = theme self.colors = colors self.isProfile = isProfile + self.displayEmptyColor = displayEmptyColor + self.isLocked = isLocked self.currentColor = currentColor self.updated = updated self.tag = tag @@ -341,8 +370,12 @@ public final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { displayOrder = item.colors.displayOrder itemsPerRow = 7 } + var numItems = displayOrder.count + if item.displayEmptyColor { + numItems += 1 + } - let rowsCount = ceil(CGFloat(displayOrder.count) / CGFloat(itemsPerRow)) + let rowsCount = ceil(CGFloat(numItems) / CGFloat(itemsPerRow)) contentSize = CGSize(width: params.width, height: 48.0 * rowsCount) insets = itemListNeighborsGroupedInsets(neighbors, params) @@ -419,12 +452,17 @@ public final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - let action: (PeerNameColor) -> Void = { color in + let action: (PeerNameColor?) -> Void = { color in item.updated(color) } var items: [PeerNameColorIconItem] = [] var i: Int = 0 + if item.displayEmptyColor { + items.append(PeerNameColorIconItem(index: nil, colors: nil, isDark: item.theme.overallDarkAppearance, selected: item.currentColor == nil, isLocked: item.isLocked, action: action)) + i += 1 + } + for index in displayOrder { let color = PeerNameColor(rawValue: index) let colors: PeerNameColors.Colors @@ -434,7 +472,7 @@ public final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { colors = item.colors.get(color, dark: item.theme.overallDarkAppearance) } - items.append(PeerNameColorIconItem(index: color, colors: colors, isDark: item.theme.overallDarkAppearance, selected: color == item.currentColor, action: action)) + items.append(PeerNameColorIconItem(index: color, colors: colors, isDark: item.theme.overallDarkAppearance, selected: color == item.currentColor, isLocked: item.isLocked, action: action)) i += 1 } strongSelf.items = items @@ -449,11 +487,17 @@ public final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { i = 0 for item in items { let iconItemNode: PeerNameColorIconItemNode - if let current = strongSelf.itemNodes[item.index.rawValue] { + let indexKey: Int32 + if let index = item.index { + indexKey = index.rawValue + } else { + indexKey = Int32.min + } + if let current = strongSelf.itemNodes[indexKey] { iconItemNode = current } else { iconItemNode = PeerNameColorIconItemNode() - strongSelf.itemNodes[item.index.rawValue] = iconItemNode + strongSelf.itemNodes[indexKey] = iconItemNode strongSelf.containerNode.addSubnode(iconItemNode) } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift index 5be45a1482..7f5576ae36 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift @@ -1264,7 +1264,7 @@ final class ChannelAppearanceScreenComponent: Component { isProfile: true, currentColor: profileColor, updated: { [weak self] value in - guard let self else { + guard let self, let value else { return } self.updatedPeerProfileColor = value @@ -1581,7 +1581,7 @@ final class ChannelAppearanceScreenComponent: Component { isProfile: false, currentColor: resolvedState.nameColor, updated: { [weak self] value in - guard let self else { + guard let self, let value else { return } self.updatedPeerNameColor = value diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift index 945edb7b48..40fa333d30 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift @@ -215,7 +215,9 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { isProfile: isProfile, currentColor: currentColor, updated: { color in - arguments.updateNameColor(color) + if let color { + arguments.updateNameColor(color) + } }, sectionId: self.section ) diff --git a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift index a07d36bff0..6779c39757 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift @@ -28,6 +28,8 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0) private let inputInsets: UIEdgeInsets + private let validCharacterSets: [CharacterSet] + var text: String { get { return self.textInputNode.attributedText?.string ?? "" @@ -73,6 +75,11 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg self.placeholderNode.displaysAsynchronously = false self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(13.0), textColor: self.theme.actionSheet.inputPlaceholderColor) + self.validCharacterSets = [ + CharacterSet.alphanumerics, + CharacterSet(charactersIn: "0123456789_"), + ] + super.init() self.textInputNode.delegate = self @@ -158,6 +165,13 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg self.complete?() return false } + if text.unicodeScalars.contains(where: { c in + return !self.validCharacterSets.contains(where: { set in + return set.contains(c) + }) + }) { + return false + } return true } diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index d0af5a34fc..0b79cb0ebb 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -590,7 +590,7 @@ public final class StoryPeerListComponent: Component { case .premium: statusContent = .premium(color: component.theme.list.itemAccentColor) case let .emoji(emoji): - statusContent = .animation(content: .customEmoji(fileId: emoji.fileId), size: CGSize(width: 22.0, height: 22.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) + statusContent = .animation(content: .customEmoji(fileId: emoji.fileId), size: CGSize(width: 44.0, height: 44.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) } var animateStatusTransition = false diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/Contents.json new file mode 100644 index 0000000000..640ffa7fd9 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "existing.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/existing.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/existing.pdf new file mode 100644 index 0000000000..455ed1a7c1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Chats.imageset/existing.pdf @@ -0,0 +1,97 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 8.400024 9.480713 cm +0.000000 0.000000 0.000000 scn +15.112180 17.693565 m +13.513130 19.189789 11.275981 20.119385 8.800000 20.119385 c +3.939894 20.119385 0.000000 16.537663 0.000000 12.119385 c +0.000000 9.599503 1.177064 7.533977 3.179985 6.067594 c +3.436123 5.880069 3.672039 4.838120 3.101106 3.962914 c +2.867639 3.605021 2.610615 3.326599 2.412749 3.112261 c +2.126743 2.802443 1.964337 2.626516 2.175334 2.538012 c +2.395411 2.445700 3.695794 2.400038 4.634356 2.926394 c +5.368968 3.338373 5.813886 3.749346 6.113510 4.026108 c +6.361263 4.254956 6.509674 4.392044 6.640382 4.362063 c +7.109039 4.254570 7.592651 4.181254 8.087825 4.145204 c +8.094584 4.144711 8.101346 4.144226 8.108109 4.143748 c +8.336406 4.127606 8.567147 4.119385 8.800000 4.119385 c +13.660106 4.119385 17.600000 7.701106 17.600000 12.119385 c +17.600000 14.286772 16.651907 16.252851 15.112180 17.693565 c +h +23.200001 9.719384 m +23.200001 12.844823 21.228483 15.551649 18.354347 16.868143 c +18.004728 17.028286 17.689714 16.618740 17.872845 16.280592 c +18.546221 15.037203 18.927999 13.627815 18.927999 12.119385 c +18.927999 7.635936 15.555238 4.027454 11.177651 3.052103 c +10.793186 2.966442 10.708088 2.446247 11.077391 2.309256 c +12.102591 1.928959 13.224373 1.719385 14.400001 1.719385 c +15.145129 1.719385 15.868631 1.803576 16.559618 1.962063 c +16.690323 1.992044 16.838730 1.854961 17.086477 1.626122 c +17.386099 1.349360 17.831041 0.938368 18.565643 0.526394 c +19.504206 0.000038 20.804588 0.045700 21.024666 0.138012 c +21.235662 0.226517 21.073257 0.402443 20.787252 0.712259 c +20.589384 0.926600 20.332361 1.205021 20.098894 1.562914 c +19.527962 2.438120 19.763878 3.480068 20.020016 3.667593 c +22.022936 5.133977 23.200001 7.199502 23.200001 9.719384 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1810 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001900 00000 n +0000001923 00000 n +0000002096 00000 n +0000002170 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2229 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/Contents.json new file mode 100644 index 0000000000..8cc615202a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "unread.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/unread.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/unread.pdf new file mode 100644 index 0000000000..7aa7e23a5b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/NewChats.imageset/unread.pdf @@ -0,0 +1,94 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 23.000000 23.000000 cm +0.000000 0.000000 0.000000 scn +7.000000 3.500000 m +7.000000 1.567003 5.432997 0.000000 3.500000 0.000000 c +1.567003 0.000000 0.000000 1.567003 0.000000 3.500000 c +0.000000 5.432997 1.567003 7.000000 3.500000 7.000000 c +5.432997 7.000000 7.000000 5.432997 7.000000 3.500000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 9.864258 cm +0.000000 0.000000 0.000000 scn +12.909021 19.745098 m +11.988510 19.999132 11.011806 20.135742 10.000000 20.135742 c +4.477152 20.135742 0.000000 16.065603 0.000000 11.044833 c +0.000000 8.181331 1.337573 5.834144 3.613619 4.167799 c +3.904685 3.954702 4.172771 2.770672 3.523984 1.776117 c +3.258681 1.369423 2.966608 1.053034 2.741760 0.809465 c +2.416753 0.457399 2.232201 0.257484 2.471971 0.156912 c +2.722059 0.052011 4.199766 0.000120 5.266314 0.598253 c +6.101101 1.066412 6.606689 1.533424 6.947171 1.847927 c +7.228708 2.107983 7.397357 2.263765 7.545889 2.229696 c +8.331102 2.049595 9.153261 1.953924 10.000000 1.953924 c +15.522848 1.953924 20.000000 6.024063 20.000000 11.044833 c +20.000000 11.770477 19.906481 12.476257 19.729799 13.152769 c +18.882269 12.366479 17.747276 11.885742 16.500000 11.885742 c +13.876647 11.885742 11.750000 14.012390 11.750000 16.635742 c +11.750000 17.824844 12.186939 18.911896 12.909021 19.745098 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1374 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001464 00000 n +0000001487 00000 n +0000001660 00000 n +0000001734 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1793 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/Contents.json new file mode 100644 index 0000000000..358678e10d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replies.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/replies.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/replies.pdf new file mode 100644 index 0000000000..6360561a07 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Reply.imageset/replies.pdf @@ -0,0 +1,77 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +-1.000000 -0.000000 -0.000000 1.000000 27.200195 6.360352 cm +0.000000 0.000000 0.000000 scn +13.820626 14.514648 m +13.820626 18.826447 l +13.820626 19.964518 15.179484 20.553219 16.009750 19.774845 c +25.809000 10.588047 l +26.356834 10.074451 26.356834 9.204846 25.809000 8.691251 c +16.009748 -0.495548 l +15.179483 -1.273922 13.820626 -0.685221 13.820626 0.452852 c +13.820626 4.764648 l +7.161232 4.764648 3.408549 1.857939 1.544460 -0.290266 c +1.095426 -0.807739 0.049461 -0.495903 0.116237 0.185970 c +0.588336 5.006769 2.903297 14.514648 13.820626 14.514648 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 597 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000687 00000 n +0000000709 00000 n +0000000882 00000 n +0000000956 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1015 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/Contents.json new file mode 100644 index 0000000000..af693ca66b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "awaydemo.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/awaydemo.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/awaydemo.pdf new file mode 100644 index 0000000000..57391563d7 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/AwayShortcut.imageset/awaydemo.pdf @@ -0,0 +1,110 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 20.000000 9.593140 cm +0.000000 0.000000 0.000000 scn +30.000000 60.406860 m +13.431458 60.406860 0.000000 48.196442 0.000000 33.134132 c +0.000000 18.071819 13.431458 5.861401 30.000000 5.861401 c +32.540215 5.861401 35.006695 6.148415 37.362335 6.688717 c +37.807930 6.790920 38.313877 6.323578 39.158485 5.543411 c +40.179932 4.599903 41.696693 3.198868 44.201057 1.794388 c +47.400703 -0.000008 51.833824 0.155663 52.584087 0.470364 c +53.303398 0.772079 52.749741 1.371830 51.774719 2.428028 c +51.100174 3.158730 50.223957 4.107895 49.428047 5.327980 c +47.481686 8.311646 48.285946 11.863739 49.159142 12.503029 c +55.987282 17.502064 60.000000 24.543625 60.000000 33.134132 c +60.000000 48.196442 46.568542 60.406860 30.000000 60.406860 c +h +13.485292 45.105156 m +12.383485 45.105156 11.490293 45.998344 11.490293 47.100155 c +11.490293 48.201962 12.383485 49.095154 13.485292 49.095154 c +28.485292 49.095154 l +29.292194 49.095154 30.019644 48.609089 30.328432 47.863609 c +30.637220 47.118126 30.466537 46.260040 29.895971 45.689476 c +18.301649 34.095154 l +28.485292 34.095154 l +29.587101 34.095154 30.480293 33.201962 30.480293 32.100151 c +30.480293 30.998344 29.587101 30.105152 28.485292 30.105152 c +13.485292 30.105152 l +12.678391 30.105152 11.950941 30.591219 11.642153 31.336699 c +11.333364 32.082180 11.504048 32.940262 12.074615 33.510834 c +23.668936 45.105156 l +13.485292 45.105156 l +h +34.485294 30.105152 m +33.383484 30.105152 32.490292 30.998344 32.490292 32.100151 c +32.490292 33.201962 33.383484 34.095154 34.485294 34.095154 c +46.485294 34.095154 l +47.292194 34.095154 48.019646 33.609085 48.328434 32.863609 c +48.637222 32.118126 48.466537 31.260040 47.895973 30.689474 c +39.301651 22.095154 l +46.485294 22.095154 l +47.587101 22.095154 48.480293 21.201962 48.480293 20.100155 c +48.480293 18.998344 47.587101 18.105156 46.485294 18.105156 c +34.485294 18.105156 l +33.678391 18.105156 32.950943 18.591221 32.642155 19.336700 c +32.333366 20.082180 32.504047 20.940266 33.074615 21.510834 c +41.668938 30.105152 l +34.485294 30.105152 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2114 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 100.000000 80.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002204 00000 n +0000002227 00000 n +0000002401 00000 n +0000002475 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2534 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/Contents.json new file mode 100644 index 0000000000..747557322c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "greetingdemo.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/greetingdemo.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/greetingdemo.pdf new file mode 100644 index 0000000000..b1a13e2cc8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/GreetingShortcut.imageset/greetingdemo.pdf @@ -0,0 +1,113 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 20.000000 9.593140 cm +0.000000 0.000000 0.000000 scn +30.000000 60.406860 m +13.431458 60.406860 0.000000 48.196442 0.000000 33.134132 c +0.000000 18.071819 13.431458 5.861401 30.000000 5.861401 c +32.540215 5.861401 35.006695 6.148415 37.362335 6.688717 c +37.807930 6.790920 38.313877 6.323578 39.158485 5.543411 c +40.179932 4.599903 41.696693 3.198868 44.201057 1.794388 c +47.400703 -0.000008 51.833824 0.155663 52.584087 0.470364 c +53.303398 0.772079 52.749741 1.371830 51.774719 2.428028 c +51.100174 3.158730 50.223957 4.107895 49.428047 5.327980 c +47.481686 8.311646 48.285946 11.863739 49.159142 12.503029 c +55.987282 17.502064 60.000000 24.543625 60.000000 33.134132 c +60.000000 48.196442 46.568542 60.406860 30.000000 60.406860 c +h +24.102232 23.801357 m +26.096096 21.816811 27.816639 20.104309 29.904844 18.640121 c +34.326607 15.539635 42.294388 18.206409 42.389248 24.293480 c +42.484905 29.817617 42.816391 32.655792 44.934528 38.828526 c +45.143944 39.438889 44.834270 40.110107 44.244049 40.324844 c +42.612083 40.918571 40.826450 39.985260 40.374058 38.317467 c +40.090858 37.273514 39.806278 35.907646 39.441441 34.156609 c +39.317574 33.562099 39.184452 32.923187 39.038994 32.237404 c +36.574406 31.567644 35.905304 30.200764 35.238831 27.718304 c +35.059917 27.052704 34.075237 27.180542 34.070129 27.868248 c +34.053421 30.041918 34.843792 32.883881 37.088749 34.499214 c +35.774624 36.432529 34.517193 38.678288 32.971912 41.678082 c +32.050930 43.466049 31.027578 45.521873 29.829105 47.938957 c +28.848585 49.810692 26.035364 48.324295 27.010414 46.462559 c +29.708126 40.987381 l +32.095585 36.141830 l +32.566368 35.242813 31.298647 34.430550 30.683102 35.247261 c +26.782978 40.666473 l +22.708023 46.328518 l +21.493135 47.992588 19.001842 46.150406 20.202797 44.505161 c +24.440683 38.623634 l +28.388968 33.144012 l +28.963285 32.382530 27.889732 31.478111 27.231575 32.145828 c +23.347502 36.312939 l +19.538147 40.399857 l +18.088379 41.859329 15.896906 39.668953 17.340765 38.215389 c +21.417105 33.849815 l +25.294756 29.697002 l +25.944740 29.037680 25.047108 27.953327 24.278332 28.526333 c +21.155664 31.040241 l +17.557913 33.936642 l +15.869324 35.195747 13.967093 32.644844 15.660569 31.382381 c +17.358421 30.067600 18.808191 28.828339 20.091669 27.661533 c +21.617983 26.273994 22.909048 24.988964 24.102232 23.801357 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2400 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 100.000000 80.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002490 00000 n +0000002513 00000 n +0000002687 00000 n +0000002761 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2820 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index cd26381637..0ed8b8b15d 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -721,12 +721,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case let .customChatContents(customChatContents) = strongSelf.presentationInterfaceState.subject { switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput: - break - case .quickReplyMessageInput: + case let .quickReplyMessageInput(_, shortcutType): if let historyView = strongSelf.chatDisplayNode.historyNode.originalHistoryView, historyView.entries.isEmpty { //TODO:localize - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: "Remove Shortcut", text: "You didn't create a quick reply message. Exiting will remove the shortcut.", actions: [ + + let titleString: String + let textString: String + switch shortcutType { + case .generic: + titleString = "Remove Shortcut" + textString = "You didn't create a quick reply message. Exiting will remove the shortcut." + case .greeting: + titleString = "Remove Greeting Message" + textString = "You didn't create a greeting message. Exiting will remove it." + case .away: + titleString = "Remove Away Message" + textString = "You didn't create an away message. Exiting will remove it." + } + + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: titleString, text: textString, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: "Remove", action: { [weak strongSelf] in strongSelf?.dismiss() @@ -6185,12 +6198,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G //TODO:localize if case let .customChatContents(customChatContents) = self.subject { switch customChatContents.kind { - case .greetingMessageInput: - self.chatTitleView?.titleContent = .custom("Greeting Message", nil, false) - case .awayMessageInput: - self.chatTitleView?.titleContent = .custom("Away Message", nil, false) - case let .quickReplyMessageInput(shortcut): - self.chatTitleView?.titleContent = .custom("\(shortcut)", nil, false) + case let .quickReplyMessageInput(shortcut, shortcutType): + switch shortcutType { + case .generic: + self.chatTitleView?.titleContent = .custom("\(shortcut)", nil, false) + case .greeting: + self.chatTitleView?.titleContent = .custom("Greeting Message", nil, false) + case .away: + self.chatTitleView?.titleContent = .custom("Away Message", nil, false) + } } } else { self.chatTitleView?.titleContent = .custom("Messages", nil, false) @@ -9477,8 +9493,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let peerId = self.chatLocation.peerId else { return } - let _ = self - let _ = shortcutId + + if !self.presentationInterfaceState.isPremium { + let controller = PremiumIntroScreen(context: self.context, source: .settings) + self.push(controller) + return + } self.updateChatPresentationInterfaceState(animated: true, interactive: false, { $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } diff --git a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift index a7c7370f72..a9c0c525e2 100644 --- a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift +++ b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift @@ -12,7 +12,7 @@ import QuickReplyNameAlertController extension ChatControllerImpl { func editChat() { //TODO:localize - if case let .customChatContents(customChatContents) = self.subject, case let .quickReplyMessageInput(currentValue) = customChatContents.kind { + if case let .customChatContents(customChatContents) = self.subject, case let .quickReplyMessageInput(currentValue, shortcutType) = customChatContents.kind, case .generic = shortcutType { var completion: ((String?) -> Void)? let alertController = quickReplyNameAlertController( context: self.context, diff --git a/submodules/TelegramUI/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Sources/ChatEmptyNode.swift index 6b98a57487..e11ac9b0f6 100644 --- a/submodules/TelegramUI/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Sources/ChatEmptyNode.swift @@ -712,8 +712,6 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC maxWidth = min(240.0, maxWidth) switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput: - break case .quickReplyMessageInput: insets.top = 10.0 imageSpacing = 5.0 @@ -734,29 +732,34 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC if case let .customChatContents(customChatContents) = interfaceState.subject { switch customChatContents.kind { - case .greetingMessageInput: - //TODO:localize - centerText = true - titleString = "New Greeting Message" - strings = [ - "Create greetings that will be automatically sent to new customers" - ] - case .awayMessageInput: - //TODO:localize - centerText = true - titleString = "New Away Message" - strings = [ - "Add messages that are automatically sent when you are off." - ] - case let .quickReplyMessageInput(shortcut): - iconName = "Chat/Empty Chat/QuickReplies" - //TODO:localize - centerText = false - titleString = "New Quick Reply" - strings = [ - "· Enter a message below that will be sent in chats when you type \"**/\(shortcut)\"**.", - "· You can access Quick Replies in any chat by typing \"/\" or using the Attachment menu." - ] + case let .quickReplyMessageInput(shortcut, shortcutType): + switch shortcutType { + case .generic: + iconName = "Chat/Empty Chat/QuickReplies" + //TODO:localize + centerText = false + titleString = "New Quick Reply" + strings = [ + "· Enter a message below that will be sent in chats when you type \"**/\(shortcut)\"**.", + "· You can access Quick Replies in any chat by typing \"/\" or using the Attachment menu." + ] + case .greeting: + iconName = "Chat/Empty Chat/GreetingShortcut" + //TODO:localize + centerText = true + titleString = "New Greeting Message" + strings = [ + "Create greetings that will be automatically sent to new customers" + ] + case .away: + iconName = "Chat/Empty Chat/AwayShortcut" + //TODO:localize + centerText = true + titleString = "New Away Message" + strings = [ + "Add messages that are automatically sent when you are off." + ] + } } } else { titleString = interfaceState.strings.Conversation_CloudStorageInfo_Title diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 060de3e3ec..4cd3cca329 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -516,7 +516,7 @@ func chatHistoryEntriesForView( } } - if let subject = associatedData.subject, case let .customChatContents(customChatContents) = subject, case .quickReplyMessageInput = customChatContents.kind { + if let subject = associatedData.subject, case let .customChatContents(customChatContents) = subject, case let .quickReplyMessageInput(_, shortcutType) = customChatContents.kind, case .generic = shortcutType { if !view.isLoading && view.laterId == nil && !view.entries.isEmpty { for i in 0 ..< 2 { let string = i == 1 ? "To edit or delete your quick reply, tap an hold on it." : "To use this quick reply in a chat, type / and select the shortcut from the list." diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 6e172bbe2d..018aebdd58 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1889,7 +1889,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.removeAll() switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput, .quickReplyMessageInput: + case .quickReplyMessageInput: if !messageText.isEmpty || (resourceAvailable && isImage) || diceEmoji != nil { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor) @@ -1935,7 +1935,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }) }))) } - + } + + if message.id.id < Int32.max - 1000 { if !actions.isEmpty { actions.append(.separator) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index c9ad7c3b0a..549e6e05e5 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -403,7 +403,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState if case let .customChatContents(customChatContents) = chatPresentationInterfaceState.subject { switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput, .quickReplyMessageInput: + case .quickReplyMessageInput: displayInputTextPanel = true } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 0c7316d4a2..689cc49425 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -57,12 +57,6 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha if case let .customChatContents(customChatContents) = presentationInterfaceState.subject { switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput: - if case .spacer = currentButton?.action { - return currentButton - } else { - return ChatNavigationButton(action: .spacer, buttonItem: UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)) - } case .quickReplyMessageInput: if let currentButton = currentButton, currentButton.action == .dismiss { return currentButton @@ -130,21 +124,18 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present if case let .customChatContents(customChatContents) = presentationInterfaceState.subject { switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput: - if let currentButton = currentButton, currentButton.action == .dismiss { - return currentButton - } else { - let buttonItem = UIBarButtonItem(title: strings.Common_Done, style: .done, target: target, action: selector) - buttonItem.accessibilityLabel = strings.Common_Done - return ChatNavigationButton(action: .dismiss, buttonItem: buttonItem) - } - case .quickReplyMessageInput: - if let currentButton = currentButton, currentButton.action == .edit { - return currentButton - } else { - let buttonItem = UIBarButtonItem(title: strings.Common_Edit, style: .plain, target: target, action: selector) - buttonItem.accessibilityLabel = strings.Common_Done - return ChatNavigationButton(action: .edit, buttonItem: buttonItem) + case let .quickReplyMessageInput(_, shortcutType): + switch shortcutType { + case .generic: + if let currentButton = currentButton, currentButton.action == .edit { + return currentButton + } else { + let buttonItem = UIBarButtonItem(title: strings.Common_Edit, style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = strings.Common_Done + return ChatNavigationButton(action: .edit, buttonItem: buttonItem) + } + case .greeting, .away: + return nil } } } diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index fbe0af4414..f7aed05dc2 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -103,7 +103,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { } else if case let .customChatContents(customChatContents) = interfaceState.subject { let displayCount: Int switch customChatContents.kind { - case .greetingMessageInput, .awayMessageInput, .quickReplyMessageInput: + case .quickReplyMessageInput: displayCount = 20 } //TODO:localize diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index ae3903849f..6b731cca05 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1845,12 +1845,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch if case let .customChatContents(customChatContents) = interfaceState.subject { //TODO:localize switch customChatContents.kind { - case .greetingMessageInput: - placeholder = "Add greeting message..." - case .awayMessageInput: - placeholder = "Add away message..." - case .quickReplyMessageInput: - placeholder = "Add quick reply..." + case let .quickReplyMessageInput(_, shortcutType): + switch shortcutType { + case .generic: + placeholder = "Add quick reply..." + case .greeting: + placeholder = "Add greeting message..." + case .away: + placeholder = "Add away message..." + } } } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 97d708b503..72ec09c895 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1891,8 +1891,12 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PremiumIntroScreen(context: context, mode: .business, source: .settings, modal: false, forceDark: false) } - public func makeChatbotSetupScreen(context: AccountContext) -> ViewController { - return ChatbotSetupScreen(context: context) + public func makeChatbotSetupScreen(context: AccountContext, initialData: ChatbotSetupScreenInitialData) -> ViewController { + return ChatbotSetupScreen(context: context, initialData: initialData as! ChatbotSetupScreen.InitialData) + } + + public func makeChatbotSetupScreenInitialData(context: AccountContext) -> Signal { + return ChatbotSetupScreen.initialData(context: context) } public func makeBusinessLocationSetupScreen(context: AccountContext, initialValue: TelegramBusinessLocation?, completion: @escaping (TelegramBusinessLocation?) -> Void) -> ViewController {