diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index f09917dc39..ae6b73cb1b 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -167,10 +167,20 @@ public final class AuthorizationSequenceController: NavigationController, MFMail controller.loginWithNumber = { [weak self, weak controller] number, syncContacts in if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((sendAuthorizationCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, phoneNumber: number, apiId: strongSelf.apiId, apiHash: strongSelf.apiHash, syncContacts: syncContacts) |> deliverOnMainQueue).start(next: { [weak self] account in + strongSelf.actionDisposable.set((sendAuthorizationCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, phoneNumber: number, apiId: strongSelf.apiId, apiHash: strongSelf.apiHash, syncContacts: syncContacts, forcedPasswordSetupNotice: { value in + guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else { + return nil + } + return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry) + }) |> deliverOnMainQueue).start(next: { [weak self] result in if let strongSelf = self { - controller?.inProgress = false - strongSelf.account = account + switch result { + case let .sentCode(account): + controller?.inProgress = false + strongSelf.account = account + case .loggedIn: + break + } } }, error: { error in if let strongSelf = self, let controller = controller { diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index f8c6c5f4d5..4d5ec98a6b 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -59,6 +59,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case sendOneLog(PresentationTheme) case sendShareLogs case sendGroupCallLogs + case sendStorageStats case sendNotificationLogs(PresentationTheme) case sendCriticalLogs(PresentationTheme) case sendAllLogs @@ -106,7 +107,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { switch self { case .testStickerImport: return DebugControllerSection.sticker.rawValue - case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs, .sendAllLogs: + case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendStorageStats, .sendNotificationLogs, .sendCriticalLogs, .sendAllLogs: return DebugControllerSection.logs.rawValue case .accounts: return DebugControllerSection.logs.rawValue @@ -143,76 +144,78 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 6 case .sendAllLogs: return 7 - case .accounts: + case .sendStorageStats: return 8 - case .logToFile: + case .accounts: return 9 - case .logToConsole: + case .logToFile: return 10 - case .redactSensitiveData: + case .logToConsole: return 11 - case .enableRaiseToSpeak: + case .redactSensitiveData: return 12 - case .keepChatNavigationStack: + case .enableRaiseToSpeak: return 13 - case .skipReadHistory: + case .keepChatNavigationStack: return 14 - case .crashOnSlowQueries: + case .skipReadHistory: return 15 - case .clearTips: + case .crashOnSlowQueries: return 16 - case .crash: + case .clearTips: return 17 - case .resetData: + case .crash: return 18 - case .resetDatabase: + case .resetData: return 19 - case .resetDatabaseAndCache: + case .resetDatabase: return 20 - case .resetHoles: + case .resetDatabaseAndCache: return 21 - case .reindexUnread: + case .resetHoles: return 22 - case .resetCacheIndex: + case .reindexUnread: return 23 - case .reindexCache: + case .resetCacheIndex: return 24 - case .resetBiometricsData: + case .reindexCache: return 25 - case .resetWebViewCache: + case .resetBiometricsData: return 26 - case .optimizeDatabase: + case .resetWebViewCache: return 27 - case .photoPreview: + case .optimizeDatabase: return 28 - case .knockoutWallpaper: + case .photoPreview: return 29 - case .experimentalCompatibility: + case .knockoutWallpaper: return 30 - case .enableDebugDataDisplay: + case .experimentalCompatibility: return 31 - case .acceleratedStickers: + case .enableDebugDataDisplay: return 32 - case .experimentalBackground: + case .acceleratedStickers: return 33 - case .inlineForums: + case .experimentalBackground: return 34 - case .localTranscription: + case .inlineForums: return 35 - case .enableReactionOverrides: + case .localTranscription: return 36 - case .restorePurchases: + case .enableReactionOverrides: return 37 - case .playerEmbedding: + case .restorePurchases: return 38 - case .playlistPlayback: + case .playerEmbedding: return 39 - case .enableQuickReactionSwitch: + case .playlistPlayback: return 40 - case .voiceConference: + case .enableQuickReactionSwitch: return 41 + case .voiceConference: + return 42 case let .preferredVideoCodec(index, _, _, _): - return 42 + index + return 43 + index case .disableVideoAspectScaling: return 100 case .enableVoipTcp: @@ -806,6 +809,61 @@ private enum DebugControllerEntry: ItemListNodeEntry { })) } + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + arguments.presentController(actionSheet, nil) + }) + }) + case .sendStorageStats: + return ItemListDisclosureItem(presentationData: presentationData, title: "Send Storage Stats", label: "", sectionId: self.section, style: .blocks, action: { + guard let context = arguments.context, context.sharedContext.applicationBindings.isMainApp else { + return + } + + let allStats: Signal = Signal { subscriber in + DispatchQueue.global().async { + let log = collectRawStorageUsageReport(containerPath: context.sharedContext.applicationBindings.containerPath) + subscriber.putNext(log.data(using: .utf8) ?? Data()) + } + + return EmptyDisposable + } + + let _ = (allStats + |> deliverOnMainQueue).start(next: { allStatsData in + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationData: presentationData) + + var items: [ActionSheetButtonItem] = [] + + if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp { + items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) + controller.peerSelected = { [weak controller] peer, _ in + let peerId = peer.id + + if let strongController = controller { + strongController.dismiss() + + let id = Int64.random(in: Int64.min ... Int64.max) + let fileResource = LocalFileMediaResource(fileId: id, size: Int64(allStatsData.count), isSecretRelated: false) + context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: allStatsData) + + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/zip", size: Int64(allStatsData.count), attributes: [.FileName(fileName: "StorageReport.txt")]) + let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) + + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() + } + } + arguments.pushController(controller) + })) + } + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -1251,6 +1309,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.sendNotificationLogs(presentationData.theme)) entries.append(.sendCriticalLogs(presentationData.theme)) entries.append(.sendAllLogs) + entries.append(.sendStorageStats) if isMainApp { entries.append(.accounts(presentationData.theme)) } diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index c252869ecb..0f2d2b3894 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -4395,7 +4395,15 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { var canBeSelectedOrLongTapped = false for itemNode in strongSelf.itemNodes { - if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { + var canBeSelected = itemNode.canBeSelected + if canBeSelected { + if !itemNode.isLayerBacked { + if !itemNode.visibleForSelection(at: strongSelf.view.convert(strongSelf.touchesPosition, to: itemNode.view)) { + canBeSelected = false + } + } + } + if itemNode.index == index && (strongSelf.items[index].selectable && canBeSelected) || itemNode.canBeLongTapped { canBeSelectedOrLongTapped = true } } @@ -4403,7 +4411,20 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if canBeSelectedOrLongTapped { strongSelf.highlightedItemIndex = index for itemNode in strongSelf.itemNodes { - if itemNode.index == index && itemNode.canBeSelected { + let itemNodeFrame = itemNode.frame + let itemNodeBounds = itemNode.bounds + let itemPoint = strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY) + + var canBeSelected = itemNode.canBeSelected + if canBeSelected { + if !itemNode.isLayerBacked { + if !itemNode.visibleForSelection(at: itemPoint) { + canBeSelected = false + } + } + } + + if itemNode.index == index && canBeSelected { if true { if !itemNode.isLayerBacked { strongSelf.reorderItemNodeToFront(itemNode) @@ -4411,10 +4432,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture strongSelf.reorderHeaderNodeToFront(headerNode) } } - let itemNodeFrame = itemNode.frame - let itemNodeBounds = itemNode.bounds if strongSelf.items[index].selectable { - itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false) + itemNode.setHighlighted(true, at: itemPoint, animated: false) } if itemNode.canBeLongTapped { diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift index 81dc1c3c52..8183be537f 100644 --- a/submodules/Display/Source/ListViewItemNode.swift +++ b/submodules/Display/Source/ListViewItemNode.swift @@ -148,6 +148,10 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { return true } + open func visibleForSelection(at point: CGPoint) -> Bool { + return true + } + open var canBeLongTapped: Bool { return false } diff --git a/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift b/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift new file mode 100644 index 0000000000..afdbc5ea02 --- /dev/null +++ b/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift @@ -0,0 +1,702 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramPresentationData +import SwitchNode +import AppBundle +import CheckNode + +public enum ItemListExpandableSwitchItemNodeType { + case regular + case icon +} + +public class ItemListExpandableSwitchItem: ListViewItem, ItemListItem { + public struct SubItem: Equatable { + public var id: AnyHashable + public var title: String + public var isSelected: Bool + + public init( + id: AnyHashable, + title: String, + isSelected: Bool + ) { + self.id = id + self.title = title + self.isSelected = isSelected + } + } + + let presentationData: ItemListPresentationData + let icon: UIImage? + let title: String + let value: Bool + let isExpanded: Bool + let subItems: [SubItem] + let type: ItemListExpandableSwitchItemNodeType + let enableInteractiveChanges: Bool + let enabled: Bool + let displayLocked: Bool + let disableLeadingInset: Bool + let maximumNumberOfLines: Int + let noCorners: Bool + public let sectionId: ItemListSectionId + let style: ItemListStyle + let updated: (Bool) -> Void + let activatedWhileDisabled: () -> Void + let selectAction: () -> Void + let subAction: (SubItem) -> Void + public let tag: ItemListItemTag? + + public let selectable: Bool = true + + public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, value: Bool, isExpanded: Bool, subItems: [SubItem], type: ItemListExpandableSwitchItemNodeType = .regular, enableInteractiveChanges: Bool = true, enabled: Bool = true, displayLocked: Bool = false, disableLeadingInset: Bool = false, maximumNumberOfLines: Int = 1, noCorners: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, updated: @escaping (Bool) -> Void, activatedWhileDisabled: @escaping () -> Void = {}, selectAction: @escaping () -> Void, subAction: @escaping (SubItem) -> Void, tag: ItemListItemTag? = nil) { + self.presentationData = presentationData + self.icon = icon + self.title = title + self.value = value + self.isExpanded = isExpanded + self.subItems = subItems + self.type = type + self.enableInteractiveChanges = enableInteractiveChanges + self.enabled = enabled + self.displayLocked = displayLocked + self.disableLeadingInset = disableLeadingInset + self.maximumNumberOfLines = maximumNumberOfLines + self.noCorners = noCorners + self.sectionId = sectionId + self.style = style + self.updated = updated + self.activatedWhileDisabled = activatedWhileDisabled + self.selectAction = selectAction + self.subAction = subAction + self.tag = tag + } + + public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = ItemListExpandableSwitchItemNode(type: self.type) + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply(ListViewItemUpdateAnimation.None) }) + }) + } + } + } + + public func selected(listView: ListView) { + listView.clearHighlightAnimated(true) + + self.selectAction() + } + + public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? ItemListExpandableSwitchItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply(animation) + }) + } + } + } + } + } +} + +private final class SubItemNode: HighlightTrackingButtonNode { + private let textNode: ImmediateTextNode + private var checkNode: CheckNode? + private let separatorNode: ASDisplayNode + + private var theme: PresentationTheme? + private var item: ItemListExpandableSwitchItem.SubItem? + private var action: ((ItemListExpandableSwitchItem.SubItem) -> Void)? + + init() { + self.textNode = ImmediateTextNode() + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + super.init() + + self.addSubnode(self.separatorNode) + self.addSubnode(self.textNode) + + self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc private func pressed() { + guard let item = self.item, let action = self.action else { + return + } + action(item) + } + + func update(presentationData: ItemListPresentationData, item: ItemListExpandableSwitchItem.SubItem, action: @escaping (ItemListExpandableSwitchItem.SubItem) -> Void, size: CGSize, transition: ContainedViewLayoutTransition) { + let themeUpdated = self.theme !== presentationData.theme + + self.item = item + self.action = action + + let leftInset: CGFloat = 60.0 + + if themeUpdated { + self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + } + + let checkNode: CheckNode + if let current = self.checkNode { + checkNode = current + if themeUpdated { + checkNode.theme = CheckNodeTheme(theme: presentationData.theme, style: .plain) + } + } else { + checkNode = CheckNode(theme: CheckNodeTheme(theme: presentationData.theme, style: .plain)) + self.checkNode = checkNode + self.addSubnode(checkNode) + } + + let checkSize = CGSize(width: 22.0, height: 22.0) + checkNode.frame = CGRect(origin: CGPoint(x: floor((leftInset - checkSize.width) / 2.0), y: floor((size.height - checkSize.height) / 2.0)), size: checkSize) + + checkNode.setSelected(item.isSelected, animated: transition.isAnimated) + + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset, y: size.height - UIScreenPixel), size: CGSize(width: size.width - leftInset, height: UIScreenPixel))) + + self.textNode.attributedText = NSAttributedString(string: item.title, font: Font.regular(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor) + let titleSize = self.textNode.updateLayout(CGSize(width: size.width - leftInset, height: 100.0)) + self.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + } +} + +public class ItemListExpandableSwitchItemNode: ListViewItemNode, ItemListItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomTopStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let highlightedBackgroundNode: ASDisplayNode + private let maskNode: ASImageNode + + private let iconNode: ASImageNode + private let titleNode: TextNode + private let titleValueNode: TextNode + private let expandArrowNode: ASImageNode + private var switchNode: ASDisplayNode & ItemListSwitchNodeImpl + private let switchGestureNode: ASDisplayNode + private var disabledOverlayNode: ASDisplayNode? + + private var lockedIconNode: ASImageNode? + + private let subItemContainer: ASDisplayNode + private var subItemNodes: [AnyHashable: SubItemNode] = [:] + + private let activateArea: AccessibilityAreaNode + + private var item: ItemListExpandableSwitchItem? + + public var tag: ItemListItemTag? { + return self.item?.tag + } + + public init(type: ItemListExpandableSwitchItemNodeType) { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + self.backgroundNode.backgroundColor = .white + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomTopStripeNode = ASDisplayNode() + self.bottomTopStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.iconNode = ASImageNode() + self.iconNode.isLayerBacked = true + self.iconNode.displaysAsynchronously = false + + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + switch type { + case .regular: + self.switchNode = SwitchNode() + case .icon: + self.switchNode = IconSwitchNode() + } + + self.titleValueNode = TextNode() + self.titleValueNode.isUserInteractionEnabled = false + + self.expandArrowNode = ASImageNode() + self.expandArrowNode.displaysAsynchronously = false + + self.highlightedBackgroundNode = ASDisplayNode() + self.highlightedBackgroundNode.isLayerBacked = true + + self.switchGestureNode = ASDisplayNode() + + self.activateArea = AccessibilityAreaNode() + + self.subItemContainer = ASDisplayNode() + self.subItemContainer.clipsToBounds = true + + super.init(layerBacked: false, dynamicBounce: false) + + self.addSubnode(self.titleNode) + self.addSubnode(self.titleValueNode) + self.addSubnode(self.expandArrowNode) + self.addSubnode(self.switchNode) + self.addSubnode(self.switchGestureNode) + self.addSubnode(self.activateArea) + self.addSubnode(self.subItemContainer) + + self.activateArea.activate = { [weak self] in + guard let strongSelf = self, let item = strongSelf.item, item.enabled else { + return false + } + let value = !strongSelf.switchNode.isOn + if item.enableInteractiveChanges { + strongSelf.switchNode.setOn(value, animated: true) + } + item.updated(value) + return true + } + } + + override public func didLoad() { + super.didLoad() + + (self.switchNode.view as? UISwitch)?.addTarget(self, action: #selector(self.switchValueChanged(_:)), for: .valueChanged) + self.switchGestureNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + func asyncLayout() -> (_ item: ItemListExpandableSwitchItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeTitleValueLayout = TextNode.asyncLayout(self.titleValueNode) + + let currentItem = self.item + var currentDisabledOverlayNode = self.disabledOverlayNode + + return { item, params, neighbors in + var contentSize: CGSize + var insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + let itemBackgroundColor: UIColor + let itemSeparatorColor: UIColor + + let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) + + var updatedTheme: PresentationTheme? + if currentItem?.presentationData.theme !== item.presentationData.theme { + updatedTheme = item.presentationData.theme + } + + var updateIcon = false + if currentItem?.icon != item.icon { + updateIcon = true + } + + switch item.style { + case .plain: + itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor + itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor + contentSize = CGSize(width: params.width, height: 44.0) + insets = itemListNeighborsPlainInsets(neighbors) + case .blocks: + itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor + itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor + contentSize = CGSize(width: params.width, height: 44.0) + insets = itemListNeighborsGroupedInsets(neighbors, params) + } + + var leftInset = 16.0 + params.leftInset + if let _ = item.icon { + leftInset += 43.0 + } + + if item.disableLeadingInset { + insets.top = 0.0 + insets.bottom = 0.0 + } + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: item.maximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - params.rightInset - 64.0 - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let titleValue = "\(item.subItems.filter(\.isSelected).count)/\(item.subItems.count)" + let (titleValueLayout, titleValueApply) = makeTitleValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleValue, font: Font.bold(14.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - params.rightInset - 64.0 - titleLayout.size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + contentSize.height = max(contentSize.height, titleLayout.size.height + 22.0) + + let mainContentHeight = contentSize.height + var effectiveSubItemsHeight: CGFloat = 0.0 + if item.isExpanded { + effectiveSubItemsHeight = CGFloat(item.subItems.count) * 44.0 + } + contentSize.height += effectiveSubItemsHeight + + if !item.enabled { + if currentDisabledOverlayNode == nil { + currentDisabledOverlayNode = ASDisplayNode() + } + } else { + currentDisabledOverlayNode = nil + } + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] animation in + if let strongSelf = self { + strongSelf.item = item + + strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: mainContentHeight)) + + strongSelf.activateArea.accessibilityLabel = item.title + strongSelf.activateArea.accessibilityValue = item.value ? item.presentationData.strings.VoiceOver_Common_On : item.presentationData.strings.VoiceOver_Common_Off + strongSelf.activateArea.accessibilityHint = item.presentationData.strings.VoiceOver_Common_SwitchHint + var accessibilityTraits = UIAccessibilityTraits() + if item.enabled { + } else { + accessibilityTraits.insert(.notEnabled) + } + strongSelf.activateArea.accessibilityTraits = accessibilityTraits + + if let icon = item.icon { + if strongSelf.iconNode.supernode == nil { + strongSelf.addSubnode(strongSelf.iconNode) + } + if updateIcon { + strongSelf.iconNode.image = icon + } + let iconY = floor((mainContentHeight - icon.size.height) / 2.0) + strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size) + } else if strongSelf.iconNode.supernode != nil { + strongSelf.iconNode.image = nil + strongSelf.iconNode.removeFromSupernode() + } + + let transition: ContainedViewLayoutTransition = animation.transition + + if let currentDisabledOverlayNode = currentDisabledOverlayNode { + if currentDisabledOverlayNode != strongSelf.disabledOverlayNode { + strongSelf.disabledOverlayNode = currentDisabledOverlayNode + strongSelf.insertSubnode(currentDisabledOverlayNode, belowSubnode: strongSelf.switchGestureNode) + currentDisabledOverlayNode.alpha = 0.0 + transition.updateAlpha(node: currentDisabledOverlayNode, alpha: 1.0) + currentDisabledOverlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width, height: mainContentHeight - separatorHeight)) + } else { + transition.updateFrame(node: currentDisabledOverlayNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width, height: mainContentHeight - separatorHeight))) + } + currentDisabledOverlayNode.backgroundColor = itemBackgroundColor.withAlphaComponent(0.6) + } else if let disabledOverlayNode = strongSelf.disabledOverlayNode { + transition.updateAlpha(node: disabledOverlayNode, alpha: 0.0, completion: { [weak disabledOverlayNode] _ in + disabledOverlayNode?.removeFromSupernode() + }) + strongSelf.disabledOverlayNode = nil + } + + if let _ = updatedTheme { + strongSelf.topStripeNode.backgroundColor = itemSeparatorColor + strongSelf.bottomTopStripeNode.backgroundColor = itemSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor + strongSelf.backgroundNode.backgroundColor = itemBackgroundColor + + strongSelf.switchNode.frameColor = item.presentationData.theme.list.itemSwitchColors.frameColor + strongSelf.switchNode.contentColor = item.presentationData.theme.list.itemSwitchColors.contentColor + strongSelf.switchNode.handleColor = item.presentationData.theme.list.itemSwitchColors.handleColor + strongSelf.switchNode.positiveContentColor = item.presentationData.theme.list.itemSwitchColors.positiveColor + strongSelf.switchNode.negativeContentColor = item.presentationData.theme.list.itemSwitchColors.negativeColor + + strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor + } + + let _ = titleApply() + + switch item.style { + case .plain: + if strongSelf.backgroundNode.supernode != nil { + strongSelf.backgroundNode.removeFromSupernode() + } + if strongSelf.topStripeNode.supernode != nil { + strongSelf.topStripeNode.removeFromSupernode() + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0) + } + if strongSelf.bottomTopStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomTopStripeNode, at: 1) + } + if strongSelf.maskNode.supernode != nil { + strongSelf.maskNode.removeFromSupernode() + } + strongSelf.bottomTopStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: mainContentHeight - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: layout.contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)) + case .blocks: + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.bottomTopStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomTopStripeNode, at: 3) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.maskNode, aboveSubnode: strongSelf.switchGestureNode) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noCorners + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = leftInset + strongSelf.bottomStripeNode.isHidden = false + strongSelf.bottomTopStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + strongSelf.bottomTopStripeNode.isHidden = false + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + animation.animator.updateFrame(layer: strongSelf.backgroundNode.layer, frame: backgroundFrame, completion: nil) + animation.animator.updateFrame(layer: strongSelf.maskNode.layer, frame: backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0), completion: nil) + animation.animator.updateFrame(layer: strongSelf.topStripeNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)), completion: nil) + animation.animator.updateFrame(layer: strongSelf.bottomTopStripeNode.layer, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: mainContentHeight - separatorHeight), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)), completion: nil) + animation.animator.updateFrame(layer: strongSelf.bottomStripeNode.layer, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)), completion: nil) + } + + strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((mainContentHeight - titleLayout.size.height) / 2.0)), size: titleLayout.size) + + let _ = titleValueApply() + strongSelf.titleValueNode.frame = CGRect(origin: CGPoint(x: strongSelf.titleNode.frame.maxX + 9.0, y: strongSelf.titleNode.frame.minY + floor((titleLayout.size.height - titleValueLayout.size.height) / 2.0)), size: titleValueLayout.size) + + if let updatedTheme { + strongSelf.expandArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: updatedTheme.list.itemPrimaryTextColor) + } + if let image = strongSelf.expandArrowNode.image { + strongSelf.expandArrowNode.position = CGPoint(x: strongSelf.titleValueNode.frame.maxX + 9.0, y: strongSelf.titleValueNode.frame.midY) + let scaleFactor: CGFloat = 0.8 + strongSelf.expandArrowNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: image.size.width * scaleFactor, height: image.size.height * scaleFactor)) + transition.updateTransformRotation(node: strongSelf.expandArrowNode, angle: item.isExpanded ? CGFloat.pi * 0.5 : -CGFloat.pi * 0.5) + } + + if let switchView = strongSelf.switchNode.view as? UISwitch { + if strongSelf.switchNode.bounds.size.width.isZero { + switchView.sizeToFit() + } + let switchSize = switchView.bounds.size + + strongSelf.switchNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - switchSize.width - 15.0, y: floor((mainContentHeight - switchSize.height) / 2.0)), size: switchSize) + strongSelf.switchGestureNode.frame = strongSelf.switchNode.frame + if switchView.isOn != item.value { + switchView.setOn(item.value, animated: animation.isAnimated) + } + switchView.isUserInteractionEnabled = item.enableInteractiveChanges + } + strongSelf.switchGestureNode.isHidden = item.enableInteractiveChanges && item.enabled + + if item.displayLocked { + var updateLockedIconImage = false + if let _ = updatedTheme { + updateLockedIconImage = true + } + + let lockedIconNode: ASImageNode + if let current = strongSelf.lockedIconNode { + lockedIconNode = current + } else { + updateLockedIconImage = true + lockedIconNode = ASImageNode() + strongSelf.lockedIconNode = lockedIconNode + strongSelf.insertSubnode(lockedIconNode, aboveSubnode: strongSelf.switchNode) + } + + if updateLockedIconImage, let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: item.presentationData.theme.list.itemSecondaryTextColor) { + lockedIconNode.image = image + } + + let switchFrame = strongSelf.switchNode.frame + + if let icon = lockedIconNode.image { + lockedIconNode.frame = CGRect(origin: CGPoint(x: switchFrame.minX + 10.0 + UIScreenPixel, y: switchFrame.minY + 9.0), size: icon.size) + } + } else if let lockedIconNode = strongSelf.lockedIconNode { + strongSelf.lockedIconNode = nil + lockedIconNode.removeFromSupernode() + } + + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 44.0 + UIScreenPixel + UIScreenPixel)) + + animation.animator.updateFrame(layer: strongSelf.subItemContainer.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: mainContentHeight), size: CGSize(width: params.width, height: effectiveSubItemsHeight)), completion: nil) + + var validIds: [AnyHashable] = [] + let subItemSize = CGSize(width: params.width - params.leftInset - params.rightInset, height: 44.0) + var nextSubItemPosition = CGPoint(x: params.leftInset, y: 0.0) + for subItem in item.subItems { + validIds.append(subItem.id) + + let subItemNode: SubItemNode + var subItemNodeTransition = transition + if let current = strongSelf.subItemNodes[subItem.id] { + subItemNode = current + } else { + subItemNodeTransition = .immediate + subItemNode = SubItemNode() + strongSelf.subItemNodes[subItem.id] = subItemNode + strongSelf.subItemContainer.addSubnode(subItemNode) + } + let subItemFrame = CGRect(origin: nextSubItemPosition, size: subItemSize) + subItemNode.update(presentationData: item.presentationData, item: subItem, action: item.subAction, size: subItemSize, transition: subItemNodeTransition) + subItemNodeTransition.updateFrame(node: subItemNode, frame: subItemFrame) + + nextSubItemPosition.y += subItemSize.height + } + var removeIds: [AnyHashable] = [] + for (id, itemNode) in strongSelf.subItemNodes { + if !validIds.contains(id) { + removeIds.append(id) + itemNode.removeFromSupernode() + } + } + for id in removeIds { + strongSelf.subItemNodes.removeValue(forKey: id) + } + } + }) + } + } + + override public func accessibilityActivate() -> Bool { + guard let item = self.item else { + return false + } + if !item.enabled { + return false + } + if let switchNode = self.switchNode as? IconSwitchNode { + switchNode.isOn = !switchNode.isOn + item.updated(switchNode.isOn) + } else if let switchNode = self.switchNode as? SwitchNode { + switchNode.isOn = !switchNode.isOn + item.updated(switchNode.isOn) + } + return true + } + + override public func visibleForSelection(at point: CGPoint) -> Bool { + if !self.canBeSelected { + return false + } + if point.y > self.subItemContainer.frame.minY { + return false + } + + return true + } + + override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { + var highlighted = highlighted + if point.y > self.subItemContainer.frame.minY { + highlighted = false + } + + super.setHighlighted(highlighted, at: point, animated: animated) + + if highlighted { + self.highlightedBackgroundNode.alpha = 1.0 + if self.highlightedBackgroundNode.supernode == nil { + var anchorNode: ASDisplayNode? + if self.bottomStripeNode.supernode != nil { + anchorNode = self.bottomStripeNode + } else if self.topStripeNode.supernode != nil { + anchorNode = self.topStripeNode + } else if self.backgroundNode.supernode != nil { + anchorNode = self.backgroundNode + } + if let anchorNode = anchorNode { + self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) + } else { + self.addSubnode(self.highlightedBackgroundNode) + } + } + } else { + if self.highlightedBackgroundNode.supernode != nil { + if animated { + self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() + } + } + }) + self.highlightedBackgroundNode.alpha = 0.0 + } else { + self.highlightedBackgroundNode.removeFromSupernode() + } + } + } + } + + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.allowsGroupOpacity = true + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4, completion: { [weak self] _ in + self?.layer.allowsGroupOpacity = false + }) + } + + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.allowsGroupOpacity = true + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } + + @objc private func switchValueChanged(_ switchView: UISwitch) { + if let item = self.item { + let value = switchView.isOn + item.updated(value) + } + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if let item = self.item, let switchView = self.switchNode.view as? UISwitch, case .ended = recognizer.state { + if item.enabled { + let value = switchView.isOn + item.updated(!value) + } else { + item.activatedWhileDisabled() + } + } + } +} diff --git a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift index 07d6e201f7..b8e7c77c86 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift @@ -87,7 +87,7 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem { } } -private protocol ItemListSwitchNodeImpl { +protocol ItemListSwitchNodeImpl { var frameColor: UIColor { get set } var contentColor: UIColor { get set } var handleColor: UIColor { get set } diff --git a/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift b/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift index 15d06381b6..e4aea03f6f 100644 --- a/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift +++ b/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift @@ -14,12 +14,12 @@ public func mediaPasteboardScreen( updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, subjects: [MediaPickerScreen.Subject.Media], - presentMediaPicker: @escaping (_ subject: MediaPickerScreen.Subject, _ saveEditedPhotos: Bool, _ bannedSendMedia: (Int32, Bool)?, _ present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void) -> Void, + presentMediaPicker: @escaping (_ subject: MediaPickerScreen.Subject, _ saveEditedPhotos: Bool, _ bannedSendPhotos: (Int32, Bool)?, _ bannedSendVideos: (Int32, Bool)?, _ present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void) -> Void, getSourceRect: (() -> CGRect?)? = nil ) -> ViewController { let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: peer.id), buttons: [.standalone], initialButton: .standalone) controller.requestController = { _, present in - presentMediaPicker(.media(subjects), false, nil, { mediaPicker, mediaPickerContext in + presentMediaPicker(.media(subjects), false, nil, nil, { mediaPicker, mediaPickerContext in present(mediaPicker, mediaPickerContext) }) } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 92c9b85622..2b9ff18c30 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -141,7 +141,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private let peer: EnginePeer? private let threadTitle: String? private let chatLocation: ChatLocation? - private let bannedSendMedia: (Int32, Bool)? + private let bannedSendPhotos: (Int32, Bool)? + private let bannedSendVideos: (Int32, Bool)? private let subject: Subject private let saveEditedPhotos: Bool @@ -945,7 +946,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { if cameraAccess == nil { cameraRect = nil } - if let (untilDate, personal) = self.controller?.bannedSendMedia { + /*if let (untilDate, personal) = self.controller?.bannedSendMedia { self.gridNode.isHidden = true let banDescription: String @@ -972,8 +973,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { placeholderTransition.updateFrame(node: placeholderNode, frame: innerBounds) self.updateNavigation(transition: .immediate) - } else if case .notDetermined = mediaAccess { - + } else */if case .notDetermined = mediaAccess { } else { if case .limited = mediaAccess { let manageNode: MediaPickerManageNode @@ -1099,7 +1099,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } placeholderNode.update(layout: layout, theme: self.presentationData.theme, strings: self.presentationData.strings, hasCamera: cameraAccess == .authorized, transition: placeholderTransition) placeholderTransition.updateFrame(node: placeholderNode, frame: innerBounds) - } else if let placeholderNode = self.placeholderNode, self.controller?.bannedSendMedia == nil { + } else if let placeholderNode = self.placeholderNode {//, self.controller?.bannedSendMedia == nil { self.placeholderNode = nil placeholderNode.removeFromSupernode() } @@ -1131,7 +1131,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private var isDismissing = false - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, bannedSendMedia: (Int32, Bool)?, subject: Subject, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, subject: Subject, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) { self.context = context let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } @@ -1140,7 +1140,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.peer = peer self.threadTitle = threadTitle self.chatLocation = chatLocation - self.bannedSendMedia = bannedSendMedia + self.bannedSendPhotos = bannedSendPhotos + self.bannedSendVideos = bannedSendVideos self.subject = subject self.saveEditedPhotos = saveEditedPhotos @@ -1495,7 +1496,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.requestAttachmentMenuExpansion() self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in if let strongSelf = self { - let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendMedia: strongSelf.bannedSendMedia, subject: .assets(collection), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) + let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 1010b50907..6f727e0b20 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -33,8 +33,9 @@ private final class ChannelPermissionsControllerArguments { let presentConversionToBroadcastGroup: () -> Void let openChannelExample: () -> Void let updateSlowmode: (Int32) -> Void + let toggleIsOptionExpanded: (TelegramChatBannedRightsFlags) -> Void - init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (Peer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void) { + init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (Peer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void) { self.context = context self.updatePermission = updatePermission self.addPeer = addPeer @@ -47,6 +48,7 @@ private final class ChannelPermissionsControllerArguments { self.presentConversionToBroadcastGroup = presentConversionToBroadcastGroup self.openChannelExample = openChannelExample self.updateSlowmode = updateSlowmode + self.toggleIsOptionExpanded = toggleIsOptionExpanded } } @@ -63,9 +65,15 @@ private enum ChannelPermissionsEntryStableId: Hashable { case peer(PeerId) } +private struct SubPermission: Equatable { + var title: String + var flags: TelegramChatBannedRightsFlags + var isSelected: Bool +} + private enum ChannelPermissionsEntry: ItemListNodeEntry { case permissionsHeader(PresentationTheme, String) - case permission(PresentationTheme, Int, String, Bool, TelegramChatBannedRightsFlags, Bool?) + case permission(PresentationTheme, Int, String, Bool, TelegramChatBannedRightsFlags, Bool?, [SubPermission], Bool) case slowmodeHeader(PresentationTheme, String) case slowmode(PresentationTheme, PresentationStrings, Int32) case slowmodeInfo(PresentationTheme, String) @@ -96,7 +104,7 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { switch self { case .permissionsHeader: return .index(0) - case let .permission(_, index, _, _, _, _): + case let .permission(_, index, _, _, _, _, _, _): return .index(1 + index) case .conversionHeader: return .index(998) @@ -129,8 +137,8 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { } else { return false } - case let .permission(theme, index, title, value, rights, enabled): - if case .permission(theme, index, title, value, rights, enabled) = rhs { + case let .permission(theme, index, title, value, rights, enabled, subPermissions, isExpanded): + if case .permission(theme, index, title, value, rights, enabled, subPermissions, isExpanded) = rhs { return true } else { return false @@ -256,16 +264,47 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { switch self { case let .permissionsHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .permission(_, _, title, value, rights, enabled): - return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, type: .icon, enableInteractiveChanges: enabled != nil, enabled: enabled ?? true, sectionId: self.section, style: .blocks, updated: { value in - if let _ = enabled { - arguments.updatePermission(rights, value) - } else { + case let .permission(_, _, title, value, rights, enabled, subPermissions, isExpanded): + if !subPermissions.isEmpty { + return ItemListExpandableSwitchItem(presentationData: presentationData, title: title, value: value, isExpanded: isExpanded, subItems: subPermissions.map { item in + return ItemListExpandableSwitchItem.SubItem( + id: AnyHashable(item.flags.rawValue), + title: item.title, + isSelected: item.isSelected + ) + }, type: .icon, enableInteractiveChanges: enabled != nil, enabled: enabled ?? true, sectionId: self.section, style: .blocks, updated: { value in + if let _ = enabled { + arguments.updatePermission(rights, value) + } else { + arguments.presentRestrictedPermissionAlert(rights) + } + }, activatedWhileDisabled: { arguments.presentRestrictedPermissionAlert(rights) - } - }, activatedWhileDisabled: { - arguments.presentRestrictedPermissionAlert(rights) - }) + }, selectAction: { + arguments.toggleIsOptionExpanded(rights) + }, subAction: { item in + guard let value = item.id.base as? Int32 else { + return + } + let subRights = TelegramChatBannedRightsFlags(rawValue: value) + + if let _ = enabled { + arguments.updatePermission(subRights, !item.isSelected) + } else { + arguments.presentRestrictedPermissionAlert(subRights) + } + }) + } else { + return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, type: .icon, enableInteractiveChanges: enabled != nil, enabled: enabled ?? true, sectionId: self.section, style: .blocks, updated: { value in + if let _ = enabled { + arguments.updatePermission(rights, value) + } else { + arguments.presentRestrictedPermissionAlert(rights) + } + }, activatedWhileDisabled: { + arguments.presentRestrictedPermissionAlert(rights) + }) + } case let .slowmodeHeader(_, value): return ItemListSectionHeaderItem(presentationData: presentationData, text: value, sectionId: self.section) case let .slowmode(theme, strings, value): @@ -334,9 +373,11 @@ private struct ChannelPermissionsControllerState: Equatable { var searchingMembers: Bool = false var modifiedRightsFlags: TelegramChatBannedRightsFlags? var modifiedSlowmodeTimeout: Int32? + var expandedPermissions = Set() } func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatBannedRightsFlags, isForum: Bool) -> String { + //TODO:localize if right.contains(.banSendMessages) { return strings.Channel_BanUser_PermissionSendMessages } else if right.contains(.banSendMedia) { @@ -355,6 +396,20 @@ func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatB return strings.Channel_EditAdmin_PermissionPinMessages } else if right.contains(.banManageTopics) { return strings.Channel_EditAdmin_PermissionCreateTopics + } else if right.contains(.banSendPhotos) { + return "Send Photos" + } else if right.contains(.banSendVideos) { + return "Send Videos" + } else if right.contains(.banSendStickers) { + return strings.Channel_BanUser_PermissionSendStickersAndGifs + } else if right.contains(.banSendMusic) { + return "Send Music" + } else if right.contains(.banSendFiles) { + return "Send Files" + } else if right.contains(.banSendVoice) { + return "Send Voice Messages" + } else if right.contains(.banSendInstantVideos) { + return "Send Video Messages" } else { return "" } @@ -387,7 +442,13 @@ func compactStringForGroupPermission(strings: PresentationStrings, right: Telegr private let internal_allPossibleGroupPermissionList: [(TelegramChatBannedRightsFlags, TelegramChannelPermission)] = [ (.banSendMessages, .banMembers), (.banSendMedia, .banMembers), + (.banSendPhotos, .banMembers), + (.banSendVideos, .banMembers), (.banSendGifs, .banMembers), + (.banSendMusic, .banMembers), + (.banSendFiles, .banMembers), + (.banSendVoice, .banMembers), + (.banSendInstantVideos, .banMembers), (.banEmbedLinks, .banMembers), (.banSendPolls, .banMembers), (.banAddMembers, .banMembers), @@ -401,8 +462,6 @@ public func allGroupPermissionList(peer: EnginePeer) -> [(TelegramChatBannedRigh return [ (.banSendMessages, .banMembers), (.banSendMedia, .banMembers), - (.banSendGifs, .banMembers), - (.banEmbedLinks, .banMembers), (.banSendPolls, .banMembers), (.banAddMembers, .banMembers), (.banPinMessages, .pinMessages), @@ -413,8 +472,6 @@ public func allGroupPermissionList(peer: EnginePeer) -> [(TelegramChatBannedRigh return [ (.banSendMessages, .banMembers), (.banSendMedia, .banMembers), - (.banSendGifs, .banMembers), - (.banEmbedLinks, .banMembers), (.banSendPolls, .banMembers), (.banAddMembers, .banMembers), (.banPinMessages, .pinMessages), @@ -422,6 +479,19 @@ public func allGroupPermissionList(peer: EnginePeer) -> [(TelegramChatBannedRigh ] } } + +public func banSendMediaSubList() -> [(TelegramChatBannedRightsFlags, TelegramChannelPermission)] { + return [ + (.banSendPhotos, .banMembers), + (.banSendVideos, .banMembers), + (.banSendGifs, .banMembers), + (.banSendMusic, .banMembers), + (.banSendFiles, .banMembers), + (.banSendVoice, .banMembers), + (.banSendInstantVideos, .banMembers), + (.banEmbedLinks, .banMembers), + ] +} let publicGroupRestrictedPermissions: TelegramChatBannedRightsFlags = [ .banPinMessages, @@ -429,7 +499,7 @@ let publicGroupRestrictedPermissions: TelegramChatBannedRightsFlags = [ ] func groupPermissionDependencies(_ right: TelegramChatBannedRightsFlags) -> TelegramChatBannedRightsFlags { - if right.contains(.banSendMedia) { + if right.contains(.banSendMedia) || banSendMediaSubList().contains(where: { $0.0 == right }) { return [.banSendMessages] } else if right.contains(.banSendGifs) { return [.banSendMessages] @@ -476,7 +546,18 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen if !channel.hasPermission(correspondingAdminRight) { enabled = false } - entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: channel.flags.contains(.isForum)), !effectiveRightsFlags.contains(rights), rights, enabled)) + + var isSelected = !effectiveRightsFlags.contains(rights) + var subItems: [SubPermission] = [] + if rights == .banSendMedia { + isSelected = banSendMediaSubList().allSatisfy({ !effectiveRightsFlags.contains($0.0) }) + + for (subRight, _) in banSendMediaSubList() { + subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: channel.isForum), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight))) + } + } + + entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: channel.flags.contains(.isForum)), isSelected, rights, enabled, subItems, state.expandedPermissions.contains(rights))) rightIndex += 1 } @@ -513,7 +594,14 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen entries.append(.permissionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_SectionTitle)) var rightIndex: Int = 0 for (rights, _) in allGroupPermissionList(peer: .legacyGroup(group)) { - entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false), !effectiveRightsFlags.contains(rights), rights, true)) + var subItems: [SubPermission] = [] + if rights == .banSendMedia { + for (subRight, _) in banSendMediaSubList() { + subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: false), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight))) + } + } + + entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false), !effectiveRightsFlags.contains(rights), rights, true, subItems, state.expandedPermissions.contains(rights))) rightIndex += 1 } @@ -611,16 +699,47 @@ public func channelPermissionsController(context: AccountContext, updatedPresent } else { effectiveRightsFlags = TelegramChatBannedRightsFlags() } - if value { - effectiveRightsFlags.remove(rights) - effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) - } else { - effectiveRightsFlags.insert(rights) - for (right, _) in allGroupPermissionList(peer: .channel(channel)) { - if groupPermissionDependencies(right).contains(rights) { - effectiveRightsFlags.insert(right) + + if rights == .banSendMedia { + if value { + effectiveRightsFlags.remove(rights) + for item in banSendMediaSubList() { + effectiveRightsFlags.remove(item.0) + } + } else { + effectiveRightsFlags.insert(rights) + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { + if groupPermissionDependencies(right).contains(rights) { + effectiveRightsFlags.insert(right) + } + } + + for item in banSendMediaSubList() { + effectiveRightsFlags.insert(item.0) + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { + if groupPermissionDependencies(right).contains(item.0) { + effectiveRightsFlags.insert(right) + } + } } } + } else { + if value { + effectiveRightsFlags.remove(rights) + effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) + } else { + effectiveRightsFlags.insert(rights) + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { + if groupPermissionDependencies(right).contains(rights) { + effectiveRightsFlags.insert(right) + } + } + } + } + if banSendMediaSubList().allSatisfy({ !effectiveRightsFlags.contains($0.0) }) { + effectiveRightsFlags.remove(.banSendMedia) + } else { + effectiveRightsFlags.insert(.banSendMedia) } state.modifiedRightsFlags = effectiveRightsFlags return state @@ -868,6 +987,16 @@ public func channelPermissionsController(context: AccountContext, updatedPresent })) } }) + }, toggleIsOptionExpanded: { flags in + updateState { state in + var state = state + if state.expandedPermissions.contains(flags) { + state.expandedPermissions.remove(flags) + } else { + state.expandedPermissions.insert(flags) + } + return state + } }) let previousParticipants = Atomic<[RenderedChannelParticipant]?>(value: nil) @@ -889,6 +1018,8 @@ public func channelPermissionsController(context: AccountContext, updatedPresent return .single((view, peers.1)) } + let previousExpandedPermissionsValue = Atomic?>(value: nil) + let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), viewAndParticipants) |> deliverOnMainQueue @@ -912,6 +1043,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent } let previous = previousParticipants.swap(participants) + let previousExpandedPermissions = previousExpandedPermissionsValue.swap(state.expandedPermissions) var searchItem: ItemListControllerSearch? if state.searchingMembers { @@ -940,8 +1072,13 @@ public func channelPermissionsController(context: AccountContext, updatedPresent }) } + var animateChanges = previous != nil && participants != nil && previous!.count >= participants!.count + if let previousExpandedPermissions, previousExpandedPermissions != state.expandedPermissions { + animateChanges = true + } + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.GroupInfo_Permissions_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelPermissionsControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: participants), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && participants != nil && previous!.count >= participants!.count) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelPermissionsControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: participants), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: animateChanges) return (controllerState, (listState, arguments)) } diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index b0722eba61..98eb144214 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -76,8 +76,8 @@ public enum DataAndStorageEntryTag: ItemListItemTag { } private enum DataAndStorageEntry: ItemListNodeEntry { - case storageUsage(PresentationTheme, String) - case networkUsage(PresentationTheme, String) + case storageUsage(PresentationTheme, String, String) + case networkUsage(PresentationTheme, String, String) case automaticDownloadHeader(PresentationTheme, String) case automaticDownloadCellular(PresentationTheme, String, String) case automaticDownloadWifi(PresentationTheme, String, String) @@ -170,14 +170,14 @@ private enum DataAndStorageEntry: ItemListNodeEntry { static func ==(lhs: DataAndStorageEntry, rhs: DataAndStorageEntry) -> Bool { switch lhs { - case let .storageUsage(lhsTheme, lhsText): - if case let .storageUsage(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .storageUsage(lhsTheme, lhsText, lhsValue): + if case let .storageUsage(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { return true } else { return false } - case let .networkUsage(lhsTheme, lhsText): - if case let .networkUsage(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .networkUsage(lhsTheme, lhsText, lhsValue): + if case let .networkUsage(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { return true } else { return false @@ -306,12 +306,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! DataAndStorageControllerArguments switch self { - case let .storageUsage(_, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Storage")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: { + case let .storageUsage(_, text, value): + return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Storage")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.openStorageUsage() }) - case let .networkUsage(_, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Network")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: { + case let .networkUsage(_, text, value): + return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Network")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.openNetworkUsage() }) case let .automaticDownloadHeader(_, text): @@ -478,11 +478,11 @@ private func stringForAutoDownloadSetting(strings: PresentationStrings, decimalS } } -private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData, defaultWebBrowser: String, contentSettingsConfiguration: ContentSettingsConfiguration?) -> [DataAndStorageEntry] { +private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData, defaultWebBrowser: String, contentSettingsConfiguration: ContentSettingsConfiguration?, networkUsage: Int64, storageUsage: Int64) -> [DataAndStorageEntry] { var entries: [DataAndStorageEntry] = [] - entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache)) - entries.append(.networkUsage(presentationData.theme, presentationData.strings.NetworkUsageSettings_Title)) + entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache, dataSizeString(storageUsage, formatting: DataSizeStringFormatting(presentationData: presentationData)))) + entries.append(.networkUsage(presentationData.theme, presentationData.strings.NetworkUsageSettings_Title, dataSizeString(networkUsage, formatting: DataSizeStringFormatting(presentationData: presentationData)))) entries.append(.automaticDownloadHeader(presentationData.theme, presentationData.strings.ChatSettings_AutoDownloadTitle.uppercased())) entries.append(.automaticDownloadCellular(presentationData.theme, presentationData.strings.ChatSettings_AutoDownloadUsingCellular, stringForAutoDownloadSetting(strings: presentationData.strings, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, settings: data.automaticMediaDownloadSettings, connectionType: .cellular))) @@ -554,6 +554,67 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da contentSettingsConfiguration.set(.single(nil) |> then(updatedContentSettingsConfiguration)) + struct UsageData: Equatable { + var network: Int64 + var storage: Int64 + } + let usageSignal: Signal = combineLatest( + context.account.postbox.mediaBox.storageBox.totalSize(), + context.account.postbox.mediaBox.cacheStorageBox.totalSize(), + accountNetworkUsageStats(account: context.account, reset: []) + ) + |> map { disk1, disk2, networkStats -> UsageData in + var network: Int64 = 0 + + var keys: [KeyPath] = [] + + keys.append(\.generic.cellular.outgoing) + keys.append(\.generic.cellular.incoming) + keys.append(\.generic.wifi.incoming) + keys.append(\.generic.wifi.outgoing) + + keys.append(\.image.cellular.outgoing) + keys.append(\.image.cellular.incoming) + keys.append(\.image.wifi.incoming) + keys.append(\.image.wifi.outgoing) + + keys.append(\.video.cellular.outgoing) + keys.append(\.video.cellular.incoming) + keys.append(\.video.wifi.incoming) + keys.append(\.video.wifi.outgoing) + + keys.append(\.audio.cellular.outgoing) + keys.append(\.audio.cellular.incoming) + keys.append(\.audio.wifi.incoming) + keys.append(\.audio.wifi.outgoing) + + keys.append(\.file.cellular.outgoing) + keys.append(\.file.cellular.incoming) + keys.append(\.file.wifi.incoming) + keys.append(\.file.wifi.outgoing) + + keys.append(\.call.cellular.outgoing) + keys.append(\.call.cellular.incoming) + keys.append(\.call.wifi.incoming) + keys.append(\.call.wifi.outgoing) + + keys.append(\.sticker.cellular.outgoing) + keys.append(\.sticker.cellular.incoming) + keys.append(\.sticker.wifi.incoming) + keys.append(\.sticker.wifi.outgoing) + + keys.append(\.voiceMessage.cellular.outgoing) + keys.append(\.voiceMessage.cellular.incoming) + keys.append(\.voiceMessage.wifi.incoming) + keys.append(\.voiceMessage.wifi.outgoing) + + for key in keys { + network += networkStats[keyPath: key] + } + + return UsageData(network: network, storage: disk1 + disk2) + } + let dataAndStorageDataPromise = Promise() dataAndStorageDataPromise.set(context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings, ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings, SharedDataKeys.proxySettings]) |> map { sharedData -> DataAndStorageData in @@ -599,7 +660,13 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da return storageUsageExceptionsScreen(context: context, category: category) })) }, openNetworkUsage: { - pushControllerImpl?(networkUsageStatsController(context: context)) + //pushControllerImpl?(networkUsageStatsController(context: context)) + + let _ = (accountNetworkUsageStats(account: context.account, reset: []) + |> take(1) + |> deliverOnMainQueue).start(next: { stats in + pushControllerImpl?(DataUsageScreen(context: context, stats: stats)) + }) }, openProxy: { pushControllerImpl?(proxySettingsController(context: context)) }, openAutomaticDownloadConnectionType: { connectionType in @@ -679,9 +746,10 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da statePromise.get(), dataAndStorageDataPromise.get(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings]), - contentSettingsConfiguration.get() + contentSettingsConfiguration.get(), + usageSignal ) - |> map { presentationData, state, dataAndStorageData, sharedData, contentSettingsConfiguration -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, state, dataAndStorageData, sharedData, contentSettingsConfiguration, usageSignal -> (ItemListControllerState, (ItemListNodeState, Any)) in let webBrowserSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) ?? WebBrowserSettings.defaultSettings let options = availableOpenInOptions(context: context, item: .url(url: "https://telegram.org")) let defaultWebBrowser: String @@ -692,7 +760,7 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChatSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData, defaultWebBrowser: defaultWebBrowser, contentSettingsConfiguration: contentSettingsConfiguration), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData, defaultWebBrowser: defaultWebBrowser, contentSettingsConfiguration: contentSettingsConfiguration, networkUsage: usageSignal.network, storageUsage: usageSignal.storage), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift index 088e5b6443..e629c476d9 100644 --- a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift @@ -123,7 +123,13 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC self.animationNode = animationNode let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) - let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0)) + let fitSize: CGSize + if item.file.isCustomEmoji { + fitSize = CGSize(width: 200.0, height: 200.0) + } else { + fitSize = CGSize(width: 400.0, height: 400.0) + } + let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize) animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: isPremiumSticker ? .once : .loop, mode: .direct(cachePathPrefix: nil)) animationNode.visibility = true @@ -197,7 +203,9 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { let boundingSize: CGSize - if let _ = self.additionalAnimationNode { + if self.item.file.isCustomEmoji { + boundingSize = CGSize(width: 120.0, height: 120.0) + } else if let _ = self.additionalAnimationNode { boundingSize = CGSize(width: 240.0, height: 240.0).fitted(size) } else { boundingSize = CGSize(width: 180.0, height: 180.0).fitted(size) @@ -239,7 +247,11 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC self.textNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - textSize.width) / 2.0) - centerOffset, y: -textSize.height - textSpacing), size: textSize) - return CGSize(width: size.width, height: imageFrame.height + textSize.height + textSpacing) + if self.item.file.isCustomEmoji { + return CGSize(width: size.width, height: imageFrame.height) + } else { + return CGSize(width: size.width, height: imageFrame.height + textSize.height + textSpacing) + } } else { return CGSize(width: size.width, height: 10.0) } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index f321ea20bf..fe1a9e250b 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -939,7 +939,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-842824308] = { return Api.account.WallPapers.parse_wallPapers($0) } dict[471437699] = { return Api.account.WallPapers.parse_wallPapersNotModified($0) } dict[-313079300] = { return Api.account.WebAuthorizations.parse_webAuthorizations($0) } - dict[872119224] = { return Api.auth.Authorization.parse_authorization($0) } + dict[782418132] = { return Api.auth.Authorization.parse_authorization($0) } dict[1148485274] = { return Api.auth.Authorization.parse_authorizationSignUpRequired($0) } dict[1948046307] = { return Api.auth.CodeType.parse_codeTypeCall($0) } dict[577556219] = { return Api.auth.CodeType.parse_codeTypeFlashCall($0) } @@ -953,6 +953,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) } dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) } dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) } + dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) } dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } dict[1511364673] = { return Api.auth.SentCodeType.parse_sentCodeTypeEmailCode($0) } diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 1e09defc19..ffd21fca27 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -1176,18 +1176,19 @@ public extension Api.account { } public extension Api.auth { enum Authorization: TypeConstructorDescription { - case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, user: Api.User) + case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let user): + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): if boxed { - buffer.appendInt32(872119224) + buffer.appendInt32(782418132) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(otherwiseReloginDays!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} user.serialize(buffer, true) break case .authorizationSignUpRequired(let flags, let termsOfService): @@ -1202,8 +1203,8 @@ public extension Api.auth { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let user): - return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("user", user as Any)]) + case .authorization(let flags, let otherwiseReloginDays, let tmpSessions, let futureAuthToken, let user): + return ("authorization", [("flags", flags as Any), ("otherwiseReloginDays", otherwiseReloginDays as Any), ("tmpSessions", tmpSessions as Any), ("futureAuthToken", futureAuthToken as Any), ("user", user as Any)]) case .authorizationSignUpRequired(let flags, let termsOfService): return ("authorizationSignUpRequired", [("flags", flags as Any), ("termsOfService", termsOfService as Any)]) } @@ -1216,16 +1217,19 @@ public extension Api.auth { if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } var _3: Int32? if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Api.User? + var _4: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) } + var _5: Api.User? if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.User + _5 = Api.parse(reader, signature: signature) as? Api.User } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, user: _4!) + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index 10408368cb..eca99479a8 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -279,6 +279,7 @@ public extension Api.auth { public extension Api.auth { enum SentCode: TypeConstructorDescription { case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?) + case sentCodeSuccess(authorization: Api.auth.Authorization) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -292,6 +293,12 @@ public extension Api.auth { if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} break + case .sentCodeSuccess(let authorization): + if boxed { + buffer.appendInt32(596704836) + } + authorization.serialize(buffer, true) + break } } @@ -299,6 +306,8 @@ public extension Api.auth { switch self { case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout): return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)]) + case .sentCodeSuccess(let authorization): + return ("sentCodeSuccess", [("authorization", authorization as Any)]) } } @@ -329,6 +338,19 @@ public extension Api.auth { return nil } } + public static func parse_sentCodeSuccess(_ reader: BufferReader) -> SentCode? { + var _1: Api.auth.Authorization? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization + } + let _c1 = _1 != nil + if _c1 { + return Api.auth.SentCode.sentCodeSuccess(authorization: _1!) + } + else { + return nil + } + } } } @@ -1112,61 +1134,3 @@ public extension Api.contacts { } } -public extension Api.contacts { - enum ResolvedPeer: TypeConstructorDescription { - case resolvedPeer(peer: Api.Peer, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .resolvedPeer(let peer, let chats, let users): - if boxed { - buffer.appendInt32(2131196633) - } - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .resolvedPeer(let peer, let chats, let users): - return ("resolvedPeer", [("peer", peer as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_resolvedPeer(_ reader: BufferReader) -> ResolvedPeer? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 0934ea5662..dd25f3fef8 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,3 +1,61 @@ +public extension Api.contacts { + enum ResolvedPeer: TypeConstructorDescription { + case resolvedPeer(peer: Api.Peer, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .resolvedPeer(let peer, let chats, let users): + if boxed { + buffer.appendInt32(2131196633) + } + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .resolvedPeer(let peer, let chats, let users): + return ("resolvedPeer", [("peer", peer as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_resolvedPeer(_ reader: BufferReader) -> ResolvedPeer? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} public extension Api.contacts { enum TopPeers: TypeConstructorDescription { case topPeers(categories: [Api.TopPeerCategoryPeers], chats: [Api.Chat], users: [Api.User]) @@ -1248,61 +1306,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum AvailableReactions: TypeConstructorDescription { - case availableReactions(hash: Int32, reactions: [Api.AvailableReaction]) - case availableReactionsNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .availableReactions(let hash, let reactions): - if boxed { - buffer.appendInt32(1989032621) - } - serializeInt32(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions.count)) - for item in reactions { - item.serialize(buffer, true) - } - break - case .availableReactionsNotModified: - if boxed { - buffer.appendInt32(-1626924713) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .availableReactions(let hash, let reactions): - return ("availableReactions", [("hash", hash as Any), ("reactions", reactions as Any)]) - case .availableReactionsNotModified: - return ("availableReactionsNotModified", []) - } - } - - public static func parse_availableReactions(_ reader: BufferReader) -> AvailableReactions? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.AvailableReaction]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AvailableReaction.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) - } - else { - return nil - } - } - public static func parse_availableReactionsNotModified(_ reader: BufferReader) -> AvailableReactions? { - return Api.messages.AvailableReactions.availableReactionsNotModified - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 0dde8a5bff..3c45a05c85 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,61 @@ +public extension Api.messages { + enum AvailableReactions: TypeConstructorDescription { + case availableReactions(hash: Int32, reactions: [Api.AvailableReaction]) + case availableReactionsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .availableReactions(let hash, let reactions): + if boxed { + buffer.appendInt32(1989032621) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { + item.serialize(buffer, true) + } + break + case .availableReactionsNotModified: + if boxed { + buffer.appendInt32(-1626924713) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .availableReactions(let hash, let reactions): + return ("availableReactions", [("hash", hash as Any), ("reactions", reactions as Any)]) + case .availableReactionsNotModified: + return ("availableReactionsNotModified", []) + } + } + + public static func parse_availableReactions(_ reader: BufferReader) -> AvailableReactions? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.AvailableReaction]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AvailableReaction.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) + } + else { + return nil + } + } + public static func parse_availableReactionsNotModified(_ reader: BufferReader) -> AvailableReactions? { + return Api.messages.AvailableReactions.availableReactionsNotModified + } + + } +} public extension Api.messages { enum BotCallbackAnswer: TypeConstructorDescription { case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) @@ -1432,281 +1490,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum MessageViews: TypeConstructorDescription { - case messageViews(views: [Api.MessageViews], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .messageViews(let views, let chats, let users): - if boxed { - buffer.appendInt32(-1228606141) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .messageViews(let views, let chats, let users): - return ("messageViews", [("views", views as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? { - var _1: [Api.MessageViews]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum Messages: TypeConstructorDescription { - case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User]) - case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - case messagesNotModified(count: Int32) - case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): - if boxed { - buffer.appendInt32(-948520370) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topics.count)) - for item in topics { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .messages(let messages, let chats, let users): - if boxed { - buffer.appendInt32(-1938715001) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .messagesNotModified(let count): - if boxed { - buffer.appendInt32(1951620897) - } - serializeInt32(count, buffer: buffer, boxed: false) - break - case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): - if boxed { - buffer.appendInt32(978610270) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextRate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): - return ("channelMessages", [("flags", flags as Any), ("pts", pts as Any), ("count", count as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("topics", topics as Any), ("chats", chats as Any), ("users", users as Any)]) - case .messages(let messages, let chats, let users): - return ("messages", [("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .messagesNotModified(let count): - return ("messagesNotModified", [("count", count as Any)]) - case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): - return ("messagesSlice", [("flags", flags as Any), ("count", count as Any), ("nextRate", nextRate as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_channelMessages(_ reader: BufferReader) -> Messages? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } - var _5: [Api.Message]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _6: [Api.ForumTopic]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) - } - var _7: [Api.Chat]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _8: [Api.User]? - if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) - } - else { - return nil - } - } - public static func parse_messages(_ reader: BufferReader) -> Messages? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.Messages.messages(messages: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - public static func parse_messagesNotModified(_ reader: BufferReader) -> Messages? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.Messages.messagesNotModified(count: _1!) - } - else { - return nil - } - } - public static func parse_messagesSlice(_ reader: BufferReader) -> Messages? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } - var _5: [Api.Message]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _6: [Api.Chat]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _7: [Api.User]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 04a7bd362c..3f476a7fd5 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,3 +1,281 @@ +public extension Api.messages { + enum MessageViews: TypeConstructorDescription { + case messageViews(views: [Api.MessageViews], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageViews(let views, let chats, let users): + if boxed { + buffer.appendInt32(-1228606141) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .messageViews(let views, let chats, let users): + return ("messageViews", [("views", views as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? { + var _1: [Api.MessageViews]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum Messages: TypeConstructorDescription { + case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User]) + case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case messagesNotModified(count: Int32) + case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): + if boxed { + buffer.appendInt32(-948520370) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topics.count)) + for item in topics { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .messages(let messages, let chats, let users): + if boxed { + buffer.appendInt32(-1938715001) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .messagesNotModified(let count): + if boxed { + buffer.appendInt32(1951620897) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): + if boxed { + buffer.appendInt32(978610270) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextRate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): + return ("channelMessages", [("flags", flags as Any), ("pts", pts as Any), ("count", count as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("topics", topics as Any), ("chats", chats as Any), ("users", users as Any)]) + case .messages(let messages, let chats, let users): + return ("messages", [("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .messagesNotModified(let count): + return ("messagesNotModified", [("count", count as Any)]) + case .messagesSlice(let flags, let count, let nextRate, let offsetIdOffset, let messages, let chats, let users): + return ("messagesSlice", [("flags", flags as Any), ("count", count as Any), ("nextRate", nextRate as Any), ("offsetIdOffset", offsetIdOffset as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_channelMessages(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.ForumTopic]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) + } + var _7: [Api.Chat]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _8: [Api.User]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) + } + else { + return nil + } + } + public static func parse_messages(_ reader: BufferReader) -> Messages? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.Messages.messages(messages: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + public static func parse_messagesNotModified(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.Messages.messagesNotModified(count: _1!) + } + else { + return nil + } + } + public static func parse_messagesSlice(_ reader: BufferReader) -> Messages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum PeerDialogs: TypeConstructorDescription { case peerDialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) @@ -1086,327 +1364,3 @@ public extension Api.payments { } } -public extension Api.payments { - enum PaymentForm: TypeConstructorDescription { - case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): - if boxed { - buffer.appendInt32(-1610250415) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(formId, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)} - if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(additionalMethods!.count)) - for item in additionalMethods! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(savedCredentials!.count)) - for item in savedCredentials! { - 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 .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): - return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)]) - } - } - - public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: Api.WebDocument? - if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _7: Api.Invoice? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _8: Int64? - _8 = reader.readInt64() - var _9: String? - _9 = parseString(reader) - var _10: String? - if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) } - var _11: Api.DataJSON? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.DataJSON - } } - var _12: [Api.PaymentFormMethod]? - if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self) - } } - var _13: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _14: [Api.PaymentSavedCredentials]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self) - } } - var _15: [Api.User]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum PaymentReceipt: TypeConstructorDescription { - case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - if boxed { - buffer.appendInt32(1891958275) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - serializeString(credentialsTitle, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) - } - } - - public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: String? - _6 = parseString(reader) - var _7: Api.WebDocument? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _8: Api.Invoice? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _9: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _10: Api.ShippingOption? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption - } } - var _11: Int64? - if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } - var _12: String? - _12 = parseString(reader) - var _13: Int64? - _13 = reader.readInt64() - var _14: String? - _14 = parseString(reader) - var _15: [Api.User]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - indirect enum PaymentResult: TypeConstructorDescription { - case paymentResult(updates: Api.Updates) - case paymentVerificationNeeded(url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentResult(let updates): - if boxed { - buffer.appendInt32(1314881805) - } - updates.serialize(buffer, true) - break - case .paymentVerificationNeeded(let url): - if boxed { - buffer.appendInt32(-666824391) - } - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .paymentResult(let updates): - return ("paymentResult", [("updates", updates as Any)]) - case .paymentVerificationNeeded(let url): - return ("paymentVerificationNeeded", [("url", url as Any)]) - } - } - - public static func parse_paymentResult(_ reader: BufferReader) -> PaymentResult? { - var _1: Api.Updates? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Updates - } - let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentResult(updates: _1!) - } - else { - return nil - } - } - public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum SavedInfo: TypeConstructorDescription { - case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .savedInfo(let flags, let savedInfo): - if boxed { - buffer.appendInt32(-74456004) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedInfo(let flags, let savedInfo): - return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) - } - } - - public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index f6509a4c29..85a52a823d 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,3 +1,327 @@ +public extension Api.payments { + enum PaymentForm: TypeConstructorDescription { + case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): + if boxed { + buffer.appendInt32(-1610250415) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(formId, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(additionalMethods!.count)) + for item in additionalMethods! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(savedCredentials!.count)) + for item in savedCredentials! { + 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 .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users): + return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)]) + } + } + + public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Api.WebDocument? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _7: Api.Invoice? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _8: Int64? + _8 = reader.readInt64() + var _9: String? + _9 = parseString(reader) + var _10: String? + if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) } + var _11: Api.DataJSON? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.DataJSON + } } + var _12: [Api.PaymentFormMethod]? + if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { + _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self) + } } + var _13: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _14: [Api.PaymentSavedCredentials]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self) + } } + var _15: [Api.User]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil + let _c15 = _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum PaymentReceipt: TypeConstructorDescription { + case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + if boxed { + buffer.appendInt32(1891958275) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + serializeString(credentialsTitle, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) + } + } + + public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + var _7: Api.WebDocument? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _8: Api.Invoice? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _9: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _10: Api.ShippingOption? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption + } } + var _11: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } + var _12: String? + _12 = parseString(reader) + var _13: Int64? + _13 = reader.readInt64() + var _14: String? + _14 = parseString(reader) + var _15: [Api.User]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + indirect enum PaymentResult: TypeConstructorDescription { + case paymentResult(updates: Api.Updates) + case paymentVerificationNeeded(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentResult(let updates): + if boxed { + buffer.appendInt32(1314881805) + } + updates.serialize(buffer, true) + break + case .paymentVerificationNeeded(let url): + if boxed { + buffer.appendInt32(-666824391) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentResult(let updates): + return ("paymentResult", [("updates", updates as Any)]) + case .paymentVerificationNeeded(let url): + return ("paymentVerificationNeeded", [("url", url as Any)]) + } + } + + public static func parse_paymentResult(_ reader: BufferReader) -> PaymentResult? { + var _1: Api.Updates? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Updates + } + let _c1 = _1 != nil + if _c1 { + return Api.payments.PaymentResult.paymentResult(updates: _1!) + } + else { + return nil + } + } + public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum SavedInfo: TypeConstructorDescription { + case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedInfo(let flags, let savedInfo): + if boxed { + buffer.appendInt32(-74456004) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedInfo(let flags, let savedInfo): + return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) + } + } + + public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) + } + else { + return nil + } + } + + } +} public extension Api.payments { enum ValidatedRequestedInfo: TypeConstructorDescription { case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) @@ -1220,207 +1544,3 @@ public extension Api.updates { } } -public extension Api.updates { - enum Difference: TypeConstructorDescription { - case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) - case differenceEmpty(date: Int32, seq: Int32) - case differenceSlice(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], intermediateState: Api.updates.State) - case differenceTooLong(pts: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): - if boxed { - buffer.appendInt32(16030880) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newEncryptedMessages.count)) - for item in newEncryptedMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - state.serialize(buffer, true) - break - case .differenceEmpty(let date, let seq): - if boxed { - buffer.appendInt32(1567990072) - } - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) - break - case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): - if boxed { - buffer.appendInt32(-1459938943) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newEncryptedMessages.count)) - for item in newEncryptedMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - intermediateState.serialize(buffer, true) - break - case .differenceTooLong(let pts): - if boxed { - buffer.appendInt32(1258196845) - } - serializeInt32(pts, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): - return ("difference", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) - case .differenceEmpty(let date, let seq): - return ("differenceEmpty", [("date", date as Any), ("seq", seq as Any)]) - case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): - return ("differenceSlice", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("intermediateState", intermediateState as Any)]) - case .differenceTooLong(let pts): - return ("differenceTooLong", [("pts", pts as Any)]) - } - } - - public static func parse_difference(_ reader: BufferReader) -> Difference? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.EncryptedMessage]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) - } - var _3: [Api.Update]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _6: Api.updates.State? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.updates.State - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) - } - else { - return nil - } - } - public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) - } - else { - return nil - } - } - public static func parse_differenceSlice(_ reader: BufferReader) -> Difference? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.EncryptedMessage]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) - } - var _3: [Api.Update]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _6: Api.updates.State? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.updates.State - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) - } - else { - return nil - } - } - public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.updates.Difference.differenceTooLong(pts: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index a2670b0450..f6d0dc14fb 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1,3 +1,207 @@ +public extension Api.updates { + enum Difference: TypeConstructorDescription { + case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) + case differenceEmpty(date: Int32, seq: Int32) + case differenceSlice(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], intermediateState: Api.updates.State) + case differenceTooLong(pts: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): + if boxed { + buffer.appendInt32(16030880) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + state.serialize(buffer, true) + break + case .differenceEmpty(let date, let seq): + if boxed { + buffer.appendInt32(1567990072) + } + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + break + case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): + if boxed { + buffer.appendInt32(-1459938943) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + intermediateState.serialize(buffer, true) + break + case .differenceTooLong(let pts): + if boxed { + buffer.appendInt32(1258196845) + } + serializeInt32(pts, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): + return ("difference", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) + case .differenceEmpty(let date, let seq): + return ("differenceEmpty", [("date", date as Any), ("seq", seq as Any)]) + case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): + return ("differenceSlice", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("intermediateState", intermediateState as Any)]) + case .differenceTooLong(let pts): + return ("differenceTooLong", [("pts", pts as Any)]) + } + } + + public static func parse_difference(_ reader: BufferReader) -> Difference? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.EncryptedMessage]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) + } + var _3: [Api.Update]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: Api.updates.State? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.updates.State + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) + } + else { + return nil + } + } + public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) + } + else { + return nil + } + } + public static func parse_differenceSlice(_ reader: BufferReader) -> Difference? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.EncryptedMessage]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) + } + var _3: [Api.Update]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: Api.updates.State? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.updates.State + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) + } + else { + return nil + } + } + public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.updates.Difference.differenceTooLong(pts: _1!) + } + else { + return nil + } + } + + } +} public extension Api.updates { enum State: TypeConstructorDescription { case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32) diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index 9f1a69798c..e773fda7a9 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -438,40 +438,14 @@ private func cleanupAccount(networkArguments: NetworkInitializationArguments, ac return .single(nil) } |> mapToSignal { result -> Signal in - let _ = (accountManager.transaction { transaction -> Void in - var tokens = transaction.getStoredLoginTokens() - switch result { - case let .loggedOut(_, futureAuthToken): - if let futureAuthToken = futureAuthToken { - tokens.insert(futureAuthToken.makeData(), at: 0) - } - default: - break + switch result { + case let .loggedOut(_, futureAuthToken): + if let futureAuthToken = futureAuthToken { + storeFutureLoginToken(accountManager: accountManager, token: futureAuthToken.makeData()) } - - var cloudValue: [Data] = [] - if let list = NSUbiquitousKeyValueStore.default.object(forKey: "T_SLTokens") as? [String] { - cloudValue = list.compactMap { string -> Data? in - guard let stringData = string.data(using: .utf8) else { - return nil - } - return Data(base64Encoded: stringData) - } - } - for data in cloudValue { - if !tokens.contains(data) { - tokens.insert(data, at: 0) - } - } - if tokens.count > 20 { - tokens.removeLast(tokens.count - 20) - } - - NSUbiquitousKeyValueStore.default.set(tokens.map { $0.base64EncodedString() }, forKey: "T_SLTokens") - NSUbiquitousKeyValueStore.default.synchronize() - - transaction.setStoredLoginTokens(tokens) - }).start() + default: + break + } account.shouldBeServiceTaskMaster.set(.single(.never)) return accountManager.transaction { transaction -> Void in transaction.updateRecord(id, { _ in diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannelBannedRights.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannelBannedRights.swift index 48d4ae7795..8de784a2cd 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannelBannedRights.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannelBannedRights.swift @@ -2,16 +2,20 @@ import Foundation import Postbox import TelegramApi - extension TelegramChatBannedRights { init(apiBannedRights: Api.ChatBannedRights) { switch apiBannedRights { case let .chatBannedRights(flags, untilDate): - self.init(flags: TelegramChatBannedRightsFlags(rawValue: flags), untilDate: untilDate) + var effectiveFlags = TelegramChatBannedRightsFlags(rawValue: flags) + effectiveFlags.remove(.banSendMedia) + self.init(flags: effectiveFlags, untilDate: untilDate) } } var apiBannedRights: Api.ChatBannedRights { - return .chatBannedRights(flags: self.flags.rawValue, untilDate: self.untilDate) + var effectiveFlags = self.flags + effectiveFlags.remove(.banSendMedia) + + return .chatBannedRights(flags: effectiveFlags.rawValue, untilDate: self.untilDate) } } diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 6a17f2e8dd..2711008330 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -71,7 +71,42 @@ private func ~=(pattern: Regex, matchable: T) -> return matchable.match(pattern) } -public func sendAuthorizationCode(accountManager: AccountManager, account: UnauthorizedAccount, phoneNumber: String, apiId: Int32, apiHash: String, syncContacts: Bool) -> Signal { +public enum SendAuthorizationCodeResult { + case sentCode(UnauthorizedAccount) + case loggedIn +} + +func storeFutureLoginToken(accountManager: AccountManager, token: Data) { + let _ = (accountManager.transaction { transaction -> Void in + var tokens = transaction.getStoredLoginTokens() + tokens.insert(token, at: 0) + + var cloudValue: [Data] = [] + if let list = NSUbiquitousKeyValueStore.default.object(forKey: "T_SLTokens") as? [String] { + cloudValue = list.compactMap { string -> Data? in + guard let stringData = string.data(using: .utf8) else { + return nil + } + return Data(base64Encoded: stringData) + } + } + for data in cloudValue { + if !tokens.contains(data) { + tokens.insert(data, at: 0) + } + } + if tokens.count > 20 { + tokens.removeLast(tokens.count - 20) + } + + NSUbiquitousKeyValueStore.default.set(tokens.map { $0.base64EncodedString() }, forKey: "T_SLTokens") + NSUbiquitousKeyValueStore.default.synchronize() + + transaction.setStoredLoginTokens(tokens) + }).start() +} + +public func sendAuthorizationCode(accountManager: AccountManager, account: UnauthorizedAccount, phoneNumber: String, apiId: Int32, apiHash: String, syncContacts: Bool, forcedPasswordSetupNotice: @escaping (Int32) -> (NoticeEntryKey, CodableEntry)?) -> Signal { var cloudValue: [Data] = [] if let list = NSUbiquitousKeyValueStore.default.object(forKey: "T_SLTokens") as? [String] { cloudValue = list.compactMap { string -> Data? in @@ -85,7 +120,7 @@ public func sendAuthorizationCode(accountManager: AccountManager castError(AuthorizationCodeRequestError.self) - |> mapToSignal { localAuthTokens -> Signal in + |> mapToSignal { localAuthTokens -> Signal in var authTokens = localAuthTokens for data in cloudValue { if !authTokens.contains(data) { @@ -168,11 +203,12 @@ public func sendAuthorizationCode(accountManager: AccountManager timeout(20.0, queue: Queue.concurrentDefaultQueue(), alternate: .fail(.timeout)) return codeAndAccount - |> mapToSignal { result, account -> Signal in - return account.postbox.transaction { transaction -> UnauthorizedAccount in + |> mapToSignal { result, account -> Signal in + return account.postbox.transaction { transaction -> Signal in switch result { case let .password(hint): transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .passwordEntry(hint: hint ?? "", number: nil, code: nil, suggestReset: false, syncContacts: syncContacts))) + return .single(.sentCode(account)) case let .sentCode(sentCode): switch sentCode { case let .sentCode(_, type, phoneCodeHash, nextType, timeout): @@ -182,12 +218,35 @@ public func sendAuthorizationCode(accountManager: AccountManager SendAuthorizationCodeResult in + switchToAuthorizedAccount(transaction: transaction, account: account) + return .loggedIn + } + |> castError(AuthorizationCodeRequestError.self) + case .authorizationSignUpRequired: + return .never() + } } + return .single(.sentCode(account)) } - return account } |> mapError { _ -> AuthorizationCodeRequestError in } + |> switchToLatest } } } @@ -215,17 +274,18 @@ public func resendAuthorizationCode(account: UnauthorizedAccount) -> Signal mapToSignal { sentCode -> Signal in return account.postbox.transaction { transaction -> Void in switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - - var parsedNextType: AuthorizationCodeNextType? - if let nextType = nextType { - parsedNextType = AuthorizationCodeNextType(apiType: nextType) - } - - transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts))) + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) + } + + transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts))) + case .sentCodeSuccess: + break } - } |> mapError { _ -> AuthorizationCodeRequestError in } + } |> mapError { _ -> AuthorizationCodeRequestError in } } } else { return .fail(.generic(info: nil)) @@ -496,6 +556,8 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat } transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts))) + case .sentCodeSuccess: + break } case .emailVerified: break @@ -594,7 +656,11 @@ public func authorizeWithCode(accountManager: AccountManager mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> Signal in switch result { - case let .authorization(_, _, _, user): + case let .authorization(_, _, _, futureAuthToken, user): + if let futureAuthToken = futureAuthToken { + storeFutureLoginToken(accountManager: accountManager, token: futureAuthToken.makeData()) + } + let user = TelegramUser(user: user) let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil) /*transaction.updatePeersInternal([user], update: { current, peer -> Peer? in @@ -720,7 +790,11 @@ public final class RecoveredAccountData { public func loginWithRecoveredAccountData(accountManager: AccountManager, account: UnauthorizedAccount, recoveredAccountData: RecoveredAccountData, syncContacts: Bool) -> Signal { return account.postbox.transaction { transaction -> Signal in switch recoveredAccountData.authorization { - case let .authorization(_, _, _, user): + case let .authorization(_, _, _, futureAuthToken, user): + if let futureAuthToken = futureAuthToken { + storeFutureLoginToken(accountManager: accountManager, token: futureAuthToken.makeData()) + } + let user = TelegramUser(user: user) let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil) @@ -856,7 +930,11 @@ public func signUpWithName(accountManager: AccountManager mapToSignal { result -> Signal in switch result { - case let .authorization(_, otherwiseReloginDays, _, user): + case let .authorization(_, otherwiseReloginDays, _, futureAuthToken, user): + if let futureAuthToken = futureAuthToken { + storeFutureLoginToken(accountManager: accountManager, token: futureAuthToken.makeData()) + } + let user = TelegramUser(user: user) let appliedState = account.postbox.transaction { transaction -> Void in let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil) diff --git a/submodules/TelegramCore/Sources/Network/Network.swift b/submodules/TelegramCore/Sources/Network/Network.swift index 166b37c5d4..e0f226d281 100644 --- a/submodules/TelegramCore/Sources/Network/Network.swift +++ b/submodules/TelegramCore/Sources/Network/Network.swift @@ -240,12 +240,14 @@ public struct NetworkUsageStats: Equatable { public let audio: NetworkUsageStatsConnectionsEntry public let file: NetworkUsageStatsConnectionsEntry public let call: NetworkUsageStatsConnectionsEntry + public let sticker: NetworkUsageStatsConnectionsEntry + public let voiceMessage: NetworkUsageStatsConnectionsEntry public let resetWifiTimestamp: Int32 public let resetCellularTimestamp: Int32 public static func ==(lhs: NetworkUsageStats, rhs: NetworkUsageStats) -> Bool { - return lhs.generic == rhs.generic && lhs.image == rhs.image && lhs.video == rhs.video && lhs.audio == rhs.audio && lhs.file == rhs.file && lhs.call == rhs.call && lhs.resetWifiTimestamp == rhs.resetWifiTimestamp && lhs.resetCellularTimestamp == rhs.resetCellularTimestamp + return lhs.generic == rhs.generic && lhs.image == rhs.image && lhs.video == rhs.video && lhs.audio == rhs.audio && lhs.file == rhs.file && lhs.call == rhs.call && lhs.resetWifiTimestamp == rhs.resetWifiTimestamp && lhs.resetCellularTimestamp == rhs.resetCellularTimestamp && lhs.sticker == rhs.sticker && lhs.voiceMessage == rhs.voiceMessage } } @@ -386,6 +388,20 @@ func networkUsageStats(basePath: String, reset: ResetNetworkUsageStats) -> Signa wifi: NetworkUsageStatsDirectionsEntry( incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .call).key]!, outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .call).key]!)), + sticker: NetworkUsageStatsConnectionsEntry( + cellular: NetworkUsageStatsDirectionsEntry( + incoming: 0, + outgoing: 0), + wifi: NetworkUsageStatsDirectionsEntry( + incoming: 0, + outgoing: 0)), + voiceMessage: NetworkUsageStatsConnectionsEntry( + cellular: NetworkUsageStatsDirectionsEntry( + incoming: 0, + outgoing: 0), + wifi: NetworkUsageStatsDirectionsEntry( + incoming: 0, + outgoing: 0)), resetWifiTimestamp: Int32(dict[UsageCalculationResetKey.wifi.rawValue]!), resetCellularTimestamp: Int32(dict[UsageCalculationResetKey.cellular.rawValue]!) )) diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 279a17b8c8..65f96a9d8b 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 151 + return 152 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift index 211bc1eb8c..d7be95d4f2 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift @@ -24,6 +24,12 @@ public struct TelegramChatBannedRightsFlags: OptionSet, Hashable { public static let banAddMembers = TelegramChatBannedRightsFlags(rawValue: 1 << 15) public static let banPinMessages = TelegramChatBannedRightsFlags(rawValue: 1 << 17) public static let banManageTopics = TelegramChatBannedRightsFlags(rawValue: 1 << 18) + public static let banSendPhotos = TelegramChatBannedRightsFlags(rawValue: 1 << 19) + public static let banSendVideos = TelegramChatBannedRightsFlags(rawValue: 1 << 20) + public static let banSendInstantVideos = TelegramChatBannedRightsFlags(rawValue: 1 << 21) + public static let banSendMusic = TelegramChatBannedRightsFlags(rawValue: 1 << 22) + public static let banSendVoice = TelegramChatBannedRightsFlags(rawValue: 1 << 23) + public static let banSendFiles = TelegramChatBannedRightsFlags(rawValue: 1 << 24) } public struct TelegramChatBannedRights: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift index f8e0f5ce7e..4cadf1f71f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift @@ -51,14 +51,16 @@ func _internal_requestChangeAccountPhoneNumberVerification(account: Account, pho return .generic } } - |> map { sentCode -> ChangeAccountPhoneNumberData in + |> mapToSignal { sentCode -> Signal in switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - var parsedNextType: AuthorizationCodeNextType? - if let nextType = nextType { - parsedNextType = AuthorizationCodeNextType(apiType: nextType) - } - return ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType) + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) + } + return .single(ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType)) + case .sentCodeSuccess: + return .never() } } } @@ -76,15 +78,17 @@ func _internal_requestNextChangeAccountPhoneNumberVerification(account: Account, return .generic } } - |> map { sentCode -> ChangeAccountPhoneNumberData in - switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - var parsedNextType: AuthorizationCodeNextType? - if let nextType = nextType { - parsedNextType = AuthorizationCodeNextType(apiType: nextType) - } - return ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType) + |> mapToSignal { sentCode -> Signal in + switch sentCode { + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) } + return .single(ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType)) + case .sentCodeSuccess: + return .never() + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift index 8f07734d2c..b7064dd658 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift @@ -92,7 +92,11 @@ func _internal_exportAuthTransferToken(accountManager: AccountManager Signal in let user = TelegramUser(user: user) let state = AuthorizedAccountState(isTestingEnvironment: updatedAccount.testingEnvironment, masterDatacenterId: updatedAccount.masterDatacenterId, peerId: user.id, state: nil) @@ -116,7 +120,11 @@ func _internal_exportAuthTransferToken(accountManager: AccountManager Signal in let user = TelegramUser(user: user) let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift b/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift index 757644691d..ac0113c958 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift @@ -26,14 +26,16 @@ func _internal_requestCancelAccountResetData(network: Network, hash: String) -> return .generic } } - |> map { sentCode -> CancelAccountResetData in + |> mapToSignal { sentCode -> Signal in switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - var parsedNextType: AuthorizationCodeNextType? - if let nextType = nextType { - parsedNextType = AuthorizationCodeNextType(apiType: nextType) - } - return CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType) + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) + } + return .single(CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType)) + case .sentCodeSuccess: + return .never() } } } @@ -47,14 +49,16 @@ func _internal_requestNextCancelAccountResetOption(network: Network, phoneNumber return .generic } } - |> map { sentCode -> CancelAccountResetData in + |> mapToSignal { sentCode -> Signal in switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - var parsedNextType: AuthorizationCodeNextType? - if let nextType = nextType { - parsedNextType = AuthorizationCodeNextType(apiType: nextType) - } - return CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType) + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) + } + return .single(CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType)) + case .sentCodeSuccess: + return .never() } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift index 362bd1fc28..fa9bef40db 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift @@ -142,6 +142,38 @@ private extension StorageUsageStats { } } +private func statForDirectory(path: String) -> Int64 { + var s = darwin_dirstat() + var result = dirstat_np(path, 1, &s, MemoryLayout.size) + if result != -1 { + return Int64(s.total_size) + } else { + result = dirstat_np(path, 0, &s, MemoryLayout.size) + if result != -1 { + return Int64(s.total_size) + } else { + return 0 + } + } +} + +public func collectRawStorageUsageReport(containerPath: String) -> String { + var log = "" + + let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + let documentsSize = statForDirectory(path: documentsPath) + log.append("Documents (\(documentsPath)): \(documentsSize)\n") + + let systemCachePath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0] + let systemCacheSize = statForDirectory(path: systemCachePath) + log.append("System Cache (\(systemCachePath)): \(systemCacheSize)\n") + + let containerSize = statForDirectory(path: containerPath) + log.append("Container (\(containerPath)): \(containerSize)\n") + + return log +} + func _internal_collectStorageUsageStats(account: Account) -> Signal { /*let additionalStats = Signal { subscriber in DispatchQueue.global().async { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift index 445b5e24e8..7ab42b0ef2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift @@ -26,10 +26,12 @@ public func secureIdPreparePhoneVerification(network: Network, value: SecureIdPh } return .generic } - |> map { sentCode -> SecureIdPreparePhoneVerificationPayload in + |> mapToSignal { sentCode -> Signal in switch sentCode { - case let .sentCode(_, type, phoneCodeHash, nextType, timeout): - return SecureIdPreparePhoneVerificationPayload(type: SentAuthorizationCodeType(apiType: type), nextType: nextType.flatMap(AuthorizationCodeNextType.init), timeout: timeout, phone: value.phone, phoneCodeHash: phoneCodeHash) + case let .sentCode(_, type, phoneCodeHash, nextType, timeout): + return .single(SecureIdPreparePhoneVerificationPayload(type: SentAuthorizationCodeType(apiType: type), nextType: nextType.flatMap(AuthorizationCodeNextType.init), timeout: timeout, phone: value.phone, phoneCodeHash: phoneCodeHash)) + case .sentCodeSuccess: + return .never() } } } diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 77e164e118..fb85fd20e7 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -100,7 +100,7 @@ public final class ChatControllerInteraction { public let sendCurrentMessage: (Bool) -> Void public let sendMessage: (String) -> Void public let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool - public let sendEmoji: (String, ChatTextInputTextCustomEmojiAttribute) -> Void + public let sendEmoji: (String, ChatTextInputTextCustomEmojiAttribute, Bool) -> Void public let sendGif: (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool public let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool public let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void @@ -210,7 +210,7 @@ public final class ChatControllerInteraction { sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool, - sendEmoji: @escaping (String, ChatTextInputTextCustomEmojiAttribute) -> Void, + sendEmoji: @escaping (String, ChatTextInputTextCustomEmojiAttribute, Bool) -> Void, sendGif: @escaping (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index d0258d654b..2100af4533 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -29,6 +29,7 @@ import TelegramUIPreferences import MultiplexedVideoNode import ChatControllerInteraction import FeaturedStickersScreen +import Pasteboard public struct ChatMediaInputPaneScrollState { let absoluteOffset: CGFloat? @@ -533,6 +534,87 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { self.externalTopPanelContainerImpl = PagerExternalTopPanelContainer() + var stickerPeekBehavior: EmojiContentPeekBehaviorImpl? + if let controllerInteraction = controllerInteraction { + let context = self.context + + stickerPeekBehavior = EmojiContentPeekBehaviorImpl( + context: self.context, + interaction: EmojiContentPeekBehaviorImpl.Interaction( + sendSticker: controllerInteraction.sendSticker, + sendEmoji: { file in + var text = "." + var emojiAttribute: ChatTextInputTextCustomEmojiAttribute? + loop: for attribute in file.attributes { + switch attribute { + case let .CustomEmoji(_, _, displayText, stickerPackReference): + text = displayText + + var packId: ItemCollectionId? + if case let .id(id, _) = stickerPackReference { + packId = ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id) + } + emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: packId, fileId: file.fileId.id, file: file) + break loop + default: + break + } + } + + if let emojiAttribute { + controllerInteraction.sendEmoji(text, emojiAttribute, true) + } + }, + setStatus: { [weak self] file in + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.accountData.setEmojiStatus(file: file, expirationDate: nil).start() + + var animateInAsReplacement = false + if let currentUndoOverlayController = strongSelf.currentUndoOverlayController { + currentUndoOverlayController.dismissWithCommitActionAndReplacementAnimation() + strongSelf.currentUndoOverlayController = nil + animateInAsReplacement = true + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + //TODO:localize + let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: "Your emoji status has been updated.", undoText: nil, customAction: nil), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return false }) + strongSelf.currentUndoOverlayController = controller + controllerInteraction.presentController(controller, nil) + }, + copyEmoji: { file in + var text = "." + var emojiAttribute: ChatTextInputTextCustomEmojiAttribute? + loop: for attribute in file.attributes { + switch attribute { + case let .CustomEmoji(_, _, displayText, _): + text = displayText + + emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file) + break loop + default: + break + } + } + + if let _ = emojiAttribute { + storeMessageTextInPasteboard(text, entities: [MessageTextEntity(range: 0 ..< (text as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: file.fileId.id))]) + } + }, + presentController: controllerInteraction.presentController, + presentGlobalOverlayController: controllerInteraction.presentGlobalOverlayController, + navigationController: controllerInteraction.navigationController + ), + chatPeerId: chatPeerId, + present: { c, a in + controllerInteraction.presentGlobalOverlayController(c, a) + } + ) + } + var premiumToastCounter = 0 self.emojiInputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { [weak self, weak interfaceInteraction, weak controllerInteraction] groupId, item, _, _, _, _ in @@ -879,7 +961,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { updateScrollingToItemGroup: { }, chatPeerId: chatPeerId, - peekBehavior: nil, + peekBehavior: stickerPeekBehavior, customLayout: nil, externalBackground: nil, externalExpansionView: nil, @@ -887,17 +969,6 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { hideBackground: false ) - var stickerPeekBehavior: EmojiContentPeekBehaviorImpl? - if let controllerInteraction = controllerInteraction { - stickerPeekBehavior = EmojiContentPeekBehaviorImpl( - context: self.context, - interaction: EmojiContentPeekBehaviorImpl.Interaction(sendSticker: controllerInteraction.sendSticker, presentController: controllerInteraction.presentController, presentGlobalOverlayController: controllerInteraction.presentGlobalOverlayController, navigationController: controllerInteraction.navigationController), - chatPeerId: chatPeerId, - present: { c, a in - controllerInteraction.presentGlobalOverlayController(c, a) - } - ) - } self.stickerInputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { [weak controllerInteraction, weak interfaceInteraction] groupId, item, view, rect, layer, _ in let _ = (ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false) |> take(1) |> deliverOnMainQueue).start(next: { hasPremium in @@ -1988,24 +2059,40 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior { public class Interaction { public let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool + public let sendEmoji: (TelegramMediaFile) -> Void + public let setStatus: (TelegramMediaFile) -> Void + public let copyEmoji: (TelegramMediaFile) -> Void public let presentController: (ViewController, Any?) -> Void public let presentGlobalOverlayController: (ViewController, Any?) -> Void public let navigationController: () -> NavigationController? - public init(sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool, presentController: @escaping (ViewController, Any?) -> Void, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?) { + public init(sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool, sendEmoji: @escaping (TelegramMediaFile) -> Void, setStatus: @escaping (TelegramMediaFile) -> Void, copyEmoji: @escaping (TelegramMediaFile) -> Void, presentController: @escaping (ViewController, Any?) -> Void, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?) { self.sendSticker = sendSticker + self.sendEmoji = sendEmoji + self.setStatus = setStatus + self.copyEmoji = copyEmoji self.presentController = presentController self.presentGlobalOverlayController = presentGlobalOverlayController self.navigationController = navigationController } } + private final class ViewRecord { + weak var view: UIView? + let peekRecognizer: PeekControllerGestureRecognizer + + init(view: UIView, peekRecognizer: PeekControllerGestureRecognizer) { + self.view = view + self.peekRecognizer = peekRecognizer + } + } + private let context: AccountContext private let interaction: Interaction? private let chatPeerId: EnginePeer.Id? private let present: (ViewController, Any?) -> Void - private var peekRecognizer: PeekControllerGestureRecognizer? + private var viewRecords: [ViewRecord] = [] private weak var peekController: PeekController? public init(context: AccountContext, interaction: Interaction?, chatPeerId: EnginePeer.Id?, present: @escaping (ViewController, Any?) -> Void) { @@ -2016,7 +2103,13 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior { } public func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?) { - if self.peekRecognizer == nil { + self.viewRecords = self.viewRecords.filter({ $0.view != nil }) + + let viewRecord = self.viewRecords.first(where: { $0.view === view }) + + if let viewRecord = viewRecord { + viewRecord.peekRecognizer.isEnabled = isEnabled + } else { let peekRecognizer = PeekControllerGestureRecognizer(contentAtPoint: { [weak self, weak view] point in guard let strongSelf = self else { return nil @@ -2032,133 +2125,242 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior { let context = strongSelf.context let accountPeerId = context.account.peerId - return combineLatest( - context.engine.stickers.isStickerSaved(id: file.fileId), - context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId)) |> map { peer -> Bool in + let chatPeerId = strongSelf.chatPeerId + + if file.isCustomEmoji { + return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId)) |> map { peer -> Bool in var hasPremium = false if case let .user(user) = peer, user.isPremium { hasPremium = true } return hasPremium } - ) - |> deliverOnMainQueue - |> map { [weak itemLayer] isStarred, hasPremium -> (UIView, CGRect, PeekControllerContent)? in - guard let strongSelf = self, let itemLayer = itemLayer else { - return nil - } - var menuItems: [ContextMenuItem] = [] - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let isLocked = file.isPremiumSticker && !hasPremium - - if let interaction = strongSelf.interaction { - let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Void = { fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer in - let _ = interaction.sendSticker(fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets) + |> deliverOnMainQueue + |> map { [weak itemLayer] hasPremium -> (UIView, CGRect, PeekControllerContent)? in + guard let strongSelf = self, let itemLayer = itemLayer else { + return nil } - - if let chatPeerId = strongSelf.chatPeerId, !isLocked { - if chatPeerId != strongSelf.context.account.peerId && chatPeerId.namespace != Namespaces.Peer.SecretChat { - menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor) + + var menuItems: [ContextMenuItem] = [] + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + var isLocked = false + if !hasPremium { + isLocked = file.isPremiumEmoji + if isLocked && chatPeerId == context.account.peerId { + isLocked = false + } + } + + if let interaction = strongSelf.interaction { + let sendEmoji: (TelegramMediaFile) -> Void = { file in + interaction.sendEmoji(file) + } + let setStatus: (TelegramMediaFile) -> Void = { file in + interaction.setStatus(file) + } + let copyEmoji: (TelegramMediaFile) -> Void = { file in + interaction.copyEmoji(file) + } + + if let _ = strongSelf.chatPeerId, !isLocked { + //TODO:localize + menuItems.append(.action(ContextMenuActionItem(text: "Send Emoji", icon: { theme in + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) { + return generateImage(image.size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + if let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size)) + } + }) + } else { + return nil + } + }, action: { _, f in + sendEmoji(file) + f(.default) + }))) + + //TODO:localize + menuItems.append(.action(ContextMenuActionItem(text: "Set as Status", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Smile"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.default) + + guard let strongSelf = self else { + return + } + + if hasPremium { + setStatus(file) + } else { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .animatedEmoji, action: { + let controller = PremiumIntroScreen(context: context, source: .animatedEmoji) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + strongSelf.interaction?.navigationController()?.pushViewController(controller) + } + }))) + + //TODO:localize + menuItems.append(.action(ContextMenuActionItem(text: "Copy Emoji", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + copyEmoji(file) + f(.default) + }))) + } + } + + if menuItems.isEmpty { + return nil + } + guard let view = view else { + return nil + } + + return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: { + guard let strongSelf = self, let interaction = strongSelf.interaction else { + return + } + let controller = PremiumIntroScreen(context: context, source: .stickers) + interaction.navigationController()?.pushViewController(controller) + })) + } + } else { + return combineLatest( + context.engine.stickers.isStickerSaved(id: file.fileId), + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId)) |> map { peer -> Bool in + var hasPremium = false + if case let .user(user) = peer, user.isPremium { + hasPremium = true + } + return hasPremium + } + ) + |> deliverOnMainQueue + |> map { [weak itemLayer] isStarred, hasPremium -> (UIView, CGRect, PeekControllerContent)? in + guard let strongSelf = self, let itemLayer = itemLayer else { + return nil + } + var menuItems: [ContextMenuItem] = [] + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let isLocked = file.isPremiumSticker && !hasPremium + + if let interaction = strongSelf.interaction { + let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Void = { fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer in + let _ = interaction.sendSticker(fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets) + } + + if let chatPeerId = strongSelf.chatPeerId, !isLocked { + if chatPeerId != strongSelf.context.account.peerId && chatPeerId.namespace != Namespaces.Peer.SecretChat { + menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + if let strongSelf = self, let peekController = strongSelf.peekController { + if let animationNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.animationNode { + sendSticker(.standalone(media: file), true, false, nil, false, animationNode.view, animationNode.bounds, nil) + } else if let imageNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.imageNode { + sendSticker(.standalone(media: file), true, false, nil, false, imageNode.view, imageNode.bounds, nil) + } + } + f(.default) + }))) + } + + menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in if let strongSelf = self, let peekController = strongSelf.peekController { if let animationNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.animationNode { - sendSticker(.standalone(media: file), true, false, nil, false, animationNode.view, animationNode.bounds, nil) + let _ = sendSticker(.standalone(media: file), false, true, nil, false, animationNode.view, animationNode.bounds, nil) } else if let imageNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.imageNode { - sendSticker(.standalone(media: file), true, false, nil, false, imageNode.view, imageNode.bounds, nil) + let _ = sendSticker(.standalone(media: file), false, true, nil, false, imageNode.view, imageNode.bounds, nil) } } f(.default) }))) } - menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - if let strongSelf = self, let peekController = strongSelf.peekController { - if let animationNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.animationNode { - let _ = sendSticker(.standalone(media: file), false, true, nil, false, animationNode.view, animationNode.bounds, nil) - } else if let imageNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.imageNode { - let _ = sendSticker(.standalone(media: file), false, true, nil, false, imageNode.view, imageNode.bounds, nil) - } - } - f(.default) - }))) - } - - menuItems.append( - .action(ContextMenuActionItem(text: isStarred ? presentationData.strings.Stickers_RemoveFromFavorites : presentationData.strings.Stickers_AddToFavorites, icon: { theme in generateTintedImage(image: isStarred ? UIImage(bundleImageName: "Chat/Context Menu/Unfave") : UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { _, f in - f(.default) - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let _ = (context.engine.stickers.toggleStickerSaved(file: file, saved: !isStarred) - |> deliverOnMainQueue).start(next: { result in - switch result { - case .generic: - interaction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: !isStarred ? presentationData.strings.Conversation_StickerAddedToFavorites : presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), nil) - case let .limitExceeded(limit, premiumLimit): - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - let text: String - if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { - text = presentationData.strings.Premium_MaxFavedStickersFinalText - } else { - text = presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string - } - interaction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil, customAction: nil), elevatedLayout: false, action: { action in - if case .info = action { - let controller = PremiumIntroScreen(context: context, source: .savedStickers) - interaction.navigationController()?.pushViewController(controller) - return true + menuItems.append( + .action(ContextMenuActionItem(text: isStarred ? presentationData.strings.Stickers_RemoveFromFavorites : presentationData.strings.Stickers_AddToFavorites, icon: { theme in generateTintedImage(image: isStarred ? UIImage(bundleImageName: "Chat/Context Menu/Unfave") : UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { _, f in + f(.default) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let _ = (context.engine.stickers.toggleStickerSaved(file: file, saved: !isStarred) + |> deliverOnMainQueue).start(next: { result in + switch result { + case .generic: + interaction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: !isStarred ? presentationData.strings.Conversation_StickerAddedToFavorites : presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), nil) + case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let text: String + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { + text = presentationData.strings.Premium_MaxFavedStickersFinalText + } else { + text = presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string } - return false - }), nil) + interaction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil, customAction: nil), elevatedLayout: false, action: { action in + if case .info = action { + let controller = PremiumIntroScreen(context: context, source: .savedStickers) + interaction.navigationController()?.pushViewController(controller) + return true + } + return false + }), nil) + } + }) + })) + ) + menuItems.append( + .action(ContextMenuActionItem(text: presentationData.strings.StickerPack_ViewPack, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Sticker"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.default) + + guard let strongSelf = self else { + return } - }) - })) - ) - menuItems.append( - .action(ContextMenuActionItem(text: presentationData.strings.StickerPack_ViewPack, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Sticker"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - f(.default) - - guard let strongSelf = self else { - return - } - - loop: for attribute in file.attributes { - switch attribute { - case let .CustomEmoji(_, _, _, packReference), let .Sticker(_, packReference, _): - if let packReference = packReference { - let controller = strongSelf.context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: interaction.navigationController(), sendSticker: { file, sourceView, sourceRect in - sendSticker(file, false, false, nil, false, sourceView, sourceRect, nil) - return true - }) - - interaction.navigationController()?.view.window?.endEditing(true) - interaction.presentController(controller, nil) + + loop: for attribute in file.attributes { + switch attribute { + case let .CustomEmoji(_, _, _, packReference), let .Sticker(_, packReference, _): + if let packReference = packReference { + let controller = strongSelf.context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: interaction.navigationController(), sendSticker: { file, sourceView, sourceRect in + sendSticker(file, false, false, nil, false, sourceView, sourceRect, nil) + return true + }) + + interaction.navigationController()?.view.window?.endEditing(true) + interaction.presentController(controller, nil) + } + break loop + default: + break } - break loop - default: - break } - } - })) - ) - } - - guard let view = view else { - return nil - } - - return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked && !isStarred, menu: menuItems, openPremiumIntro: { - guard let strongSelf = self, let interaction = strongSelf.interaction else { - return + })) + ) } - let controller = PremiumIntroScreen(context: context, source: .stickers) - interaction.navigationController()?.pushViewController(controller) - })) + + guard let view = view else { + return nil + } + + return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked && !isStarred, menu: menuItems, openPremiumIntro: { + guard let strongSelf = self, let interaction = strongSelf.interaction else { + return + } + let controller = PremiumIntroScreen(context: context, source: .stickers) + interaction.navigationController()?.pushViewController(controller) + })) + } } }, present: { [weak self] content, sourceView, sourceRect in guard let strongSelf = self else { @@ -2184,11 +2386,9 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior { let _ = strongSelf }) - self.peekRecognizer = peekRecognizer + self.viewRecords.append(ViewRecord(view: view, peekRecognizer: peekRecognizer)) view.addGestureRecognizer(peekRecognizer) peekRecognizer.isEnabled = isEnabled - } else { - self.peekRecognizer?.isEnabled = isEnabled } } } diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusPreviewScreen.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusPreviewScreen.swift index b8505beb86..87fc6550ae 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusPreviewScreen.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusPreviewScreen.swift @@ -672,6 +672,7 @@ final class EmojiStatusPreviewScreenComponent: Component { } )))) } + //TODO:localize menuItems.append(AnyComponentWithIdentity(id: "Other", component: AnyComponent(ContextMenuActionItem( title: component.strings.EmojiStatusSetup_TimerOther, action: { [weak self] in diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 979ed7d5fc..9f096552d8 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -5766,7 +5766,7 @@ public final class EmojiPagerContentComponent: Component { self.component = component self.state = state - component.inputInteractionHolder.inputInteraction?.peekBehavior?.setGestureRecognizerEnabled(view: self, isEnabled: component.itemLayoutType == .detailed, itemAtPoint: { [weak self] point in + component.inputInteractionHolder.inputInteraction?.peekBehavior?.setGestureRecognizerEnabled(view: self, isEnabled: true, itemAtPoint: { [weak self] point in guard let strongSelf = self else { return nil } diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD index a801b5c688..abb9847aa3 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD +++ b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD @@ -40,6 +40,7 @@ swift_library( "//submodules/TelegramAnimatedStickerNode", "//submodules/LegacyComponents", "//submodules/GalleryData", + "//submodules/SegmentedControlNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataButtonComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataButtonComponent.swift new file mode 100644 index 0000000000..d29ba7aa1f --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataButtonComponent.swift @@ -0,0 +1,140 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import TelegramStringFormatting +import CheckNode + +final class DataButtonComponent: Component { + let theme: PresentationTheme + let title: String + let action: () -> Void + + init( + theme: PresentationTheme, + title: String, + action: @escaping () -> Void + ) { + self.theme = theme + self.title = title + self.action = action + } + + static func ==(lhs: DataButtonComponent, rhs: DataButtonComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.title != rhs.title { + return false + } + return true + } + + class View: HighlightTrackingButton { + private let title = ComponentView() + + private var component: DataButtonComponent? + + private var highlightBackgroundFrame: CGRect? + private var highlightBackgroundLayer: SimpleLayer? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.clipsToBounds = true + self.layer.cornerRadius = 10.0 + + self.highligthedChanged = { [weak self] isHighlighted in + guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else { + return + } + + if isHighlighted { + self.superview?.bringSubviewToFront(self) + + let highlightBackgroundLayer: SimpleLayer + if let current = self.highlightBackgroundLayer { + highlightBackgroundLayer = current + } else { + highlightBackgroundLayer = SimpleLayer() + self.highlightBackgroundLayer = highlightBackgroundLayer + self.layer.insertSublayer(highlightBackgroundLayer, at: 0) + highlightBackgroundLayer.backgroundColor = component.theme.list.itemHighlightedBackgroundColor.cgColor + } + highlightBackgroundLayer.frame = highlightBackgroundFrame + highlightBackgroundLayer.opacity = 1.0 + } else { + if let highlightBackgroundLayer = self.highlightBackgroundLayer { + self.highlightBackgroundLayer = nil + highlightBackgroundLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak highlightBackgroundLayer] _ in + highlightBackgroundLayer?.removeFromSuperlayer() + }) + } + } + } + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + guard let component = self.component else { + return + } + component.action() + } + + func update(component: DataButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + + if themeUpdated { + self.backgroundColor = component.theme.list.itemBlocksBackgroundColor + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: component.title, font: Font.regular(17.0), textColor: component.theme.list.itemDestructiveColor)))), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 100.0) + ) + + let height: CGFloat = 44.0 + + let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: floor((height - titleSize.height) / 2.0)), size: titleSize) + + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + self.highlightBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height)) + + return CGSize(width: availableSize.width, height: height) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoriesComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoriesComponent.swift new file mode 100644 index 0000000000..150ac91f3b --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoriesComponent.swift @@ -0,0 +1,192 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import CheckNode +import SolidRoundedButtonComponent + +final class DataCategoriesComponent: Component { + struct CategoryData: Equatable { + var key: DataUsageScreenComponent.Category + var color: UIColor + var title: String + var size: Int64 + var sizeFraction: Double + var incoming: Int64 + var outgoing: Int64 + var isSeparable: Bool + var isExpanded: Bool + + init(key: DataUsageScreenComponent.Category, color: UIColor, title: String, size: Int64, sizeFraction: Double, incoming: Int64, outgoing: Int64, isSeparable: Bool, isExpanded: Bool) { + self.key = key + self.title = title + self.color = color + self.size = size + self.sizeFraction = sizeFraction + self.incoming = incoming + self.outgoing = outgoing + self.isSeparable = isSeparable + self.isExpanded = isExpanded + } + } + + let theme: PresentationTheme + let strings: PresentationStrings + let categories: [CategoryData] + let toggleCategoryExpanded: (DataUsageScreenComponent.Category) -> Void + + init( + theme: PresentationTheme, + strings: PresentationStrings, + categories: [CategoryData], + toggleCategoryExpanded: @escaping (DataUsageScreenComponent.Category) -> Void + ) { + self.theme = theme + self.strings = strings + self.categories = categories + self.toggleCategoryExpanded = toggleCategoryExpanded + } + + static func ==(lhs: DataCategoriesComponent, rhs: DataCategoriesComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.categories != rhs.categories { + return false + } + return true + } + + class View: UIView { + private let containerView: UIView + private var itemViews: [DataUsageScreenComponent.Category: ComponentView] = [:] + + private var component: DataCategoriesComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.containerView = UIView() + + super.init(frame: frame) + + self.clipsToBounds = true + self.layer.cornerRadius = 10.0 + + self.addSubview(self.containerView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: DataCategoriesComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + + var itemsTransition = transition + if let animationHint = transition.userData(DataUsageScreenComponent.AnimationHint.self) { + switch animationHint.value { + case .clearedItems, .modeChanged: + if let copyView = self.containerView.snapshotView(afterScreenUpdates: false) { + itemsTransition = .immediate + + self.addSubview(copyView) + self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16) + copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyView] _ in + copyView?.removeFromSuperview() + }) + } + } + } + + var contentHeight: CGFloat = 0.0 + + var validKeys = Set() + for i in 0 ..< component.categories.count { + let category = component.categories[i] + validKeys.insert(category.key) + + var itemTransition = itemsTransition + let itemView: ComponentView + if let current = self.itemViews[category.key] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + itemView.parentState = state + self.itemViews[category.key] = itemView + } + + let itemSize = itemView.update( + transition: itemTransition, + component: AnyComponent(DataCategoryItemComponent( + theme: component.theme, + strings: component.strings, + category: category, + isExpanded: category.isExpanded, + hasNext: i != component.categories.count - 1, + action: { [weak self] key in + guard let self, let component = self.component else { + return + } + component.toggleCategoryExpanded(key) + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 1000.0) + ) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: itemSize) + if let itemComponentView = itemView.view { + if itemComponentView.superview == nil { + self.containerView.addSubview(itemComponentView) + } + itemTransition.setFrame(view: itemComponentView, frame: itemFrame) + } + + contentHeight += itemSize.height + } + + var removeKeys: [DataUsageScreenComponent.Category] = [] + for (key, itemView) in self.itemViews { + if !validKeys.contains(key) { + if let itemComponentView = itemView.view { + transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in + itemComponentView?.removeFromSuperview() + }) + } + removeKeys.append(key) + } + } + for key in removeKeys { + self.itemViews.removeValue(forKey: key) + } + + self.backgroundColor = component.theme.list.itemBlocksBackgroundColor + + self.containerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: contentHeight)) + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoryItemCompoment.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoryItemCompoment.swift new file mode 100644 index 0000000000..52cdcf9c91 --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataCategoryItemCompoment.swift @@ -0,0 +1,611 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import TelegramStringFormatting +import CheckNode + +private final class SubItemComponent: Component { + let theme: PresentationTheme + let strings: PresentationStrings + let isIncoming: Bool + let value: Int64 + let hasNext: Bool + + init( + theme: PresentationTheme, + strings: PresentationStrings, + isIncoming: Bool, + value: Int64, + hasNext: Bool + ) { + self.theme = theme + self.strings = strings + self.isIncoming = isIncoming + self.value = value + self.hasNext = hasNext + } + + static func ==(lhs: SubItemComponent, rhs: SubItemComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.isIncoming != rhs.isIncoming { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + class View: HighlightTrackingButton { + private let iconView: UIImageView + private let title = ComponentView() + private let titleValue = ComponentView() + private let label = ComponentView() + private let separatorLayer: SimpleLayer + + private var component: SubItemComponent? + + private var highlightBackgroundFrame: CGRect? + private var highlightBackgroundLayer: SimpleLayer? + + override init(frame: CGRect) { + self.iconView = UIImageView() + self.separatorLayer = SimpleLayer() + + super.init(frame: frame) + + self.layer.addSublayer(self.separatorLayer) + + self.addSubview(self.iconView) + + self.highligthedChanged = { [weak self] isHighlighted in + guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else { + return + } + + if isHighlighted { + self.superview?.bringSubviewToFront(self) + + let highlightBackgroundLayer: SimpleLayer + if let current = self.highlightBackgroundLayer { + highlightBackgroundLayer = current + } else { + highlightBackgroundLayer = SimpleLayer() + self.highlightBackgroundLayer = highlightBackgroundLayer + self.layer.insertSublayer(highlightBackgroundLayer, above: self.separatorLayer) + highlightBackgroundLayer.backgroundColor = component.theme.list.itemHighlightedBackgroundColor.cgColor + } + highlightBackgroundLayer.frame = highlightBackgroundFrame + highlightBackgroundLayer.opacity = 1.0 + } else { + if let highlightBackgroundLayer = self.highlightBackgroundLayer { + self.highlightBackgroundLayer = nil + highlightBackgroundLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak highlightBackgroundLayer] _ in + highlightBackgroundLayer?.removeFromSuperlayer() + }) + } + } + } + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.isUserInteractionEnabled = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + return result + } + + func update(component: SubItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme || self.component?.isIncoming != component.isIncoming + + self.component = component + + if themeUpdated { + self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: component.isIncoming ? "Settings/Menu/DataExpandedIn" : "Settings/Menu/DataExpandedOut"), color: component.theme.list.itemPrimaryTextColor) + } + + var leftInset: CGFloat = 62.0 + var additionalLeftInset: CGFloat = 0.0 + + additionalLeftInset += 45.0 + leftInset += additionalLeftInset + + let rightInset: CGFloat = 16.0 + + var availableWidth: CGFloat = availableSize.width - leftInset - rightInset + + let fractionString: String = "" + /*if component.category.sizeFraction != 0.0 { + let fractionValue: Double = floor(component.category.sizeFraction * 100.0 * 10.0) / 10.0 + if fractionValue < 0.1 { + fractionString = "<0.1%" + } else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 { + fractionString = "\(Int(fractionValue))%" + } else { + fractionString = "\(fractionValue)%" + } + } else { + fractionString = "" + }*/ + + let labelSize = self.label.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: dataSizeString(Int(component.value), formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: ".")), font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + availableWidth = max(1.0, availableWidth - labelSize.width - 1.0) + + let titleValueSize = self.titleValue.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: fractionString, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + availableWidth = max(1.0, availableWidth - titleValueSize.width - 4.0) + + //TODO:localize + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: component.isIncoming ? "Incoming" : "Outgoing", font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + + let height: CGFloat = 44.0 + + let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize) + let titleValueFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floor((height - titleValueSize.height) / 2.0)), size: titleValueSize) + + let labelFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - labelSize.width, y: floor((height - labelSize.height) / 2.0)), size: labelSize) + + if let image = self.iconView.image { + transition.setFrame(view: self.iconView, frame: CGRect(origin: CGPoint(x: leftInset - additionalLeftInset + floor((additionalLeftInset - image.size.width) * 0.5), y: floor((height - image.size.height) * 0.5)), size: image.size)) + } + + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + if let titleValueView = self.titleValue.view { + if titleValueView.superview == nil { + titleValueView.isUserInteractionEnabled = false + self.addSubview(titleValueView) + } + + if titleValueView.bounds.size != titleValueFrame.size { + titleValueView.frame = titleValueFrame + } else { + transition.setFrame(view: titleValueView, frame: titleValueFrame) + } + } + if let labelView = self.label.view { + if labelView.superview == nil { + labelView.isUserInteractionEnabled = false + self.addSubview(labelView) + } + transition.setFrame(view: labelView, frame: labelFrame) + } + + if themeUpdated { + self.separatorLayer.backgroundColor = component.theme.list.itemBlocksSeparatorColor.cgColor + } + transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel))) + + transition.setAlpha(layer: self.separatorLayer, alpha: (component.hasNext) ? 1.0 : 0.0) + + self.highlightBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height + ((component.hasNext) ? UIScreenPixel : 0.0))) + + return CGSize(width: availableSize.width, height: height) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +final class DataCategoryItemComponent: Component { + let theme: PresentationTheme + let strings: PresentationStrings + let category: DataCategoriesComponent.CategoryData + let isExpanded: Bool + let hasNext: Bool + let action: (DataUsageScreenComponent.Category) -> Void + + init( + theme: PresentationTheme, + strings: PresentationStrings, + category: DataCategoriesComponent.CategoryData, + isExpanded: Bool, + hasNext: Bool, + action: @escaping (DataUsageScreenComponent.Category) -> Void + ) { + self.theme = theme + self.strings = strings + self.category = category + self.isExpanded = isExpanded + self.hasNext = hasNext + self.action = action + } + + static func ==(lhs: DataCategoryItemComponent, rhs: DataCategoryItemComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.category != rhs.category { + return false + } + if lhs.isExpanded != rhs.isExpanded { + return false + } + if lhs.hasNext != rhs.hasNext { + return false + } + return true + } + + class View: HighlightTrackingButton { + private let iconView: UIImageView + private var expandIconView: UIImageView? + private let title = ComponentView() + private let titleValue = ComponentView() + private let label = ComponentView() + private let separatorLayer: SimpleLayer + + private let subcategoryClippingContainer: UIView + private var itemViews: [AnyHashable: ComponentView] = [:] + + private var component: DataCategoryItemComponent? + + private var highlightBackgroundFrame: CGRect? + private var highlightBackgroundLayer: SimpleLayer? + + override init(frame: CGRect) { + self.iconView = UIImageView() + self.separatorLayer = SimpleLayer() + + self.subcategoryClippingContainer = UIView() + self.subcategoryClippingContainer.clipsToBounds = true + + super.init(frame: frame) + + self.addSubview(self.subcategoryClippingContainer) + + self.layer.addSublayer(self.separatorLayer) + + self.addSubview(self.iconView) + + self.highligthedChanged = { [weak self] isHighlighted in + guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else { + return + } + + if isHighlighted { + self.superview?.bringSubviewToFront(self) + + let highlightBackgroundLayer: SimpleLayer + if let current = self.highlightBackgroundLayer { + highlightBackgroundLayer = current + } else { + highlightBackgroundLayer = SimpleLayer() + self.highlightBackgroundLayer = highlightBackgroundLayer + self.layer.insertSublayer(highlightBackgroundLayer, above: self.separatorLayer) + highlightBackgroundLayer.backgroundColor = component.theme.list.itemHighlightedBackgroundColor.cgColor + } + highlightBackgroundLayer.frame = highlightBackgroundFrame + highlightBackgroundLayer.opacity = 1.0 + } else { + if let highlightBackgroundLayer = self.highlightBackgroundLayer { + self.highlightBackgroundLayer = nil + highlightBackgroundLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak highlightBackgroundLayer] _ in + highlightBackgroundLayer?.removeFromSuperlayer() + }) + } + } + } + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + guard let component = self.component else { + return + } + component.action(component.category.key) + } + + @objc private func checkPressed() { + guard let component = self.component else { + return + } + component.action(component.category.key) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + if result === self.subcategoryClippingContainer { + return self + } + return result + } + + func update(component: DataCategoryItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme || self.component?.category.color != component.category.color + + self.component = component + + + if themeUpdated { + let imageName: String + switch component.category.key { + case .photos: + imageName = "Settings/Menu/DataPhotos" + case .videos: + imageName = "Settings/Menu/DataVideo" + case .files: + imageName = "Settings/Menu/DataFiles" + case .music: + imageName = "Settings/Menu/DataMusic" + case .messages: + imageName = "Settings/Menu/DataMessages" + case .stickers: + imageName = "Settings/Menu/DataStickers" + case .voiceMessages: + imageName = "Settings/Menu/DataVoice" + case .calls: + imageName = "Settings/Menu/DataCalls" + case .totalIn: + imageName = "Settings/Menu/DataIn" + case .totalOut: + imageName = "Settings/Menu/DataOut" + } + self.iconView.image = UIImage(bundleImageName: imageName) + } + + var leftInset: CGFloat = 62.0 + let additionalLeftInset: CGFloat = 0.0 + leftInset += additionalLeftInset + + let rightInset: CGFloat = 16.0 + + var availableWidth: CGFloat = availableSize.width - leftInset - rightInset + + if component.category.isSeparable { + let expandIconView: UIImageView + if let current = self.expandIconView { + expandIconView = current + if themeUpdated { + expandIconView.image = PresentationResourcesItemList.disclosureArrowImage(component.theme) + } + } else { + expandIconView = UIImageView() + expandIconView.image = PresentationResourcesItemList.disclosureArrowImage(component.theme) + self.expandIconView = expandIconView + self.addSubview(expandIconView) + } + + if let image = expandIconView.image { + availableWidth -= image.size.width + 6.0 + transition.setBounds(view: expandIconView, bounds: CGRect(origin: CGPoint(), size: image.size)) + } + } else if let expandIconView = self.expandIconView { + self.expandIconView = nil + expandIconView.removeFromSuperview() + } + + let fractionString: String + if component.category.sizeFraction != 0.0 { + let fractionValue: Double = floor(component.category.sizeFraction * 100.0 * 10.0) / 10.0 + if fractionValue < 0.1 { + fractionString = "<0.1%" + } else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 { + fractionString = "\(Int(fractionValue))%" + } else { + fractionString = "\(fractionValue)%" + } + } else { + fractionString = "" + } + + let labelSize = self.label.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: dataSizeString(Int(component.category.size), formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: ".")), font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + availableWidth = max(1.0, availableWidth - labelSize.width - 1.0) + + let titleValueSize = self.titleValue.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: fractionString, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + availableWidth = max(1.0, availableWidth - titleValueSize.width - 4.0) + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: component.category.title, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor)))), + environment: {}, + containerSize: CGSize(width: availableWidth, height: 100.0) + ) + + var height: CGFloat = 44.0 + + let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize) + let titleValueFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floor((height - titleValueSize.height) / 2.0)), size: titleValueSize) + + var labelFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - labelSize.width, y: floor((height - labelSize.height) / 2.0)), size: labelSize) + + if let expandIconView = self.expandIconView, let image = expandIconView.image { + labelFrame.origin.x -= image.size.width - 6.0 + + transition.setPosition(view: expandIconView, position: CGPoint(x: availableSize.width - rightInset + 6.0 - floor(image.size.width * 0.5), y: floor(height * 0.5))) + let angle: CGFloat = component.isExpanded ? CGFloat.pi : 0.0 + transition.setTransform(view: expandIconView, transform: CATransform3DMakeRotation(CGFloat.pi * 0.5 - angle, 0.0, 0.0, 1.0)) + } + + if let image = self.iconView.image { + transition.setFrame(view: self.iconView, frame: CGRect(origin: CGPoint(x: floor((leftInset - image.size.width) * 0.5), y: floor((height - image.size.height) * 0.5)), size: image.size)) + } + + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + if let titleValueView = self.titleValue.view { + if titleValueView.superview == nil { + titleValueView.isUserInteractionEnabled = false + self.addSubview(titleValueView) + } + + if titleValueView.bounds.size != titleValueFrame.size { + titleValueView.frame = titleValueFrame + } else { + transition.setFrame(view: titleValueView, frame: titleValueFrame) + } + } + if let labelView = self.label.view { + if labelView.superview == nil { + labelView.isUserInteractionEnabled = false + self.addSubview(labelView) + } + transition.setFrame(view: labelView, frame: labelFrame) + } + + if themeUpdated { + self.separatorLayer.backgroundColor = component.theme.list.itemBlocksSeparatorColor.cgColor + } + transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel))) + + transition.setAlpha(layer: self.separatorLayer, alpha: (component.isExpanded || component.hasNext) ? 1.0 : 0.0) + + self.highlightBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height + ((component.isExpanded || component.hasNext) ? UIScreenPixel : 0.0))) + + var validKeys = Set() + if component.isExpanded, component.category.isSeparable { + struct SubItem { + var id: AnyHashable + var isIncoming: Bool + var value: Int64 + } + let items: [SubItem] = [ + SubItem(id: "in", isIncoming: true, value: component.category.incoming), + SubItem(id: "out", isIncoming: false, value: component.category.outgoing) + ] + + for i in 0 ..< items.count { + let item = items[i] + validKeys.insert(item.id) + + var itemTransition = transition + let itemView: ComponentView + if let current = self.itemViews[item.id] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + self.itemViews[item.id] = itemView + } + + itemView.parentState = state + let itemSize = itemView.update( + transition: itemTransition, + component: AnyComponent(SubItemComponent( + theme: component.theme, + strings: component.strings, + isIncoming: item.isIncoming, + value: item.value, + hasNext: i != items.count - 1 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 1000.0) + ) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: height), size: itemSize) + if let itemComponentView = itemView.view { + if itemComponentView.superview == nil { + self.subcategoryClippingContainer.addSubview(itemComponentView) + if !transition.animation.isImmediate { + itemComponentView.alpha = 0.0 + transition.setAlpha(view: itemComponentView, alpha: 1.0) + } + } + itemTransition.setFrame(view: itemComponentView, frame: itemFrame) + } + + height += itemSize.height + } + } + var removeKeys: [AnyHashable] = [] + for (key, itemView) in self.itemViews { + if !validKeys.contains(key) { + if let itemComponentView = itemView.view { + transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in + itemComponentView?.removeFromSuperview() + }) + } + removeKeys.append(key) + } + } + for key in removeKeys { + self.itemViews.removeValue(forKey: key) + } + + transition.setFrame(view: self.subcategoryClippingContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height))) + + return CGSize(width: availableSize.width, height: height) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift new file mode 100644 index 0000000000..a180f69f3c --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift @@ -0,0 +1,1033 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import Markdown +import ContextUI +import AnimatedAvatarSetNode +import AvatarNode +import RadialStatusNode +import UndoUI +import AnimatedStickerNode +import TelegramAnimatedStickerNode +import TelegramStringFormatting +import GalleryData +import AnimatedTextComponent + +final class DataUsageScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let statsSet: StatsSet + + init( + context: AccountContext, + statsSet: StatsSet + ) { + self.context = context + self.statsSet = statsSet + } + + static func ==(lhs: DataUsageScreenComponent, rhs: DataUsageScreenComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.statsSet != rhs.statsSet { + return false + } + return true + } + + private final class ScrollViewImpl: UIScrollView { + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + + override var contentOffset: CGPoint { + set(value) { + /*var value = value + if value.y > self.contentSize.height - self.bounds.height { + value.y = max(0.0, self.contentSize.height - self.bounds.height) + self.bounces = false + } else { + self.bounces = true + }*/ + super.contentOffset = value + } get { + return super.contentOffset + } + } + } + + final class AnimationHint { + enum Value { + case modeChanged + case clearedItems + } + let value: Value + + init(value: Value) { + self.value = value + } + } + + struct CategoryData: Equatable { + var incoming: Int64 + var outgoing: Int64 + } + + struct Stats: Equatable { + var categories: [Category: CategoryData] = [:] + + init() { + } + + init(stats: NetworkUsageStats, isWifi: Bool) { + for category in Category.allCases { + switch category { + case .photos: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.image.wifi.incoming, outgoing: stats.image.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.image.cellular.incoming, outgoing: stats.image.cellular.outgoing) + } + case .videos: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.video.wifi.incoming, outgoing: stats.video.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.video.cellular.incoming, outgoing: stats.video.cellular.outgoing) + } + case .files: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.file.wifi.incoming, outgoing: stats.file.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.file.cellular.incoming, outgoing: stats.file.cellular.outgoing) + } + case .music: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.audio.wifi.incoming, outgoing: stats.audio.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.audio.cellular.incoming, outgoing: stats.audio.cellular.outgoing) + } + case .messages: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.generic.wifi.incoming, outgoing: stats.generic.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.generic.cellular.incoming, outgoing: stats.generic.cellular.outgoing) + } + case .stickers: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.sticker.wifi.incoming, outgoing: stats.sticker.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.sticker.cellular.incoming, outgoing: stats.sticker.cellular.outgoing) + } + case .voiceMessages: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.voiceMessage.wifi.incoming, outgoing: stats.voiceMessage.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.voiceMessage.cellular.incoming, outgoing: stats.voiceMessage.cellular.outgoing) + } + case .calls: + if isWifi { + self.categories[category] = CategoryData(incoming: stats.call.wifi.incoming, outgoing: stats.call.wifi.outgoing) + } else { + self.categories[category] = CategoryData(incoming: stats.call.cellular.incoming, outgoing: stats.call.cellular.outgoing) + } + case .totalIn, .totalOut: + break + } + } + } + + var isEmpty: Bool { + return !self.categories.values.contains(where: { $0.incoming != 0 || $0.outgoing != 0 }) + } + } + + struct StatsSet: Equatable { + var wifi: Stats + var cellular: Stats + var resetTimestamp: Int32 + + init() { + self.wifi = Stats() + self.cellular = Stats() + self.resetTimestamp = Int32(Date().timeIntervalSince1970) + } + + init(stats: NetworkUsageStats) { + self.wifi = Stats(stats: stats, isWifi: true) + self.cellular = Stats(stats: stats, isWifi: false) + self.resetTimestamp = stats.resetWifiTimestamp + } + } + + enum Category: Hashable { + case photos + case videos + case files + case music + case messages + case stickers + case voiceMessages + case calls + case totalIn + case totalOut + + static var allCases: [Category] { + return [ + .photos, + .videos, + .files, + .music, + .messages, + .stickers, + .voiceMessages, + .calls + ] + } + + var color: UIColor { + switch self { + case .photos: + return UIColor(rgb: 0x5AC8FA) + case .videos: + return UIColor(rgb: 0x007AFF) + case .files: + return UIColor(rgb: 0x34C759) + case .music: + return UIColor(rgb: 0xFF2D55) + case .messages: + return UIColor(rgb: 0x5856D6) + case .stickers: + return UIColor(rgb: 0xFF9500) + case .voiceMessages: + return UIColor(rgb: 0xAF52DE) + case .calls: + return UIColor(rgb: 0xFF9500) + case .totalOut: + return UIColor(rgb: 0xFF9500) + case .totalIn: + return UIColor(rgb: 0xFF9500) + } + } + + var isSeparable: Bool { + switch self { + case .photos: + return true + case .videos: + return true + case .files: + return true + case .music: + return true + case .messages: + return true + case .stickers: + return true + case .voiceMessages: + return true + case .calls: + return true + case .totalIn, .totalOut: + return false + } + } + + func title(strings: PresentationStrings) -> String { + switch self { + case .photos: + return strings.StorageManagement_SectionPhotos + case .videos: + return strings.StorageManagement_SectionVideos + case .files: + return strings.StorageManagement_SectionFiles + case .music: + return strings.StorageManagement_SectionMusic + case .messages: + //TODO:localize + return "Messages" + case .stickers: + return strings.StorageManagement_SectionStickers + case .voiceMessages: + return "Voice Messages" + case .calls: + return "Calls" + case .totalIn: + return "Data Received" + case .totalOut: + return "Data Sent" + } + } + } + + enum SelectedStats { + case all + case mobile + case wifi + } + + class View: UIView, UIScrollViewDelegate { + private let scrollView: ScrollViewImpl + + private var allStats: StatsSet? + private var selectedStats: SelectedStats = .all + private var expandedCategories: Set = Set() + + private let navigationBackgroundView: BlurredBackgroundView + private let navigationSeparatorLayer: SimpleLayer + private let navigationSeparatorLayerContainer: SimpleLayer + + private let headerView = ComponentView() + private let headerOffsetContainer: UIView + private let headerDescriptionView = ComponentView() + + private var doneStatusNode: RadialStatusNode? + + private let scrollContainerView: UIView + + private let pieChartView = ComponentView() + private let chartTotalLabel = ComponentView() + + private let segmentedControlView = ComponentView() + + private let categoriesView = ComponentView() + private let categoriesDescriptionView = ComponentView() + + private let clearButtonView = ComponentView() + + private let totalCategoriesTitleView = ComponentView() + private let totalCategoriesView = ComponentView() + + private var component: DataUsageScreenComponent? + private weak var state: EmptyComponentState? + private var navigationMetrics: (navigationHeight: CGFloat, statusBarHeight: CGFloat)? + private var controller: (() -> ViewController?)? + + private var enableVelocityTracking: Bool = false + private var previousVelocityM1: CGFloat = 0.0 + private var previousVelocity: CGFloat = 0.0 + + private var ignoreScrolling: Bool = false + + override init(frame: CGRect) { + self.headerOffsetContainer = UIView() + self.headerOffsetContainer.isUserInteractionEnabled = false + + self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true) + self.navigationBackgroundView.alpha = 0.0 + + self.navigationSeparatorLayer = SimpleLayer() + self.navigationSeparatorLayer.opacity = 0.0 + self.navigationSeparatorLayerContainer = SimpleLayer() + self.navigationSeparatorLayerContainer.opacity = 0.0 + + self.scrollContainerView = UIView() + + self.scrollView = ScrollViewImpl() + + super.init(frame: frame) + + self.scrollView.delaysContentTouches = true + self.scrollView.canCancelContentTouches = true + self.scrollView.clipsToBounds = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.scrollView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = false + self.scrollView.scrollsToTop = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = true + self.addSubview(self.scrollView) + + self.scrollView.addSubview(self.scrollContainerView) + + self.addSubview(self.navigationBackgroundView) + + self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer) + self.layer.addSublayer(self.navigationSeparatorLayerContainer) + + self.addSubview(self.headerOffsetContainer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + if self.enableVelocityTracking { + self.previousVelocityM1 = self.previousVelocity + if let value = (scrollView.value(forKey: (["_", "verticalVelocity"] as [String]).joined()) as? NSNumber)?.doubleValue { + self.previousVelocity = CGFloat(value) + } + } + + self.updateScrolling(transition: .immediate) + } + } + + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + } + + private func updateScrolling(transition: Transition) { + let scrollBounds = self.scrollView.bounds + + if let headerView = self.headerView.view, let navigationMetrics = self.navigationMetrics { + var headerOffset: CGFloat = scrollBounds.minY + + let minY = navigationMetrics.statusBarHeight + floor((navigationMetrics.navigationHeight - navigationMetrics.statusBarHeight) / 2.0) + + let minOffset = headerView.center.y - minY + + headerOffset = min(headerOffset, minOffset) + + let animatedTransition = Transition(animation: .curve(duration: 0.18, curve: .easeInOut)) + let navigationBackgroundAlpha: CGFloat = abs(headerOffset - minOffset) < 4.0 ? 1.0 : 0.0 + + animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha) + animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha) + + let expansionDistance: CGFloat = 32.0 + var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance + expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor)) + + transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor) + + var offsetFraction: CGFloat = abs(headerOffset - minOffset) / 60.0 + offsetFraction = min(1.0, max(0.0, offsetFraction)) + transition.setScale(view: headerView, scale: 1.0 * offsetFraction + 0.8 * (1.0 - offsetFraction)) + + transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size)) + } + } + + func update(component: DataUsageScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + self.state = state + + if self.allStats == nil { + self.allStats = component.statsSet + } + + let environment = environment[ViewControllerComponentContainer.Environment.self].value + + let animationHint = transition.userData(AnimationHint.self) + + if let animationHint { + if case .clearedItems = animationHint.value { + /*if let snapshotView = self.scrollContainerView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = self.scrollContainerView.frame + self.scrollView.insertSubview(snapshotView, aboveSubview: self.scrollContainerView) + self.scrollContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + }*/ + } + } else { + transition.setAlpha(view: self.scrollView, alpha: 1.0) + transition.setAlpha(view: self.headerOffsetContainer, alpha: 1.0) + } + + self.controller = environment.controller + + self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight) + + self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor + + let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight)) + self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) + self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition) + transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame) + + let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)) + + transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame) + transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size)) + + self.backgroundColor = environment.theme.list.blocksBackgroundColor + + var contentHeight: CGFloat = 0.0 + + let topInset: CGFloat = 19.0 + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + + let bottomInset: CGFloat = environment.safeInsets.bottom + + contentHeight += environment.statusBarHeight + topInset + + let allCategories: [Category] = [ + .photos, + .videos, + .files, + .music, + .stickers, + .voiceMessages, + .messages, + .calls + ] + + var listCategories: [DataCategoriesComponent.CategoryData] = [] + + var totalSize: Int64 = 0 + var totalIn: Int64 = 0 + var totalOut: Int64 = 0 + if let allStats = self.allStats { + var stats: Stats + switch self.selectedStats { + case .all: + stats = allStats.wifi + for (category, value) in allStats.cellular.categories { + if stats.categories[category] == nil { + stats.categories[category] = value + } else { + stats.categories[category]?.incoming += value.incoming + stats.categories[category]?.incoming += value.outgoing + } + } + case .wifi: + stats = allStats.wifi + case .mobile: + stats = allStats.cellular + } + + for (_, value) in stats.categories { + totalSize += value.incoming + value.outgoing + totalIn += value.incoming + totalOut += value.outgoing + } + + for category in allCategories { + var categoryIn: Int64 = 0 + var categoryOut: Int64 = 0 + if let categoryData = stats.categories[category] { + categoryIn = categoryData.incoming + categoryOut = categoryData.outgoing + } + let categorySize: Int64 = categoryIn + categoryOut + + let categoryFraction: Double + if categorySize == 0 || totalSize == 0 { + categoryFraction = 0.0 + } else { + categoryFraction = Double(categorySize) / Double(totalSize) + } + + listCategories.append(DataCategoriesComponent.CategoryData( + key: category, + color: category.color, + title: category.title(strings: environment.strings), + size: categorySize, + sizeFraction: categoryFraction, + incoming: categoryIn, + outgoing: categoryOut, + isSeparable: category.isSeparable && (categoryIn + categoryOut != 0), + isExpanded: self.expandedCategories.contains(category) + )) + } + } + + listCategories.sort(by: { lhs, rhs in + if lhs.size != rhs.size { + return lhs.size > rhs.size + } + return lhs.title < rhs.title + }) + + var chartItems: [PieChartComponent.ChartData.Item] = [] + for listCategory in listCategories { + let categoryChartFraction: CGFloat = listCategory.sizeFraction + chartItems.append(PieChartComponent.ChartData.Item(id: AnyHashable(listCategory.key), displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: nil, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0)) + } + + if totalSize == 0 { + chartItems.removeAll() + } + + let totalCategories: [DataCategoriesComponent.CategoryData] = [ + DataCategoriesComponent.CategoryData( + key: .totalOut, + color: Category.totalOut.color, + title: Category.totalOut.title(strings: environment.strings), + size: totalOut, + sizeFraction: 0.0, + incoming: 0, + outgoing: 0, + isSeparable: false, + isExpanded: false + ), + DataCategoriesComponent.CategoryData( + key: .totalIn, + color: Category.totalIn.color, + title: Category.totalIn.title(strings: environment.strings), + size: totalIn, + sizeFraction: 0.0, + incoming: 0, + outgoing: 0, + isSeparable: false, + isExpanded: false + ) + ] + + let chartData = PieChartComponent.ChartData(items: chartItems) + self.pieChartView.parentState = state + + var pieChartTransition = transition + if transition.animation.isImmediate, let animationHint { + switch animationHint.value { + case .modeChanged, .clearedItems: + pieChartTransition = Transition(animation: .curve(duration: 0.4, curve: .spring)) + } + } + + let pieChartSize = self.pieChartView.update( + transition: pieChartTransition, + component: AnyComponent(PieChartComponent( + theme: environment.theme, + strings: environment.strings, + chartData: chartData + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 60.0) + ) + let pieChartFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: pieChartSize) + if let pieChartComponentView = self.pieChartView.view { + if pieChartComponentView.superview == nil { + self.scrollView.addSubview(pieChartComponentView) + } + + pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame) + } + if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty { + let checkColor = UIColor(rgb: 0x34C759) + + let doneStatusNode: RadialStatusNode + var animateIn = false + if let current = self.doneStatusNode { + doneStatusNode = current + } else { + doneStatusNode = RadialStatusNode(backgroundNodeColor: .clear) + self.doneStatusNode = doneStatusNode + self.scrollView.addSubnode(doneStatusNode) + animateIn = true + } + let doneSize = CGSize(width: 100.0, height: 100.0) + doneStatusNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - doneSize.width) / 2.0), y: contentHeight), size: doneSize) + + if animateIn { + Queue.mainQueue().after(0.18, { + doneStatusNode.transitionToState(.check(checkColor), animated: true) + }) + } + + contentHeight += doneSize.height + } else { + contentHeight += pieChartSize.height + + if let doneStatusNode = self.doneStatusNode { + self.doneStatusNode = nil + doneStatusNode.removeFromSupernode() + } + } + + contentHeight += 23.0 + + let headerText: String + if listCategories.isEmpty { + headerText = "Data Usage Reset" + } else { + headerText = "Data Usage" + } + let headerViewSize = self.headerView.update( + transition: transition, + component: AnyComponent(Text(text: headerText, font: Font.semibold(20.0), color: environment.theme.list.itemPrimaryTextColor)), + environment: {}, + containerSize: CGSize(width: floor((availableSize.width) / 0.8), height: 100.0) + ) + let headerViewFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerViewSize.width) / 2.0), y: contentHeight), size: headerViewSize) + if let headerComponentView = self.headerView.view { + if headerComponentView.superview == nil { + self.headerOffsetContainer.addSubview(headerComponentView) + } + transition.setPosition(view: headerComponentView, position: headerViewFrame.center) + transition.setBounds(view: headerComponentView, bounds: CGRect(origin: CGPoint(), size: headerViewFrame.size)) + } + contentHeight += headerViewSize.height + + contentHeight += 6.0 + + let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor) + let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor) + + //TODO:localize + + let timestampString: String + if let allStats = self.allStats, allStats.resetTimestamp != 0 { + let formatter = DateFormatter() + formatter.dateFormat = "E, d MMM yyyy HH:mm" + let dateStringPlain = formatter.string(from: Date(timeIntervalSince1970: Double(allStats.resetTimestamp))) + timestampString = "Your network usage since \(dateStringPlain)" + } else { + timestampString = "Your network usage" + } + + let totalUsageText: String = timestampString + let headerDescriptionSize = self.headerDescriptionView.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .markdown(text: totalUsageText, attributes: MarkdownAttributes( + body: body, + bold: bold, + link: body, + linkAttribute: { _ in nil } + )), horizontalAlignment: .center, maximumNumberOfLines: 0)), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 15.0 * 2.0, height: 10000.0) + ) + let headerDescriptionFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerDescriptionSize.width) / 2.0), y: contentHeight), size: headerDescriptionSize) + if let headerDescriptionComponentView = self.headerDescriptionView.view { + if headerDescriptionComponentView.superview == nil { + self.scrollContainerView.addSubview(headerDescriptionComponentView) + } + transition.setFrame(view: headerDescriptionComponentView, frame: headerDescriptionFrame) + } + contentHeight += headerDescriptionSize.height + contentHeight += 8.0 + + contentHeight += 12.0 + + if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty { + if let chartTotalLabelView = self.chartTotalLabel.view { + chartTotalLabelView.removeFromSuperview() + } + } else { + let sizeText = dataSizeString(Int(totalSize), forceDecimal: true, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: ".")) + + var animatedTextItems: [AnimatedTextComponent.Item] = [] + var remainingSizeText = sizeText + if let index = remainingSizeText.firstIndex(of: ".") { + animatedTextItems.append(AnimatedTextComponent.Item(id: "n-full", content: .text(String(remainingSizeText[remainingSizeText.startIndex ..< index])))) + animatedTextItems.append(AnimatedTextComponent.Item(id: "dot", content: .text("."))) + remainingSizeText = String(remainingSizeText[remainingSizeText.index(after: index)...]) + } + if let index = remainingSizeText.firstIndex(of: " ") { + animatedTextItems.append(AnimatedTextComponent.Item(id: "n-fract", content: .text(String(remainingSizeText[remainingSizeText.startIndex ..< index])))) + remainingSizeText = String(remainingSizeText[index...]) + } + if !remainingSizeText.isEmpty { + animatedTextItems.append(AnimatedTextComponent.Item(id: "rest", isUnbreakable: true, content: .text(remainingSizeText))) + } + + let chartTotalLabelSize = self.chartTotalLabel.update( + transition: transition, + component: AnyComponent(AnimatedTextComponent( + font: Font.with(size: 20.0, design: .round, weight: .bold), + color: environment.theme.list.itemPrimaryTextColor, + items: animatedTextItems + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 200.0) + ) + if let chartTotalLabelView = self.chartTotalLabel.view { + if chartTotalLabelView.superview == nil { + self.scrollContainerView.addSubview(chartTotalLabelView) + } + let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize) + transition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame) + transition.setAlpha(view: chartTotalLabelView, alpha: listCategories.isEmpty ? 0.0 : 1.0) + } + } + + let segmentedSize = self.segmentedControlView.update( + transition: transition, + component: AnyComponent(SegmentControlComponent( + theme: environment.theme, + items: [ + SegmentControlComponent.Item(id: AnyHashable(SelectedStats.all), title: "All"), + SegmentControlComponent.Item(id: AnyHashable(SelectedStats.mobile), title: "Mobile"), + SegmentControlComponent.Item(id: AnyHashable(SelectedStats.wifi), title: "WiFi") + ], + selectedId: "total", + action: { [weak self] id in + guard let self, let id = id.base as? SelectedStats else { + return + } + self.selectedStats = id + self.state?.updated(transition: Transition(animation: .none).withUserData(AnimationHint(value: .modeChanged))) + })), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + ) + let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize) + if let segmentedControlComponentView = self.segmentedControlView.view { + if segmentedControlComponentView.superview == nil { + self.scrollContainerView.addSubview(segmentedControlComponentView) + } + transition.setFrame(view: segmentedControlComponentView, frame: segmentedControlFrame) + } + contentHeight += segmentedSize.height + contentHeight += 26.0 + + self.categoriesView.parentState = state + let categoriesSize = self.categoriesView.update( + transition: transition, + component: AnyComponent(DataCategoriesComponent( + theme: environment.theme, + strings: environment.strings, + categories: listCategories, + toggleCategoryExpanded: { [weak self] key in + guard let self else { + return + } + if self.expandedCategories.contains(key) { + self.expandedCategories.remove(key) + } else { + self.expandedCategories.insert(key) + } + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude) + ) + if let categoriesComponentView = self.categoriesView.view { + if categoriesComponentView.superview == nil { + self.scrollContainerView.addSubview(categoriesComponentView) + } + + transition.setFrame(view: categoriesComponentView, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: categoriesSize)) + } + contentHeight += categoriesSize.height + contentHeight += 8.0 + + //TODO:localize + let categoriesDescriptionSize = self.categoriesDescriptionView.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .markdown(text: "Tap on each section for detailed view.", attributes: MarkdownAttributes( + body: body, + bold: bold, + link: body, + linkAttribute: { _ in nil } + )), maximumNumberOfLines: 0)), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 15.0 * 2.0, height: 10000.0) + ) + let categoriesDescriptionFrame = CGRect(origin: CGPoint(x: sideInset + 15.0, y: contentHeight), size: categoriesDescriptionSize) + if let categoriesDescriptionComponentView = self.categoriesDescriptionView.view { + if categoriesDescriptionComponentView.superview == nil { + self.scrollContainerView.addSubview(categoriesDescriptionComponentView) + } + transition.setFrame(view: categoriesDescriptionComponentView, frame: categoriesDescriptionFrame) + } + contentHeight += categoriesDescriptionSize.height + contentHeight += 40.0 + + //TODO:localize + let totalCategoriesTitleSize = self.totalCategoriesTitleView.update( + transition: transition, + component: AnyComponent(MultilineTextComponent(text: .markdown(text: "TOTAL NETWORK USAGE", attributes: MarkdownAttributes( + body: body, + bold: bold, + link: body, + linkAttribute: { _ in nil } + )), maximumNumberOfLines: 0)), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 15.0 * 2.0, height: 10000.0) + ) + let totalCategoriesTitleFrame = CGRect(origin: CGPoint(x: sideInset + 15.0, y: contentHeight), size: totalCategoriesTitleSize) + if let totalCategoriesTitleComponentView = self.totalCategoriesTitleView.view { + if totalCategoriesTitleComponentView.superview == nil { + self.scrollContainerView.addSubview(totalCategoriesTitleComponentView) + } + transition.setFrame(view: totalCategoriesTitleComponentView, frame: totalCategoriesTitleFrame) + } + contentHeight += totalCategoriesTitleSize.height + contentHeight += 8.0 + + self.totalCategoriesView.parentState = state + let totalCategoriesSize = self.totalCategoriesView.update( + transition: transition, + component: AnyComponent(DataCategoriesComponent( + theme: environment.theme, + strings: environment.strings, + categories: totalCategories, + toggleCategoryExpanded: { _ in + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude) + ) + if let totalCategoriesComponentView = self.totalCategoriesView.view { + if totalCategoriesComponentView.superview == nil { + self.scrollContainerView.addSubview(totalCategoriesComponentView) + } + + transition.setFrame(view: totalCategoriesComponentView, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: totalCategoriesSize)) + } + contentHeight += totalCategoriesSize.height + contentHeight += 40.0 + + if let allStats = self.allStats, !(allStats.wifi.isEmpty && allStats.cellular.isEmpty) { + let clearButtonSize = self.clearButtonView.update( + transition: transition, + component: AnyComponent(DataButtonComponent( + theme: environment.theme, + title: "Reset Statistics", + action: { [weak self] in + self?.requestClear() + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + let clearButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: clearButtonSize) + if let clearButtonComponentView = self.clearButtonView.view { + if clearButtonComponentView.superview == nil { + self.scrollContainerView.addSubview(clearButtonComponentView) + } + transition.setFrame(view: clearButtonComponentView, frame: clearButtonFrame) + } + contentHeight += clearButtonSize.height + contentHeight += 40.0 + } else { + if let clearButtonComponentView = self.clearButtonView.view { + clearButtonComponentView.isHidden = true + } + } + + contentHeight += bottomInset + + self.ignoreScrolling = true + + let contentOffset = self.scrollView.bounds.minY + transition.setPosition(view: self.scrollView, position: CGRect(origin: CGPoint(), size: availableSize).center) + let contentSize = CGSize(width: availableSize.width, height: contentHeight) + if self.scrollView.contentSize != contentSize { + self.scrollView.contentSize = contentSize + } + transition.setFrame(view: self.scrollContainerView, frame: CGRect(origin: CGPoint(), size: contentSize)) + + var scrollViewBounds = self.scrollView.bounds + scrollViewBounds.size = availableSize + if let animationHint, case .clearedItems = animationHint.value { + scrollViewBounds.origin.y = 0.0 + } + transition.setBounds(view: self.scrollView, bounds: scrollViewBounds) + + if !pieChartTransition.animation.isImmediate && self.scrollView.bounds.minY != contentOffset { + let deltaOffset = self.scrollView.bounds.minY - contentOffset + pieChartTransition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: -deltaOffset), to: CGPoint(), additive: true) + pieChartTransition.animateBoundsOrigin(view: self.headerOffsetContainer, from: CGPoint(x: 0.0, y: -deltaOffset), to: CGPoint(), additive: true) + } + + self.ignoreScrolling = false + + self.updateScrolling(transition: transition) + + return availableSize + } + + private func reportCleared() { + guard let component = self.component else { + return + } + guard let controller = self.controller?() else { + return + } + let _ = component + let _ = controller + + /*let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + controller.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(size, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), in: .window(.root))*/ + } + + private func reloadStats(firstTime: Bool, completion: @escaping () -> Void) { + guard let component = self.component else { + completion() + return + } + let _ = component + } + + private func requestClear() { + self.commitClear() + } + + private func commitClear() { + guard let component = self.component else { + return + } + + #if !DEBUG + let _ = accountNetworkUsageStats(account: component.context.account, reset: .wifi).start() + let _ = accountNetworkUsageStats(account: component.context.account, reset: .cellular).start() + #endif + + self.allStats = StatsSet() + //self.state?.updated(transition: Transition(animation: .none).withUserData(AnimationHint(value: .clearedItems))) + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(value: .clearedItems))) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public final class DataUsageScreen: ViewControllerComponentContainer { + private let context: AccountContext + + private let readyValue = Promise() + override public var ready: Promise { + return self.readyValue + } + + fileprivate var childCompleted: ((@escaping () -> Void) -> Void)? + + public init(context: AccountContext, stats: NetworkUsageStats) { + self.context = context + + //let componentReady = Promise() + super.init(context: context, component: DataUsageScreenComponent(context: context, statsSet: DataUsageScreenComponent.StatsSet(stats: stats)), navigationBarAppearance: .transparent) + + //self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true))) + self.readyValue.set(.single(true)) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func viewDidLoad() { + super.viewDidLoad() + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift index d25e2a8a85..543ed5eec2 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/PieChartComponent.swift @@ -210,20 +210,24 @@ private final class ChartLabel: UIView { final class PieChartComponent: Component { struct ChartData: Equatable { struct Item: Equatable { - var id: StorageUsageScreenComponent.Category + var id: AnyHashable var displayValue: Double var displaySize: Int64 var value: Double var color: UIColor + var particle: String? + var title: String var mergeable: Bool var mergeFactor: CGFloat - init(id: StorageUsageScreenComponent.Category, displayValue: Double, displaySize: Int64, value: Double, color: UIColor, mergeable: Bool, mergeFactor: CGFloat) { + init(id: AnyHashable, displayValue: Double, displaySize: Int64, value: Double, color: UIColor, particle: String?, title: String, mergeable: Bool, mergeFactor: CGFloat) { self.id = id self.displayValue = displayValue self.displaySize = displaySize self.value = value self.color = color + self.particle = particle + self.title = title self.mergeable = mergeable self.mergeFactor = mergeFactor } @@ -296,8 +300,10 @@ final class PieChartComponent: Component { } private struct CalculatedSection { - var id: StorageUsageScreenComponent.Category + var id: AnyHashable var color: UIColor + var particle: String? + var title: String var innerAngle: Range var outerAngle: Range var innerRadius: CGFloat @@ -305,8 +311,10 @@ final class PieChartComponent: Component { var label: CalculatedLabel? init( - id: StorageUsageScreenComponent.Category, + id: AnyHashable, color: UIColor, + particle: String?, + title: String, innerAngle: Range, outerAngle: Range, innerRadius: CGFloat, @@ -315,6 +323,8 @@ final class PieChartComponent: Component { ) { self.id = id self.color = color + self.particle = particle + self.title = title self.innerAngle = innerAngle self.outerAngle = outerAngle self.innerRadius = innerRadius @@ -361,6 +371,8 @@ final class PieChartComponent: Component { self.sections.append(CalculatedSection( id: right.id, color: left.color.interpolateTo(right.color, fraction: progress) ?? right.color, + particle: right.particle, + title: right.title, innerAngle: innerAngle, outerAngle: outerAngle, innerRadius: left.innerRadius.interpolate(to: right.innerRadius, amount: progress), @@ -440,6 +452,8 @@ final class PieChartComponent: Component { self.sections.append(CalculatedSection( id: item.id, color: itemColor, + particle: item.particle, + title: item.title, innerAngle: arcInnerStartAngle ..< arcInnerEndAngle, outerAngle: arcOuterStartAngle ..< arcOuterEndAngle, innerRadius: innerDiameter * 0.5, @@ -782,7 +796,7 @@ final class PieChartComponent: Component { private var particleImage: UIImage? private var particleLayers: [SimpleLayer] = [] - init(category: StorageUsageScreenComponent.Category) { + init(particle: String?) { self.maskLayer = SimpleShapeLayer() self.maskLayer.fillColor = UIColor.white.cgColor @@ -799,23 +813,8 @@ final class PieChartComponent: Component { self.addSublayer(self.gradientLayer) self.addSublayer(self.labelLayer) - switch category { - case .photos: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticlePhotos")?.precomposed() - case .videos: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleVideos")?.precomposed() - case .files: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleDocuments")?.precomposed() - case .music: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleMusic")?.precomposed() - case .other: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleOther")?.precomposed() - case .stickers: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleStickers")?.precomposed() - case .avatars: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleAvatars")?.precomposed() - case .misc: - self.particleImage = UIImage(bundleImageName: "Settings/Storage/ParticleOther")?.precomposed() + if let particle { + self.particleImage = UIImage(bundleImageName: particle)?.precomposed() } } @@ -1069,9 +1068,9 @@ final class PieChartComponent: Component { return nil } - func tooltipLocation(forKey key: StorageUsageScreenComponent.Category) -> CGPoint? { + func tooltipLocation(forKey key: AnyHashable) -> CGPoint? { for (id, itemLayer) in self.sectionLayers { - if id == AnyHashable(key) { + if id == key { return itemLayer.tooltipLocation() } } @@ -1117,7 +1116,7 @@ final class PieChartComponent: Component { if data.items.isEmpty { self.currentLayout = CalculatedLayout( size: CGSize(width: 200.0, height: 200.0), - items: [.init(id: .other, displayValue: 0.0, displaySize: 0, value: 1.0, color: .green, mergeable: false, mergeFactor: 1.0)], + items: [.init(id: AnyHashable(StorageUsageScreenComponent.Category.other), displayValue: 0.0, displaySize: 0, value: 1.0, color: .green, particle: "Settings/Storage/ParticleOther", title: "", mergeable: false, mergeFactor: 1.0)], selectedKey: self.selectedKey, isEmpty: true ) @@ -1217,7 +1216,7 @@ final class PieChartComponent: Component { if let current = self.sectionLayers[section.id] { sectionLayer = current } else { - sectionLayer = SectionLayer(category: section.id) + sectionLayer = SectionLayer(particle: section.particle) self.sectionLayers[section.id] = sectionLayer self.layer.addSublayer(sectionLayer) } @@ -1296,7 +1295,7 @@ final class PieChartComponent: Component { transition.setFrame(view: self.dataView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - 200.0) / 2.0), y: 0.0), size: CGSize(width: 200.0, height: 200.0))) self.dataView.setItems(theme: component.theme, data: component.chartData, selectedKey: self.selectedKey, animated: !transition.animation.isImmediate) - if let selectedKey = self.selectedKey?.base as? StorageUsageScreenComponent.Category, let item = component.chartData.items.first(where: { $0.id == selectedKey }) { + if let selectedKey = self.selectedKey, let item = component.chartData.items.first(where: { $0.id == selectedKey }) { let tooltip: ComponentView var tooltipTransition = transition var animateIn = false @@ -1334,7 +1333,7 @@ final class PieChartComponent: Component { component: AnyComponent(ChartSelectionTooltip( theme: component.theme, fractionText: fractionString, - title: selectedKey.title(strings: component.strings), + title: item.title, sizeText: dataSizeString(Int(item.displaySize), formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: ".")) )), environment: {}, diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/SegmentControlComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/SegmentControlComponent.swift new file mode 100644 index 0000000000..1b71a910ec --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/SegmentControlComponent.swift @@ -0,0 +1,113 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import TelegramStringFormatting +import CheckNode +import SegmentedControlNode + +final class SegmentControlComponent: Component { + struct Item: Equatable { + var id: AnyHashable + var title: String + } + + let theme: PresentationTheme + let items: [Item] + let selectedId: AnyHashable? + let action: (AnyHashable) -> Void + + init( + theme: PresentationTheme, + items: [Item], + selectedId: AnyHashable?, + action: @escaping (AnyHashable) -> Void + ) { + self.theme = theme + self.items = items + self.selectedId = selectedId + self.action = action + } + + static func ==(lhs: SegmentControlComponent, rhs: SegmentControlComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.items != rhs.items { + return false + } + if lhs.selectedId != rhs.selectedId { + return false + } + return true + } + + class View: UIView { + private let title = ComponentView() + + private var component: SegmentControlComponent? + + private var segmentedNode: SegmentedControlNode? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: SegmentControlComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + + let segmentedNode: SegmentedControlNode + if let current = self.segmentedNode { + segmentedNode = current + + if themeUpdated { + segmentedNode.updateTheme(SegmentedControlTheme(theme: component.theme)) + } + } else { + let mappedItems: [SegmentedControlItem] = component.items.map { item -> SegmentedControlItem in + return SegmentedControlItem(title: item.title) + } + segmentedNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: component.theme), items: mappedItems, selectedIndex: component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0) + self.segmentedNode = segmentedNode + self.addSubnode(segmentedNode) + + segmentedNode.selectedIndexChanged = { [weak self] index in + guard let self, let component = self.component else { + return + } + self.component?.action(component.items[index].id) + } + } + + let controlSize = segmentedNode.updateLayout(SegmentedControlLayout.sizeToFit(maximumWidth: availableSize.width, minimumWidth: min(availableSize.width, 296.0), height: 31.0), transition: transition.containedViewLayoutTransition) + + transition.containedViewLayoutTransition.updateFrame(node: segmentedNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: controlSize)) + + return controlSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 09144922bf..0a929020f8 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -305,6 +305,27 @@ final class StorageUsageScreenComponent: Component { return strings.StorageManagement_SectionMiscellaneous } } + + var particle: String? { + switch self { + case .photos: + return "Settings/Storage/ParticlePhotos" + case .videos: + return "Settings/Storage/ParticleVideos" + case .files: + return "Settings/Storage/ParticleDocuments" + case .music: + return "Settings/Storage/ParticleMusic" + case .other: + return "Settings/Storage/ParticleOther" + case .stickers: + return "Settings/Storage/ParticleStickers" + case .avatars: + return "Settings/Storage/ParticleAvatars" + case .misc: + return "Settings/Storage/ParticleOther" + } + } } private final class AggregatedData { @@ -1306,7 +1327,7 @@ final class StorageUsageScreenComponent: Component { if let aggregatedData = self.aggregatedData, !aggregatedData.selectedCategories.isEmpty && !aggregatedData.selectedCategories.contains(listCategory.key) { categoryChartFraction = 0.0 } - chartItems.append(PieChartComponent.ChartData.Item(id: listCategory.key, displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, mergeable: false, mergeFactor: 1.0)) + chartItems.append(PieChartComponent.ChartData.Item(id: listCategory.key, displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: listCategory.key.particle, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0)) } var totalOtherSize: Int64 = 0 @@ -1339,7 +1360,7 @@ final class StorageUsageScreenComponent: Component { categoryChartFraction = 0.0 } - var chartItem = PieChartComponent.ChartData.Item(id: listCategory.key, displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, mergeable: false, mergeFactor: 1.0) + var chartItem = PieChartComponent.ChartData.Item(id: listCategory.key, displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: listCategory.key.particle, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0) if chartItem.value > 0.00001 { chartItem.value = max(chartItem.value, 0.01) @@ -1355,7 +1376,7 @@ final class StorageUsageScreenComponent: Component { } if !listCategories.isEmpty { - chartItems.append(PieChartComponent.ChartData.Item(id: .other, displayValue: otherRealSum, displaySize: totalOtherSize, value: self.isOtherCategoryExpanded ? 0.0 : otherSum, color: Category.misc.color, mergeable: false, mergeFactor: 1.0)) + chartItems.append(PieChartComponent.ChartData.Item(id: AnyHashable(Category.other), displayValue: otherRealSum, displaySize: totalOtherSize, value: self.isOtherCategoryExpanded ? 0.0 : otherSum, color: Category.misc.color, particle: Category.misc.particle, title: Category.misc.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0)) } let chartData = PieChartComponent.ChartData(items: chartItems) diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/Contents.json new file mode 100644 index 0000000000..27a736434b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "smile_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/smile_24.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/smile_24.pdf new file mode 100644 index 0000000000..07a4fda0e3 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Smile.imageset/smile_24.pdf @@ -0,0 +1,175 @@ +%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 3.000000 1.669922 cm +0.000000 0.000000 0.000000 scn +17.334999 10.330078 m +17.334999 5.726785 13.603293 1.995079 9.000000 1.995079 c +9.000000 0.665077 l +14.337832 0.665077 18.665001 4.992246 18.665001 10.330078 c +17.334999 10.330078 l +h +9.000000 1.995079 m +4.396707 1.995079 0.665000 5.726785 0.665000 10.330078 c +-0.665000 10.330078 l +-0.665000 4.992246 3.662168 0.665077 9.000000 0.665077 c +9.000000 1.995079 l +h +0.665000 10.330078 m +0.665000 14.933372 4.396707 18.665077 9.000000 18.665077 c +9.000000 19.995079 l +3.662168 19.995079 -0.665000 15.667911 -0.665000 10.330078 c +0.665000 10.330078 l +h +9.000000 18.665077 m +13.603293 18.665077 17.334999 14.933372 17.334999 10.330078 c +18.665001 10.330078 l +18.665001 15.667911 14.337832 19.995079 9.000000 19.995079 c +9.000000 18.665077 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 7.660156 12.477661 cm +0.000000 0.000000 0.000000 scn +2.347826 1.369570 m +2.347826 0.613180 1.822247 0.000005 1.173913 0.000005 c +0.525579 0.000005 0.000000 0.613180 0.000000 1.369570 c +0.000000 2.125960 0.525579 2.739136 1.173913 2.739136 c +1.822247 2.739136 2.347826 2.125960 2.347826 1.369570 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 14.000000 12.477661 cm +0.000000 0.000000 0.000000 scn +2.347826 1.369570 m +2.347826 0.613180 1.822247 0.000005 1.173913 0.000005 c +0.525579 0.000005 0.000000 0.613180 0.000000 1.369570 c +0.000000 2.125960 0.525579 2.739136 1.173913 2.739136 c +1.822247 2.739136 2.347826 2.125960 2.347826 1.369570 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.500000 6.076050 cm +0.000000 0.000000 0.000000 scn +7.572875 3.015941 m +7.759379 3.332331 7.654087 3.740008 7.337697 3.926513 c +7.021306 4.113018 6.613629 4.007725 6.427125 3.691334 c +7.572875 3.015941 l +h +0.572875 3.691335 m +0.386370 4.007725 -0.021307 4.113018 -0.337697 3.926513 c +-0.654087 3.740008 -0.759380 3.332331 -0.572875 3.015941 c +0.572875 3.691335 l +h +7.000000 3.353638 m +6.427125 3.691334 6.427270 3.691582 6.427412 3.691822 c +6.427454 3.691892 6.427591 3.692125 6.427674 3.692266 c +6.427841 3.692548 6.427992 3.692801 6.428125 3.693027 c +6.428393 3.693477 6.428595 3.693815 6.428731 3.694042 c +6.429003 3.694496 6.429012 3.694507 6.428758 3.694089 c +6.428248 3.693253 6.426688 3.690706 6.424076 3.686561 c +6.418847 3.678266 6.409435 3.663620 6.395833 3.643527 c +6.368581 3.603271 6.324809 3.541589 6.264480 3.465568 c +6.143271 3.312833 5.958584 3.106452 5.710678 2.900174 c +5.216724 2.489165 4.487087 2.089347 3.500000 2.089347 c +3.500000 0.759347 l +4.875639 0.759347 5.896002 1.324174 6.561367 1.877810 c +6.893120 2.153855 7.140421 2.429797 7.306286 2.638804 c +7.389494 2.743654 7.453030 2.832698 7.497203 2.897951 c +7.519312 2.930611 7.536642 2.957415 7.549225 2.977380 c +7.555520 2.987366 7.560634 2.995653 7.564572 3.002117 c +7.566542 3.005350 7.568218 3.008128 7.569601 3.010437 c +7.570293 3.011591 7.570911 3.012628 7.571457 3.013546 c +7.571730 3.014005 7.571984 3.014434 7.572220 3.014833 c +7.572339 3.015033 7.572502 3.015310 7.572561 3.015410 c +7.572720 3.015679 7.572875 3.015941 7.000000 3.353638 c +h +3.500000 2.089347 m +2.512913 2.089347 1.783276 2.489165 1.289322 2.900174 c +1.041416 3.106452 0.856728 3.312833 0.735520 3.465568 c +0.675191 3.541589 0.631418 3.603271 0.604167 3.643527 c +0.590564 3.663620 0.581153 3.678266 0.575924 3.686562 c +0.573312 3.690706 0.571751 3.693253 0.571242 3.694089 c +0.570987 3.694507 0.570996 3.694496 0.571268 3.694042 c +0.571404 3.693815 0.571606 3.693477 0.571874 3.693027 c +0.572008 3.692801 0.572158 3.692548 0.572325 3.692266 c +0.572408 3.692125 0.572546 3.691892 0.572588 3.691822 c +0.572729 3.691582 0.572875 3.691335 0.000000 3.353638 c +-0.572875 3.015941 -0.572721 3.015679 -0.572562 3.015410 c +-0.572503 3.015310 -0.572339 3.015033 -0.572221 3.014833 c +-0.571985 3.014434 -0.571730 3.014005 -0.571457 3.013546 c +-0.570912 3.012628 -0.570293 3.011591 -0.569602 3.010437 c +-0.568218 3.008128 -0.566542 3.005350 -0.564573 3.002117 c +-0.560634 2.995653 -0.555520 2.987366 -0.549226 2.977380 c +-0.536642 2.957415 -0.519313 2.930610 -0.497203 2.897950 c +-0.453030 2.832698 -0.389495 2.743654 -0.306287 2.638804 c +-0.140421 2.429797 0.106880 2.153855 0.438633 1.877810 c +1.103997 1.324174 2.124361 0.759347 3.500000 0.759347 c +3.500000 2.089347 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 4309 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.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 +0000004399 00000 n +0000004422 00000 n +0000004595 00000 n +0000004669 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4728 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/Contents.json new file mode 100644 index 0000000000..a897fd0823 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "calls.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/calls.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/calls.pdf new file mode 100644 index 0000000000..63353862fc --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataCalls.imageset/calls.pdf @@ -0,0 +1,111 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 0.584314 0.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm +1.000000 1.000000 1.000000 scn +4.868853 4.891197 m +2.124590 7.636960 0.000000 11.022417 0.000000 13.837070 c +0.000000 15.086933 0.413115 16.218699 1.357377 17.133953 c +1.927869 17.694916 2.596721 18.000000 3.216393 18.000000 c +3.747541 18.000000 4.229508 17.783487 4.573771 17.301258 c +6.244052 14.601130 l +6.749065 13.784742 6.700242 12.741879 6.121165 11.976244 c +4.939975 10.414518 l +4.831778 10.306263 4.792434 10.188166 4.792434 10.060226 c +4.792434 9.942129 4.841614 9.814191 4.890794 9.705935 c +5.176040 9.105606 6.031778 8.062413 7.035057 7.058586 c +8.028500 6.054759 9.090795 5.218237 9.671123 4.913152 c +9.779319 4.863944 9.907188 4.814737 10.044893 4.814737 c +10.182598 4.814737 10.310467 4.854102 10.418663 4.962358 c +11.944046 6.134850 l +12.711500 6.724756 13.764204 6.777284 14.586570 6.266706 c +17.340984 4.556588 l +17.822952 4.221979 18.000000 3.769273 18.000000 3.296884 c +18.000000 2.637506 17.626228 1.948606 17.124590 1.417168 c +16.219671 0.442865 15.118032 0.000000 13.829508 0.000000 c +11.016394 0.000000 7.603279 2.155275 4.868853 4.891197 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2078 +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 +0000002168 00000 n +0000002191 00000 n +0000002364 00000 n +0000002438 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2497 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/Contents.json new file mode 100644 index 0000000000..a29b340f30 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "down.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/down.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/down.pdf new file mode 100644 index 0000000000..1ed35a6dfa --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedIn.imageset/down.pdf @@ -0,0 +1,159 @@ +%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 5.500061 11.394928 cm +0.000000 0.000000 0.000000 scn +-0.470226 1.865154 m +-0.729925 1.605455 -0.729925 1.184401 -0.470226 0.924702 c +-0.210527 0.665003 0.210527 0.665003 0.470226 0.924702 c +-0.470226 1.865154 l +h +3.500000 4.894928 m +3.970228 5.365152 l +3.845517 5.489864 3.676371 5.559927 3.500001 5.559928 c +3.323632 5.559928 3.154486 5.489866 3.029774 5.365154 c +3.500000 4.894928 l +h +6.529741 0.924704 m +6.789439 0.665004 7.210494 0.665002 7.470193 0.924700 c +7.729893 1.184397 7.729895 1.605452 7.470198 1.865152 c +6.529741 0.924704 l +h +0.470226 0.924702 m +3.970226 4.424702 l +3.029774 5.365154 l +-0.470226 1.865154 l +0.470226 0.924702 l +h +3.029772 4.424704 m +6.529741 0.924704 l +7.470198 1.865152 l +3.970228 5.365152 l +3.029772 4.424704 l +h +f +n +Q +q +-1.000000 -0.000000 0.000000 -1.000000 9.000031 16.825317 cm +0.000000 0.000000 0.000000 scn +-0.665000 1.330017 m +-0.665000 0.962748 -0.367269 0.665017 0.000000 0.665017 c +0.367269 0.665017 0.665000 0.962748 0.665000 1.330017 c +-0.665000 1.330017 l +h +0.665000 9.330017 m +0.665000 9.697287 0.367269 9.995017 -0.000000 9.995017 c +-0.367269 9.995017 -0.665000 9.697287 -0.665000 9.330017 c +0.665000 9.330017 l +h +0.665000 1.330017 m +0.665000 9.330017 l +-0.665000 9.330017 l +-0.665000 1.330017 l +0.665000 1.330017 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.000000 1.665283 cm +0.000000 0.000000 0.000000 scn +0.000000 1.995017 m +-0.367269 1.995017 -0.665000 1.697286 -0.665000 1.330017 c +-0.665000 0.962748 -0.367269 0.665017 0.000000 0.665017 c +0.000000 1.995017 l +h +10.000000 0.665017 m +10.367270 0.665017 10.665000 0.962748 10.665000 1.330017 c +10.665000 1.697286 10.367270 1.995017 10.000000 1.995017 c +10.000000 0.665017 l +h +0.000000 0.665017 m +10.000000 0.665017 l +10.000000 1.995017 l +0.000000 1.995017 l +0.000000 0.665017 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.000000 1.665283 cm +0.000000 0.000000 0.000000 scn +0.000000 1.995017 m +-0.367269 1.995017 -0.665000 1.697286 -0.665000 1.330017 c +-0.665000 0.962748 -0.367269 0.665017 0.000000 0.665017 c +0.000000 1.995017 l +h +10.000000 0.665017 m +10.367270 0.665017 10.665000 0.962748 10.665000 1.330017 c +10.665000 1.697286 10.367270 1.995017 10.000000 1.995017 c +10.000000 0.665017 l +h +0.000000 0.665017 m +10.000000 0.665017 l +10.000000 1.995017 l +0.000000 1.995017 l +0.000000 0.665017 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 2383 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 18.000000 18.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 +0000002473 00000 n +0000002496 00000 n +0000002669 00000 n +0000002743 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2802 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/Contents.json new file mode 100644 index 0000000000..38a468f89e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "up.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/up.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/up.pdf new file mode 100644 index 0000000000..43438d30e3 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataExpandedOut.imageset/up.pdf @@ -0,0 +1,207 @@ +%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 12.500031 10.598022 cm +0.000000 0.000000 0.000000 scn +-0.470226 1.865154 m +-0.729925 1.605455 -0.729925 1.184401 -0.470226 0.924702 c +-0.210527 0.665003 0.210527 0.665003 0.470226 0.924702 c +-0.470226 1.865154 l +h +3.500000 4.894928 m +3.970228 5.365152 l +3.845517 5.489864 3.676371 5.559927 3.500001 5.559928 c +3.323632 5.559928 3.154486 5.489866 3.029774 5.365154 c +3.500000 4.894928 l +h +6.529741 0.924704 m +6.789439 0.665004 7.210494 0.665002 7.470193 0.924700 c +7.729893 1.184397 7.729895 1.605452 7.470198 1.865152 c +6.529741 0.924704 l +h +0.470226 0.924702 m +3.970226 4.424702 l +3.029774 5.365154 l +-0.470226 1.865154 l +0.470226 0.924702 l +h +3.029772 4.424704 m +6.529741 0.924704 l +7.470198 1.865152 l +3.970228 5.365152 l +3.029772 4.424704 l +h +f +n +Q +q +-1.000000 -0.000000 0.000000 -1.000000 9.000031 15.822968 cm +0.000000 0.000000 0.000000 scn +-0.665000 1.330017 m +-0.665000 0.962748 -0.367269 0.665017 0.000000 0.665017 c +0.367269 0.665017 0.665000 0.962748 0.665000 1.330017 c +-0.665000 1.330017 l +h +0.665000 9.330017 m +0.665000 9.697287 0.367269 9.995017 -0.000000 9.995017 c +-0.367269 9.995017 -0.665000 9.697287 -0.665000 9.330017 c +0.665000 9.330017 l +h +0.665000 1.330017 m +0.665000 9.330017 l +-0.665000 9.330017 l +-0.665000 1.330017 l +0.665000 1.330017 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.000000 1.662933 cm +0.000000 0.000000 0.000000 scn +0.665000 3.330017 m +0.665000 3.697286 0.367269 3.995017 0.000000 3.995017 c +-0.367269 3.995017 -0.665000 3.697286 -0.665000 3.330017 c +0.665000 3.330017 l +h +10.665000 3.330017 m +10.665000 3.697286 10.367270 3.995017 10.000000 3.995017 c +9.632730 3.995017 9.335000 3.697286 9.335000 3.330017 c +10.665000 3.330017 l +h +1.000000 0.665017 m +9.000000 0.665017 l +9.000000 1.995017 l +1.000000 1.995017 l +1.000000 0.665017 l +h +0.665000 2.330017 m +0.665000 3.330017 l +-0.665000 3.330017 l +-0.665000 2.330017 l +0.665000 2.330017 l +h +10.665000 2.330017 m +10.665000 3.330017 l +9.335000 3.330017 l +9.335000 2.330017 l +10.665000 2.330017 l +h +9.000000 0.665017 m +9.919554 0.665017 10.665000 1.410463 10.665000 2.330017 c +9.335000 2.330017 l +9.335000 2.145002 9.185016 1.995017 9.000000 1.995017 c +9.000000 0.665017 l +h +1.000000 1.995017 m +0.814985 1.995017 0.665000 2.145002 0.665000 2.330017 c +-0.665000 2.330017 l +-0.665000 1.410463 0.080446 0.665017 1.000000 0.665017 c +1.000000 1.995017 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.000000 1.662933 cm +0.000000 0.000000 0.000000 scn +0.665000 3.330017 m +0.665000 3.697286 0.367269 3.995017 0.000000 3.995017 c +-0.367269 3.995017 -0.665000 3.697286 -0.665000 3.330017 c +0.665000 3.330017 l +h +10.665000 3.330017 m +10.665000 3.697286 10.367270 3.995017 10.000000 3.995017 c +9.632730 3.995017 9.335000 3.697286 9.335000 3.330017 c +10.665000 3.330017 l +h +1.000000 0.665017 m +9.000000 0.665017 l +9.000000 1.995017 l +1.000000 1.995017 l +1.000000 0.665017 l +h +0.665000 2.330017 m +0.665000 3.330017 l +-0.665000 3.330017 l +-0.665000 2.330017 l +0.665000 2.330017 l +h +10.665000 2.330017 m +10.665000 3.330017 l +9.335000 3.330017 l +9.335000 2.330017 l +10.665000 2.330017 l +h +9.000000 0.665017 m +9.919554 0.665017 10.665000 1.410463 10.665000 2.330017 c +9.335000 2.330017 l +9.335000 2.145002 9.185016 1.995017 9.000000 1.995017 c +9.000000 0.665017 l +h +1.000000 1.995017 m +0.814985 1.995017 0.665000 2.145002 0.665000 2.330017 c +-0.665000 2.330017 l +-0.665000 1.410463 0.080446 0.665017 1.000000 0.665017 c +1.000000 1.995017 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 3492 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 18.000000 18.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 +0000003582 00000 n +0000003605 00000 n +0000003778 00000 n +0000003852 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3911 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/Contents.json new file mode 100644 index 0000000000..bcb4f755f5 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "files.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/files.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/files.pdf new file mode 100644 index 0000000000..411b272cd9 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataFiles.imageset/files.pdf @@ -0,0 +1,143 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.203922 0.780392 0.349020 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 7.000000 6.000000 cm +1.000000 1.000000 1.000000 scn +5.330000 18.000000 m +5.807298 18.000000 6.123703 17.999550 6.367812 17.982895 c +6.604466 17.966749 6.711959 17.938187 6.777740 17.910938 c +7.064423 17.792191 7.292191 17.564423 7.410939 17.277740 c +7.438186 17.211960 7.466748 17.104465 7.482895 16.867813 c +7.499550 16.623703 7.500000 16.307299 7.500000 15.830000 c +7.500000 13.530003 l +7.500000 13.496872 l +7.499987 12.965037 7.499976 12.516354 7.530005 12.148819 c +7.561447 11.763987 7.629904 11.395631 7.808452 11.045210 c +8.079773 10.512712 8.512709 10.079777 9.045207 9.808455 c +9.395627 9.629908 9.763983 9.561451 10.148815 9.530008 c +10.516351 9.499980 10.965034 9.499990 11.496870 9.500004 c +11.530001 9.500004 l +13.829997 9.500004 l +14.307296 9.500004 14.623703 9.499555 14.867812 9.482899 c +15.104466 9.466752 15.211960 9.438190 15.277740 9.410942 c +15.564423 9.292194 15.792191 9.064426 15.910938 8.777744 c +15.938186 8.711964 15.966748 8.604470 15.982895 8.367816 c +15.999551 8.123706 16.000000 7.807300 16.000000 7.330000 c +16.000000 4.800000 l +16.000000 3.119843 16.000000 2.279763 15.673019 1.638029 c +15.385400 1.073542 14.926457 0.614601 14.361972 0.326981 c +13.720237 0.000000 12.880157 0.000000 11.200000 0.000000 c +4.800000 0.000000 l +3.119843 0.000000 2.279764 0.000000 1.638029 0.326981 c +1.073542 0.614601 0.614601 1.073542 0.326980 1.638029 c +0.000000 2.279763 0.000000 3.119843 0.000000 4.800000 c +0.000000 13.200001 l +0.000000 14.880157 0.000000 15.720236 0.326980 16.361971 c +0.614601 16.926458 1.073542 17.385399 1.638029 17.673019 c +2.279764 18.000000 3.119843 18.000000 4.800000 18.000000 c +5.330000 18.000000 l +h +15.621112 11.042418 m +15.558301 11.176268 15.488032 11.306717 15.410561 11.433140 c +15.163194 11.836806 14.817290 12.182710 14.125484 12.874516 c +10.874516 16.125484 l +10.182710 16.817291 9.836806 17.163193 9.433140 17.410561 c +9.306716 17.488033 9.176266 17.558304 9.042415 17.621115 c +9.097363 17.412268 9.124138 17.199299 9.139045 16.980810 c +9.160017 16.673437 9.160009 16.300030 9.160000 15.857494 c +9.160000 15.830000 l +9.160000 13.530003 l +9.160000 12.956255 9.160645 12.575861 9.184492 12.283996 c +9.207546 12.001820 9.248083 11.876238 9.287522 11.798835 c +9.399694 11.578686 9.578682 11.399698 9.798831 11.287526 c +9.876234 11.248087 10.001816 11.207550 10.283993 11.184496 c +10.575858 11.160649 10.956251 11.160004 11.530001 11.160004 c +13.829997 11.160004 l +13.857491 11.160004 l +14.300028 11.160013 14.673436 11.160021 14.980811 11.139048 c +15.199297 11.124141 15.412265 11.097366 15.621112 11.042418 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3553 +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 +0000003643 00000 n +0000003666 00000 n +0000003839 00000 n +0000003913 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3972 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/Contents.json new file mode 100644 index 0000000000..460e89a7c8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "datain.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/datain.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/datain.pdf new file mode 100644 index 0000000000..a24634ff89 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataIn.imageset/datain.pdf @@ -0,0 +1,148 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.203922 0.780392 0.349020 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +0.000000 1.000000 -1.000000 0.000000 22.778702 10.334999 cm +1.000000 1.000000 1.000000 scn +0.341218 6.954933 m +-0.113739 7.409894 -0.113739 8.147528 0.341218 8.602488 c +5.841218 14.102518 l +6.296178 14.557480 7.033815 14.557482 7.488777 14.102523 c +7.943739 13.647563 7.943741 12.909925 7.488781 12.454964 c +3.977601 8.943764 l +13.165000 8.943764 l +13.808412 8.943764 14.330000 8.422175 14.330000 7.778764 c +14.330000 7.135352 13.808412 6.613764 13.165000 6.613764 c +3.977494 6.613764 l +7.488781 3.102457 l +7.943741 2.647495 7.943739 1.909858 7.488777 1.454898 c +7.033815 0.999939 6.296178 0.999941 5.841218 1.454903 c +0.341218 6.954933 l +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.000000 4.669998 cm +0.000000 0.000000 0.000000 scn +0.000000 3.495002 m +-0.643412 3.495002 -1.165000 2.973413 -1.165000 2.330002 c +-1.165000 1.686590 -0.643412 1.165002 0.000000 1.165002 c +0.000000 3.495002 l +h +14.000000 1.165002 m +14.643412 1.165002 15.165000 1.686590 15.165000 2.330002 c +15.165000 2.973413 14.643412 3.495002 14.000000 3.495002 c +14.000000 1.165002 l +h +0.000000 1.165002 m +14.000000 1.165002 l +14.000000 3.495002 l +0.000000 3.495002 l +0.000000 1.165002 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.000000 4.669998 cm +1.000000 1.000000 1.000000 scn +0.000000 3.495002 m +-0.643412 3.495002 -1.165000 2.973413 -1.165000 2.330002 c +-1.165000 1.686590 -0.643412 1.165002 0.000000 1.165002 c +0.000000 3.495002 l +h +14.000000 1.165002 m +14.643412 1.165002 15.165000 1.686590 15.165000 2.330002 c +15.165000 2.973413 14.643412 3.495002 14.000000 3.495002 c +14.000000 1.165002 l +h +0.000000 1.165002 m +14.000000 1.165002 l +14.000000 3.495002 l +0.000000 3.495002 l +0.000000 1.165002 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 2638 +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 +0000002728 00000 n +0000002751 00000 n +0000002924 00000 n +0000002998 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3057 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/Contents.json new file mode 100644 index 0000000000..ad55630238 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "messages.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/messages.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/messages.pdf new file mode 100644 index 0000000000..d6e500efdc --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMessages.imageset/messages.pdf @@ -0,0 +1,100 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.345098 0.337255 0.839216 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 5.000000 4.484375 cm +1.000000 1.000000 1.000000 scn +20.000000 11.044711 m +20.000000 16.065481 15.522848 20.135620 10.000000 20.135620 c +4.477152 20.135620 0.000000 16.065481 0.000000 11.044711 c +0.000000 8.181209 1.337573 5.834022 3.613619 4.167677 c +3.904685 3.954580 4.172771 2.770550 3.523984 1.775995 c +2.875197 0.781441 2.066323 0.326941 2.471971 0.156790 c +2.722059 0.051889 4.199766 -0.000002 5.266314 0.598131 c +6.791368 1.453400 7.217727 2.304844 7.545889 2.229574 c +8.331102 2.049473 9.153261 1.953802 10.000000 1.953802 c +15.522848 1.953802 20.000000 6.023941 20.000000 11.044711 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 1584 +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 +0000001674 00000 n +0000001697 00000 n +0000001870 00000 n +0000001944 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2003 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/Contents.json new file mode 100644 index 0000000000..27336116c3 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "music.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/music.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/music.pdf new file mode 100644 index 0000000000..b8aa88f33b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataMusic.imageset/music.pdf @@ -0,0 +1,103 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 0.176471 0.333333 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm +1.000000 1.000000 1.000000 scn +10.000000 0.000000 m +15.522847 0.000000 20.000000 4.477153 20.000000 10.000000 c +20.000000 15.522848 15.522847 20.000000 10.000000 20.000000 c +4.477152 20.000000 0.000000 15.522848 0.000000 10.000000 c +0.000000 4.477153 4.477152 0.000000 10.000000 0.000000 c +h +14.751925 9.167950 m +8.554701 5.036467 l +7.890146 4.593431 7.000000 5.069821 7.000000 5.868517 c +7.000000 14.131483 l +7.000000 14.930179 7.890145 15.406569 8.554700 14.963533 c +14.751925 10.832050 l +15.345657 10.436228 15.345657 9.563771 14.751925 9.167950 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1564 +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 +0000001654 00000 n +0000001677 00000 n +0000001850 00000 n +0000001924 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1983 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/Contents.json new file mode 100644 index 0000000000..23d6c66b3c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "dataout.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/dataout.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/dataout.pdf new file mode 100644 index 0000000000..248d972dc0 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataOut.imageset/dataout.pdf @@ -0,0 +1,197 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.478431 1.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +0.000000 -1.000000 1.000000 0.000000 7.335037 24.665001 cm +1.000000 1.000000 1.000000 scn +0.341223 6.841220 m +0.122742 7.059700 0.000000 7.356022 0.000000 7.665000 c +0.000000 7.973978 0.122740 8.270301 0.341221 8.488781 c +5.841220 13.988781 l +6.296181 14.443742 7.033819 14.443742 7.488780 13.988781 c +7.943740 13.533820 7.943740 12.796184 7.488780 12.341223 c +3.977603 8.830046 l +13.165001 8.830046 l +13.808413 8.830046 14.330001 8.308458 14.330001 7.665046 c +14.330001 7.021635 13.808413 6.500046 13.165001 6.500046 c +3.977525 6.500046 l +7.488777 2.988814 l +7.943739 2.533854 7.943741 1.796217 7.488781 1.341255 c +7.033822 0.886293 6.296185 0.886292 5.841223 1.341250 c +0.341223 6.841220 l +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.000000 4.669998 cm +0.000000 0.000000 0.000000 scn +1.165000 4.330002 m +1.165000 4.973413 0.643412 5.495002 0.000000 5.495002 c +-0.643412 5.495002 -1.165000 4.973413 -1.165000 4.330002 c +1.165000 4.330002 l +h +15.165000 4.330002 m +15.165000 4.973413 14.643412 5.495002 14.000000 5.495002 c +13.356588 5.495002 12.835000 4.973413 12.835000 4.330002 c +15.165000 4.330002 l +h +1.000000 1.165002 m +13.000000 1.165002 l +13.000000 3.495002 l +1.000000 3.495002 l +1.000000 1.165002 l +h +1.165000 3.330002 m +1.165000 4.330002 l +-1.165000 4.330002 l +-1.165000 3.330002 l +1.165000 3.330002 l +h +15.165000 3.330002 m +15.165000 4.330002 l +12.835000 4.330002 l +12.835000 3.330002 l +15.165000 3.330002 l +h +13.000000 1.165002 m +14.195696 1.165002 15.165000 2.134305 15.165000 3.330002 c +12.835000 3.330002 l +12.835000 3.421129 12.908874 3.495002 13.000000 3.495002 c +13.000000 1.165002 l +h +1.000000 3.495002 m +1.091127 3.495002 1.165000 3.421129 1.165000 3.330002 c +-1.165000 3.330002 l +-1.165000 2.134305 -0.195696 1.165002 1.000000 1.165002 c +1.000000 3.495002 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.000000 4.669998 cm +1.000000 1.000000 1.000000 scn +1.165000 4.330002 m +1.165000 4.973413 0.643412 5.495002 0.000000 5.495002 c +-0.643412 5.495002 -1.165000 4.973413 -1.165000 4.330002 c +1.165000 4.330002 l +h +15.165000 4.330002 m +15.165000 4.973413 14.643412 5.495002 14.000000 5.495002 c +13.356588 5.495002 12.835000 4.973413 12.835000 4.330002 c +15.165000 4.330002 l +h +1.000000 1.165002 m +13.000000 1.165002 l +13.000000 3.495002 l +1.000000 3.495002 l +1.000000 1.165002 l +h +1.165000 3.330002 m +1.165000 4.330002 l +-1.165000 4.330002 l +-1.165000 3.330002 l +1.165000 3.330002 l +h +15.165000 3.330002 m +15.165000 4.330002 l +12.835000 4.330002 l +12.835000 3.330002 l +15.165000 3.330002 l +h +13.000000 1.165002 m +14.195696 1.165002 15.165000 2.134305 15.165000 3.330002 c +12.835000 3.330002 l +12.835000 3.421129 12.908874 3.495002 13.000000 3.495002 c +13.000000 1.165002 l +h +1.000000 3.495002 m +1.091127 3.495002 1.165000 3.421129 1.165000 3.330002 c +-1.165000 3.330002 l +-1.165000 2.134305 -0.195696 1.165002 1.000000 1.165002 c +1.000000 3.495002 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 3829 +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 +0000003919 00000 n +0000003942 00000 n +0000004115 00000 n +0000004189 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4248 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/Contents.json new file mode 100644 index 0000000000..1020ec45a8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "photos.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/photos.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/photos.pdf new file mode 100644 index 0000000000..c57bbf11d1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataPhotos.imageset/photos.pdf @@ -0,0 +1,155 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.352941 0.784314 0.980392 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm +1.000000 1.000000 1.000000 scn +0.326980 16.361971 m +0.000000 15.720236 0.000000 14.880157 0.000000 13.200001 c +0.000000 7.873625 l +2.719312 10.802114 l +2.739366 10.823713 l +2.739403 10.823753 l +2.739418 10.823770 l +2.925786 11.024508 3.099065 11.211145 3.256842 11.354792 c +3.426955 11.509670 3.635908 11.669102 3.908790 11.762256 c +4.292071 11.893097 4.707929 11.893097 5.091210 11.762256 c +5.364092 11.669102 5.573045 11.509670 5.743158 11.354791 c +5.900949 11.211132 6.074247 11.024471 6.260634 10.823713 c +6.280688 10.802114 l +10.392858 6.373626 l +11.719312 7.802115 l +11.739367 7.823714 l +11.925754 8.024473 12.099051 8.211132 12.256842 8.354791 c +12.426956 8.509670 12.635908 8.669102 12.908790 8.762256 c +13.292071 8.893097 13.707929 8.893097 14.091210 8.762256 c +14.364092 8.669102 14.573045 8.509670 14.743158 8.354791 c +14.900943 8.211139 15.074232 8.024488 15.260609 7.823739 c +15.260634 7.823713 l +15.280688 7.802114 l +18.000000 4.873625 l +18.000000 13.200000 l +18.000000 14.880157 18.000000 15.720236 17.673019 16.361971 c +17.385399 16.926458 16.926458 17.385399 16.361971 17.673019 c +15.720237 18.000000 14.880157 18.000000 13.200001 18.000000 c +4.800000 18.000000 l +3.119843 18.000000 2.279764 18.000000 1.638029 17.673019 c +1.073542 17.385399 0.614601 16.926458 0.326980 16.361971 c +h +16.108553 0.218262 m +11.525509 5.153847 l +12.935749 6.672566 l +13.149095 6.902323 13.274549 7.036421 13.374386 7.127316 c +13.419220 7.168135 13.445271 7.187207 13.456385 7.194696 c +13.484964 7.202284 13.515036 7.202284 13.543615 7.194696 c +13.554729 7.187207 13.580781 7.168135 13.625614 7.127316 c +13.725451 7.036421 13.850905 6.902323 14.064251 6.672565 c +17.925373 2.514434 l +17.877884 2.167143 17.800177 1.887589 17.673019 1.638029 c +17.385399 1.073542 16.926458 0.614601 16.361971 0.326981 c +16.281078 0.285763 16.197035 0.249743 16.108553 0.218262 c +h +14.044044 0.002020 m +5.064251 9.672565 l +4.850905 9.902323 4.725451 10.036421 4.625614 10.127316 c +4.580781 10.168135 4.554729 10.187207 4.543615 10.194696 c +4.515036 10.202284 4.484964 10.202284 4.456385 10.194696 c +4.445271 10.187207 4.419219 10.168135 4.374386 10.127316 c +4.274549 10.036421 4.149095 9.902323 3.935749 9.672565 c +0.000000 5.434067 l +0.000000 4.800000 l +0.000000 3.119843 0.000000 2.279763 0.326980 1.638029 c +0.614601 1.073542 1.073542 0.614601 1.638029 0.326981 c +2.279764 0.000000 3.119842 0.000000 4.800000 0.000000 c +13.200000 0.000000 l +13.508314 0.000000 13.788341 0.000000 14.044044 0.002020 c +h +13.500000 12.000000 m +14.328427 12.000000 15.000000 12.671573 15.000000 13.500000 c +15.000000 14.328427 14.328427 15.000000 13.500000 15.000000 c +12.671573 15.000000 12.000000 14.328427 12.000000 13.500000 c +12.000000 12.671573 12.671573 12.000000 13.500000 12.000000 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3765 +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 +0000003855 00000 n +0000003878 00000 n +0000004051 00000 n +0000004125 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4184 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/Contents.json new file mode 100644 index 0000000000..f0f8dc6c02 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "stickers.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/stickers.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/stickers.pdf new file mode 100644 index 0000000000..4f5e85bef0 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataStickers.imageset/stickers.pdf @@ -0,0 +1,173 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 0.800000 0.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm +1.000000 1.000000 1.000000 scn +0.435974 15.815962 m +0.000000 14.960315 0.000000 13.840210 0.000000 11.600000 c +0.000000 5.538462 l +0.000000 4.106961 0.000000 3.391212 0.181116 2.809988 c +0.572199 1.554960 1.554961 0.572199 2.809988 0.181116 c +3.391211 0.000000 4.106961 0.000000 5.538461 0.000000 c +7.132836 0.000000 8.332593 0.000000 9.296358 0.049431 c +9.392109 0.065231 9.458178 0.084789 9.510882 0.106621 c +9.837995 0.242115 10.097885 0.502007 10.233379 0.829117 c +10.269915 0.917324 10.300633 1.046162 10.317467 1.296604 c +10.334650 1.552246 10.335000 1.881901 10.335000 2.365276 c +10.335000 2.390583 l +10.334996 3.020454 10.334994 3.541773 10.356327 3.976810 c +9.927901 3.881188 9.474939 3.834996 8.999999 3.834996 c +7.566772 3.834998 6.432307 4.408777 5.671366 4.962191 c +5.289614 5.239828 4.994541 5.517408 4.793315 5.727383 c +4.692429 5.832656 4.614337 5.921766 4.559963 5.986553 c +4.532754 6.018972 4.511416 6.045382 4.496066 6.064772 c +4.477521 6.088497 l +4.471629 6.096197 l +4.469531 6.098966 l +4.468694 6.100076 l +4.468329 6.100561 l +4.468162 6.100784 4.468000 6.101000 5.000000 6.500000 c +5.532000 6.899000 l +5.531705 6.899393 l +5.538846 6.890306 l +5.546690 6.880400 5.560019 6.863840 5.578709 6.841572 c +5.616131 6.796984 5.674759 6.729844 5.753560 6.647617 c +5.911709 6.482592 6.147886 6.260172 6.453634 6.037808 c +7.067693 5.591221 7.933228 5.164998 9.000001 5.164996 c +9.630135 5.164996 10.171014 5.264124 10.633532 5.448587 c +10.639013 5.461485 10.644600 5.474353 10.650297 5.487185 c +11.018882 6.317377 11.682623 6.981118 12.512815 7.349703 c +12.904016 7.523386 13.325486 7.596285 13.811561 7.631045 c +14.286482 7.665009 14.873948 7.665005 15.609417 7.665000 c +15.634724 7.665000 l +16.118099 7.665000 16.447754 7.665350 16.703396 7.682533 c +16.953838 7.699367 17.082676 7.730085 17.170883 7.766621 c +17.497993 7.902115 17.757885 8.162005 17.893379 8.489118 c +17.915211 8.541822 17.934769 8.607892 17.950569 8.703643 c +18.000000 9.667408 18.000000 10.867164 18.000000 12.461538 c +18.000000 13.893039 18.000000 14.608788 17.818884 15.190012 c +17.427801 16.445040 16.445040 17.427801 15.190012 17.818884 c +14.608788 18.000000 13.893039 18.000000 12.461538 18.000000 c +6.400000 18.000000 l +4.159790 18.000000 3.039685 18.000000 2.184038 17.564026 c +1.431390 17.180532 0.819467 16.568611 0.435974 15.815962 c +h +17.652130 6.526627 m +17.381193 6.419186 17.097967 6.376053 16.792589 6.355527 c +16.487034 6.334990 16.112497 6.334994 15.657384 6.335000 c +15.634724 6.335000 l +14.868204 6.335000 14.328320 6.334603 13.906430 6.304433 c +13.490259 6.274672 13.242615 6.218527 13.052504 6.134122 c +12.523582 5.899294 12.100706 5.476418 11.865878 4.947496 c +11.781473 4.757385 11.725328 4.509741 11.695567 4.093570 c +11.665397 3.671680 11.665000 3.131796 11.665000 2.365276 c +11.665000 2.342616 l +11.665006 1.887503 11.665010 1.512966 11.644473 1.207411 c +11.623947 0.902033 11.580814 0.618807 11.473373 0.347870 c +11.542342 0.366713 11.610334 0.386574 11.677527 0.407513 c +14.501338 1.287447 16.712553 3.498662 17.592487 6.322473 c +17.613426 6.389666 17.633287 6.457658 17.652130 6.526627 c +h +5.531581 6.899558 m +5.311111 7.192892 4.894629 7.252222 4.601000 7.032000 c +4.307185 6.811639 4.247638 6.394815 4.468000 6.101000 c +5.000000 6.500000 l +5.532000 6.899000 5.531850 6.899200 5.531705 6.899393 c +5.531581 6.899558 l +h +7.500000 11.187500 m +7.500000 10.324555 6.940356 9.625000 6.250000 9.625000 c +5.559644 9.625000 5.000000 10.324555 5.000000 11.187500 c +5.000000 12.050446 5.559644 12.750000 6.250000 12.750000 c +6.940356 12.750000 7.500000 12.050446 7.500000 11.187500 c +h +13.000000 11.187500 m +13.000000 10.324555 12.440355 9.625000 11.750000 9.625000 c +11.059645 9.625000 10.500000 10.324555 10.500000 11.187500 c +10.500000 12.050446 11.059645 12.750000 11.750000 12.750000 c +12.440355 12.750000 13.000000 12.050446 13.000000 11.187500 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 4865 +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 +0000004955 00000 n +0000004978 00000 n +0000005151 00000 n +0000005225 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +5284 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/Contents.json new file mode 100644 index 0000000000..c007a435f4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "video.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/video.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/video.pdf new file mode 100644 index 0000000000..15497bbab8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVideo.imageset/video.pdf @@ -0,0 +1,123 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.478431 1.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 5.000000 8.000000 cm +1.000000 1.000000 1.000000 scn +0.435974 11.815962 m +0.000000 10.960315 0.000000 9.840210 0.000000 7.599999 c +0.000000 6.400000 l +0.000000 4.159790 0.000000 3.039685 0.435974 2.184038 c +0.819467 1.431390 1.431390 0.819468 2.184038 0.435974 c +3.039685 0.000000 4.159790 0.000000 6.400001 0.000000 c +7.600000 0.000000 l +9.840210 0.000000 10.960316 0.000000 11.815962 0.435974 c +12.568610 0.819468 13.180532 1.431390 13.564026 2.184038 c +14.000000 3.039685 14.000000 4.159790 14.000000 6.400001 c +14.000000 7.600000 l +14.000000 9.840210 14.000000 10.960315 13.564026 11.815962 c +13.180532 12.568610 12.568610 13.180532 11.815962 13.564026 c +10.960316 14.000000 9.840210 14.000000 7.599999 14.000000 c +6.400000 14.000000 l +4.159790 14.000000 3.039685 14.000000 2.184038 13.564026 c +1.431390 13.180532 0.819467 12.568610 0.435974 11.815962 c +h +20.495096 10.957284 m +20.443178 11.542397 19.985497 12.000000 19.428572 12.000000 c +19.196747 12.000000 18.971174 11.919026 18.785715 11.769232 c +16.071428 9.000020 l +15.977040 8.916782 l +15.674964 8.626339 15.500000 8.209476 15.500000 7.769259 c +15.500000 6.230808 l +15.505201 6.099547 l +15.539715 5.665031 15.744408 5.264177 16.071428 5.000048 c +18.785715 2.230835 l +18.871206 2.168387 l +19.337015 1.862048 19.951559 1.981793 20.285713 2.461603 c +20.424809 2.661328 20.500000 2.904249 20.500000 3.153906 c +20.500000 10.846162 l +20.495096 10.957284 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2403 +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 +0000002493 00000 n +0000002516 00000 n +0000002689 00000 n +0000002763 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2822 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/Contents.json new file mode 100644 index 0000000000..fca5e3d85c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/voice.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/voice.pdf new file mode 100644 index 0000000000..d0abd6649a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DataVoice.imageset/voice.pdf @@ -0,0 +1,156 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.686275 0.321569 0.870588 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 11.000000 12.000000 cm +1.000000 1.000000 1.000000 scn +0.000000 10.000000 m +0.000000 12.209139 1.790861 14.000000 4.000000 14.000000 c +4.000000 14.000000 l +6.209139 14.000000 8.000000 12.209139 8.000000 10.000000 c +8.000000 4.000000 l +8.000000 1.790861 6.209139 0.000000 4.000000 0.000000 c +4.000000 0.000000 l +1.790861 0.000000 0.000000 1.790861 0.000000 4.000000 c +0.000000 10.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.000000 7.000000 cm +1.000000 1.000000 1.000000 scn +1.000000 9.500000 m +1.000000 10.052285 0.552285 10.500000 0.000000 10.500000 c +-0.552285 10.500000 -1.000000 10.052285 -1.000000 9.500000 c +1.000000 9.500000 l +h +15.000000 9.500000 m +15.000000 10.052285 14.552285 10.500000 14.000000 10.500000 c +13.447716 10.500000 13.000000 10.052285 13.000000 9.500000 c +15.000000 9.500000 l +h +-1.000000 9.500000 m +-1.000000 9.000000 l +1.000000 9.000000 l +1.000000 9.500000 l +-1.000000 9.500000 l +h +15.000000 9.000000 m +15.000000 9.500000 l +13.000000 9.500000 l +13.000000 9.000000 l +15.000000 9.000000 l +h +7.000000 1.000000 m +11.418278 1.000000 15.000000 4.581722 15.000000 9.000000 c +13.000000 9.000000 l +13.000000 5.686292 10.313708 3.000000 7.000000 3.000000 c +7.000000 1.000000 l +h +-1.000000 9.000000 m +-1.000000 4.581722 2.581722 1.000000 7.000000 1.000000 c +7.000000 3.000000 l +3.686292 3.000000 1.000000 5.686292 1.000000 9.000000 c +-1.000000 9.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 15.000000 4.000000 cm +1.000000 1.000000 1.000000 scn +-1.000000 1.000000 m +-1.000000 0.447715 -0.552285 0.000000 0.000000 0.000000 c +0.552285 0.000000 1.000000 0.447715 1.000000 1.000000 c +-1.000000 1.000000 l +h +-1.000000 5.000000 m +-1.000000 1.000000 l +1.000000 1.000000 l +1.000000 5.000000 l +-1.000000 5.000000 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 2734 +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 +0000002824 00000 n +0000002847 00000 n +0000003020 00000 n +0000003094 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3153 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 92db2f7930..ea011b83aa 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -911,7 +911,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) ?? false } : nil, sendEmoji: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { text, attribute in - self?.controllerInteraction?.sendEmoji(text, attribute) + self?.controllerInteraction?.sendEmoji(text, attribute, false) } : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in if let strongSelf = self { strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in @@ -2032,28 +2032,43 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.sendMessages(transformedMessages) } return true - }, sendEmoji: { [weak self] text, attribute in + }, sendEmoji: { [weak self] text, attribute, immediately in if let strongSelf = self { - strongSelf.interfaceInteraction?.insertText(NSAttributedString(string: text, attributes: [ChatTextInputAttributes.customEmoji: attribute])) - strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in - return state.updatedInputMode({ _ in - return .text - }) - }) - - let _ = (ApplicationSpecificNotice.getEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager) - |> deliverOnMainQueue).start(next: { count in - guard let strongSelf = self else { - return + if immediately { + if let file = attribute.file { + var bubbleUpEmojiOrStickersets: [ItemCollectionId] = [] + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + if case let .id(id, _) = packReference { + bubbleUpEmojiOrStickersets.append(ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id)) + } + } + } + + strongSelf.sendMessages([.message(text: text, attributes: [TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< (text as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: file.fileId.id))])], inlineStickers: [file.fileId : file], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)], commit: false) } - if count < 2 { - let _ = ApplicationSpecificNotice.incrementEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager).start() - - Queue.mainQueue().after(0.5, { - strongSelf.displayEmojiTooltip() + } else { + strongSelf.interfaceInteraction?.insertText(NSAttributedString(string: text, attributes: [ChatTextInputAttributes.customEmoji: attribute])) + strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in + return state.updatedInputMode({ _ in + return .text }) - } - }) + }) + + let _ = (ApplicationSpecificNotice.getEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { count in + guard let strongSelf = self else { + return + } + if count < 2 { + let _ = ApplicationSpecificNotice.incrementEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager).start() + + Queue.mainQueue().after(0.5, { + strongSelf.displayEmojiTooltip() + }) + } + }) + } } }, sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in if let strongSelf = self { @@ -10261,11 +10276,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if self.presentationInterfaceState.interfaceState.mediaRecordingMode == .audio { var canSendMedia = false if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel { - if channel.hasBannedPermission(.banSendMedia) == nil { + if channel.hasBannedPermission(.banSendMedia) == nil && channel.hasBannedPermission(.banSendVoice) == nil { canSendMedia = true } } else if let group = self.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup { - if !group.hasBannedPermission(.banSendMedia) { + if !group.hasBannedPermission(.banSendMedia) && !group.hasBannedPermission(.banSendVoice) { canSendMedia = true } } else { @@ -11932,22 +11947,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.dismissInput() - var bannedSendMedia: (Int32, Bool)? + var bannedSendPhotos: (Int32, Bool)? + var bannedSendVideos: (Int32, Bool)? + var bannedSendFiles: (Int32, Bool)? + var canSendPolls = true if let peer = peer as? TelegramUser, peer.botInfo == nil { canSendPolls = false } else if peer is TelegramSecretChat { canSendPolls = false } else if let channel = peer as? TelegramChannel { - if let value = channel.hasBannedPermission(.banSendMedia) { - bannedSendMedia = value + if let value = channel.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = value + } + if let value = channel.hasBannedPermission(.banSendVideos) { + bannedSendVideos = value + } + if let value = channel.hasBannedPermission(.banSendFiles) { + bannedSendFiles = value } if channel.hasBannedPermission(.banSendPolls) != nil { canSendPolls = false } } else if let group = peer as? TelegramGroup { - if group.hasBannedPermission(.banSendMedia) { - bannedSendMedia = (Int32.max, false) + if group.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = (Int32.max, false) + } + if group.hasBannedPermission(.banSendVideos) { + bannedSendVideos = (Int32.max, false) + } + if group.hasBannedPermission(.banSendFiles) { + bannedSendFiles = (Int32.max, false) } if group.hasBannedPermission(.banSendPolls) { canSendPolls = false @@ -12097,7 +12127,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G controller.prepareForReuse() return } - strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendMedia: bannedSendMedia, present: { controller, mediaPickerContext in + strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: { controller, mediaPickerContext in let _ = currentMediaController.swap(controller) if !inputText.string.isEmpty { mediaPickerContext?.setCaption(inputText) @@ -12119,7 +12149,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G controller.prepareForReuse() return } - let controller = attachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendMedia, presentGallery: { [weak self, weak attachmentController] in + let controller = attachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendFiles, presentGallery: { [weak self, weak attachmentController] in attachmentController?.dismiss(animated: true) self?.presentFileGallery() }, presentFiles: { [weak self, weak attachmentController] in @@ -12796,11 +12826,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.present(actionSheet, in: .window(.root)) } - private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil), saveEditedPhotos: Bool, bannedSendMedia: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) { + private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { return } - let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadTitle: self.threadInfo?.title, chatLocation: self.chatLocation, bannedSendMedia: bannedSendMedia, subject: subject, saveEditedPhotos: saveEditedPhotos) + let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadTitle: self.threadInfo?.title, chatLocation: self.chatLocation, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, subject: subject, saveEditedPhotos: saveEditedPhotos) let mediaPickerContext = controller.mediaPickerContext controller.openCamera = { [weak self] cameraView in self?.openCamera(cameraView: cameraView) @@ -13616,7 +13646,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let stickerPackReference = stickerPackReference { - let _ = (self.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false) + self.presentEmojiList(references: [stickerPackReference]) + + /*let _ = (self.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false) |> deliverOnMainQueue).start(next: { [weak self] stickerPack in if let strongSelf = self, case let .result(info, _, _) = stickerPack { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: strongSelf.presentationData.strings.Stickers_EmojiPackInfoText(info.title).string, undoText: strongSelf.presentationData.strings.Stickers_PremiumPackView, customAction: nil), elevatedLayout: false, action: { [weak self] action in @@ -13626,7 +13658,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return false }), in: .current) } - }) + })*/ } } @@ -13894,9 +13926,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), subjects: subjects, - presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendMedia, present in + presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendPhotos, bannedSendVideos, present in if let strongSelf = self { - strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendMedia: bannedSendMedia, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] signals, silentPosting, scheduleTime, getAnimatedTransitionSource, completion in + strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] signals, silentPosting, scheduleTime, getAnimatedTransitionSource, completion in self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) }) } @@ -17390,7 +17422,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let presentationData = self.presentationData let controller = StickerPackScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(references), parentNavigationController: self.effectiveNavigationController, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in if let strongSelf = self { - strongSelf.controllerInteraction?.sendEmoji(text, attribute) + strongSelf.controllerInteraction?.sendEmoji(text, attribute, false) } } : nil, actionPerformed: { [weak self] actions in guard let strongSelf = self else { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 9e03899432..678e6c7887 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -274,7 +274,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, navigateToThreadMessage: { _, _, _ in - }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in + }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in self?.openUrl(url) }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in if let strongSelf = self, let navigationController = strongSelf.getNavigationController() { diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index dc5728da27..4c7ef91f57 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -84,7 +84,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false - }, sendEmoji: { _, _ in + }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index eb195fc3a0..d7c8e7637b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1751,8 +1751,14 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL if let defaultBannedRights = channel.defaultBannedRights { var count = 0 for (right, _) in allGroupPermissionList(peer: .channel(channel)) { - if !defaultBannedRights.flags.contains(right) { - count += 1 + if right == .banSendMedia { + if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { + count += 1 + } + } else { + if !defaultBannedRights.flags.contains(right) { + count += 1 + } } } activePermissionCount = count @@ -1868,8 +1874,14 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL if let defaultBannedRights = group.defaultBannedRights { var count = 0 for (right, _) in allGroupPermissionList(peer: .legacyGroup(group)) { - if !defaultBannedRights.flags.contains(right) { - count += 1 + if right == .banSendMedia { + if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { + count += 1 + } + } else { + if !defaultBannedRights.flags.contains(right) { + count += 1 + } } } activePermissionCount = count @@ -2563,7 +2575,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false - }, sendEmoji: { _, _ in + }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 080818c108..54bfd835e6 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1301,7 +1301,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { tapMessage?(message) }, clickThroughMessage: { clickThroughMessage?() - }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in + }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in