diff --git a/Random.txt b/Random.txt index b4188442e1..c407a4cb13 100644 --- a/Random.txt +++ b/Random.txt @@ -1 +1 @@ -06de25b179c80e58 +06de25b179c80e59 diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index d502eee560..e72541198b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15846,6 +15846,7 @@ Error: %8$@"; "AuthConfirmation.Emoji.Title" = "Tap the emoji shown\non your other device"; "AuthConfirmation.Emoji.Description" = "Telegram wants to make sure it's really you."; +"AuthConfirmation.Emoji.DescriptionFirst" = "Telegram wants to make sure it's really you trying to log in to **%@**."; "Chat.TagPlaceholder" = "Tag"; @@ -15861,6 +15862,7 @@ Error: %8$@"; "Chat.EnableSharingOffer.RejectConfirmation.Reject" = "Keep Disabled"; "Chat.TagUpdated.Added" = "Tag added"; +"Chat.TagUpdated.Edited" = "Tag edited"; "Chat.TagUpdated.Removed" = "Tag removed"; "Conversation.FormatDate.Title" = "Date"; @@ -15890,7 +15892,10 @@ Error: %8$@"; "GroupPermission.AddException" = "Add Exception"; "GroupInfo.ActionSend" = "Send Message"; +"GroupInfo.ActionAddRank" = "Add Member Tag"; "GroupInfo.ActionEditRank" = "Edit Member Tag"; +"GroupInfo.ActionEditAdminRank" = "Edit Admin Tag"; +"GroupInfo.ActionEditAdmin" = "Edit Admin Rights"; "GroupInfo.ActionRemove" = "Remove"; "PeerInfo.EnableSharing" = "Enable Sharing"; @@ -15938,12 +15943,33 @@ Error: %8$@"; "DisableSharing.Title" = "Disable Sharing"; "DisableSharing.Screenshot.Title" = "No Screenshots"; "DisableSharing.Screenshot.Text" = "Disable screenshots and screen recordings in this chat."; - "DisableSharing.Forwarding.Title" = "No Forwarding"; "DisableSharing.Forwarding.Text" = "Disable message forwarding to other chats."; - "DisableSharing.Saving.Title" = "No Saving"; "DisableSharing.Saving.Text" = "Disable copying text and saving photos and videos to Photos."; "DisableSharing.Confirm" = "Disable Sharing"; +"EnableSharing.Title" = "Enable Sharing"; +"EnableSharing.Text" = "You need **%@'s** approval to enable sharing. Send a request?"; +"EnableSharing.SendRequest" = "Send Request"; + "OAUTH_REQUEST" = "Login request for %1$@, location: %2$@"; + +"GroupInfo.AddRank" = "Add Tag"; + +"Channel.Management.DismissAdmin" = "Dismiss"; + +"Attachment.ScanDocument" = "Scan Document"; + +"Month.NomJanuary" = "January"; +"Month.NomFebruary" = "February"; +"Month.NomMarch" = "March"; +"Month.NomApril" = "April"; +"Month.NomMay" = "May"; +"Month.NomJune" = "June"; +"Month.NomJuly" = "July"; +"Month.NomAugust" = "August"; +"Month.NomSeptember" = "September"; +"Month.NomOctober" = "October"; +"Month.NomNovember" = "November"; +"Month.NomDecember" = "December"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 4bf66ba784..6c59236378 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1008,6 +1008,7 @@ public protocol CameraScreen: ViewController { } public protocol MediaEditorScreen: ViewController { + var customNavigationController: UINavigationController? { get set } } public protocol MediaPickerScreen: ViewController { @@ -1045,6 +1046,7 @@ public protocol TelegramRootControllerInterface: NavigationController { func getPrivacySettings() -> Promise? func getTwoStepAuthData() -> Promise? + func getNotificationExceptions() -> Promise? func openContacts() func openSettings(edit: Bool) diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index facdee43cc..c1d00519a0 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -981,6 +981,7 @@ public protocol PeerInfoScreen: ViewController { var peerId: PeerId { get } var privacySettings: Promise { get } var twoStepAuthData: Promise { get } + var notificationExceptions: Promise { get } func activateEdit() func openEmojiStatusSetup() diff --git a/submodules/AccountContext/Sources/OpenChatMessage.swift b/submodules/AccountContext/Sources/OpenChatMessage.swift index c31593f349..ad53c7d0c3 100644 --- a/submodules/AccountContext/Sources/OpenChatMessage.swift +++ b/submodules/AccountContext/Sources/OpenChatMessage.swift @@ -27,6 +27,7 @@ public final class OpenChatMessageParams { public let message: Message public let mediaIndex: Int? public let standalone: Bool + public let copyProtected: Bool public let reverseMessageGalleryOrder: Bool public let mode: ChatControllerInteractionOpenMessageMode public let navigationController: NavigationController? @@ -61,6 +62,7 @@ public final class OpenChatMessageParams { message: Message, mediaIndex: Int? = nil, standalone: Bool, + copyProtected: Bool = false, reverseMessageGalleryOrder: Bool, mode: ChatControllerInteractionOpenMessageMode = .default, navigationController: NavigationController?, @@ -93,6 +95,7 @@ public final class OpenChatMessageParams { self.message = message self.mediaIndex = mediaIndex self.standalone = standalone + self.copyProtected = copyProtected self.reverseMessageGalleryOrder = reverseMessageGalleryOrder self.mode = mode self.navigationController = navigationController diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 9d2f520c9e..2a63824a1f 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -65,6 +65,7 @@ public final class PeerSelectionControllerParams { public let hasTypeHeaders: Bool public let selectForumThreads: Bool public let hasCreation: Bool + public let immediatelySwitchToContacts: Bool public let immediatelyActivateMultipleSelection: Bool public init( @@ -87,6 +88,7 @@ public final class PeerSelectionControllerParams { hasTypeHeaders: Bool = false, selectForumThreads: Bool = false, hasCreation: Bool = false, + immediatelySwitchToContacts: Bool = false, immediatelyActivateMultipleSelection: Bool = false ) { self.context = context @@ -108,6 +110,7 @@ public final class PeerSelectionControllerParams { self.hasTypeHeaders = hasTypeHeaders self.selectForumThreads = selectForumThreads self.hasCreation = hasCreation + self.immediatelySwitchToContacts = immediatelySwitchToContacts self.immediatelyActivateMultipleSelection = immediatelyActivateMultipleSelection } } diff --git a/submodules/AttachmentUI/Sources/AttachmentContainer.swift b/submodules/AttachmentUI/Sources/AttachmentContainer.swift index afdfcd5846..c45190c870 100644 --- a/submodules/AttachmentUI/Sources/AttachmentContainer.swift +++ b/submodules/AttachmentUI/Sources/AttachmentContainer.swift @@ -30,6 +30,8 @@ public func attachmentDefaultTopInset(layout: ContainerViewLayout?) -> CGFloat { final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate { private let glass: Bool + private let hasPill: Bool + let wrappingNode: ASDisplayNode let clipNode: ASDisplayNode let bottomClipNode: ASDisplayNode @@ -85,13 +87,14 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate { var isInnerPanGestureEnabled: (() -> Bool)? var onExpandAnimationCompleted: () -> Void = {} - init(presentationData: PresentationData, isFullSize: Bool, glass: Bool) { + init(presentationData: PresentationData, isFullSize: Bool, glass: Bool, hasPill: Bool) { self.presentationData = presentationData self.isFullSize = isFullSize if isFullSize { self.isExpanded = true } self.glass = glass + self.hasPill = hasPill self.wrappingNode = ASDisplayNode() self.clipNode = ASDisplayNode() @@ -147,7 +150,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate { self.panGestureRecognizer = panRecognizer self.wrappingNode.view.addGestureRecognizer(panRecognizer) - if self.glass { + if self.hasPill { self.clipNode.view.addSubview(self.pillView) } diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 3256d21dcd..49fa0329f3 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -553,7 +553,7 @@ public class AttachmentController: ViewController, MinimizableController { self.wrapperNode = ASDisplayNode() self.wrapperNode.clipsToBounds = true - self.container = AttachmentContainer(presentationData: self.presentationData, isFullSize: controller.isFullSize, glass: controller.style == .glass) + self.container = AttachmentContainer(presentationData: self.presentationData, isFullSize: controller.isFullSize, glass: controller._hasGlassStyle, hasPill: controller.style == .glass) self.container.canHaveKeyboardFocus = true let panelStyle: AttachmentPanel.Style @@ -892,11 +892,10 @@ public class AttachmentController: ViewController, MinimizableController { return true } let previousType = self.currentType - self.currentType = type - self.controller?.requestController(type, { [weak self] controller, mediaPickerContext in + let shouldSwitch = self.controller?.requestController(type, { [weak self] controller, mediaPickerContext in if let strongSelf = self { if let controller = controller { - if case .glass = strongSelf.controller?.style { + if strongSelf.controller?._hasGlassStyle == true { controller._hasGlassStyle = true } strongSelf.controller?._ready.set(controller.ready.get()) @@ -971,8 +970,11 @@ public class AttachmentController: ViewController, MinimizableController { } strongSelf.mediaPickerContext = mediaPickerContext } - }) - return true + }) ?? true + if shouldSwitch { + self.currentType = type + } + return shouldSwitch } private func animateSwitchTransition(_ controller: AttachmentContainable, previousController: AttachmentContainable?) { @@ -1437,8 +1439,9 @@ public class AttachmentController: ViewController, MinimizableController { } } - public var requestController: (AttachmentButtonType, @escaping (AttachmentContainable?, AttachmentMediaPickerContext?) -> Void) -> Void = { _, completion in + public var requestController: (AttachmentButtonType, @escaping (AttachmentContainable?, AttachmentMediaPickerContext?) -> Void) -> Bool = { _, completion in completion(nil, nil) + return true } public var getInputContainerNode: () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil } @@ -1474,7 +1477,7 @@ public class AttachmentController: ViewController, MinimizableController { super.init(navigationBarPresentationData: nil) - self._hasGlassStyle = style == .glass + self._hasGlassStyle = true //style == .glass self.statusBar.statusBarStyle = .Ignore self.blocksBackgroundWhenInOverlay = true diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index ee4c3c06b1..baf4a556a0 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -441,26 +441,17 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU return } var dismissImpl: (() -> Void)? - let controller = AuthConfirmationScreen(context: self.context, requestSubject: subject, subject: result, completion: { [weak self] accountContext, accountPeer, authResult in + let controller = AuthConfirmationScreen(context: self.context, requestSubject: subject, subject: result, completion: { [weak self] accountContext, accountPeer, authResult, disposable in guard let self else { return } switch authResult { case let .accept(allowWriteAccess, sharePhoneNumber, matchCode): - let signal: Signal - if accountContext === context { - signal = accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode) - } else { - accountContext.account.shouldBeServiceTaskMaster.set(.single(.now)) - signal = accountContext.engine.messages.requestMessageActionUrlAuth(subject: subject) - |> castError(MessageActionUrlAuthError.self) - |> mapToSignal { result in - return accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode) - } |> afterDisposed { - accountContext.account.shouldBeServiceTaskMaster.set(.single(.never)) - } + let signal = accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode) + |> afterDisposed { + disposable.dispose() } - + let _ = (signal |> deliverOnMainQueue).start(next: { authResult in dismissImpl?() diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 284cade852..0debd0179e 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -5253,7 +5253,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } var canEditRank = false - if let channel = peer as? TelegramChannel, channel.hasPermission(.editRank) { + if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.editRank) { canEditRank = true } else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { canEditRank = true diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index d8dbb29e6a..2d595caf56 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -810,13 +810,23 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch var didFocusOnItem = false controller.afterTransactionCompleted = { [weak controller] in + guard let controller else { + return + } + + if let focusOnItemTag, !didFocusOnItem { + controller.forEachItemNode { itemNode in + if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag, tag.isEqual(to: focusOnItemTag) { + didFocusOnItem = true + itemNode.displayHighlight() + } + } + } + guard let toggleDirection = animateNextShowHideTagsTransition.swap(nil) else { return } - guard let controller else { - return - } var presetItemNodes: [ChatListFilterPresetListItemNode] = [] controller.forEachItemNode { itemNode in if let itemNode = itemNode as? ChatListFilterPresetListItemNode { @@ -831,15 +841,6 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch } delay += 0.02 } - - if let focusOnItemTag, !didFocusOnItem { - controller.forEachItemNode { itemNode in - if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag, tag.isEqual(to: focusOnItemTag) { - didFocusOnItem = true - itemNode.displayHighlight() - } - } - } } return controller diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 31d619cce4..beb6050497 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -2083,8 +2083,12 @@ public final class ContactListNode: ASDisplayNode { strongSelf.presentationData = presentationData if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { - strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor - strongSelf.listNode.verticalScrollIndicatorColor = presentationData.theme.list.scrollIndicatorColor + strongSelf.backgroundColor = listStyle == .blocks ? strongSelf.presentationData.theme.list.blocksBackgroundColor : strongSelf.presentationData.theme.chatList.backgroundColor + if listStyle == .blocks { + strongSelf.listNode.verticalScrollIndicatorColor = .clear + } else { + strongSelf.listNode.verticalScrollIndicatorColor = strongSelf.presentationData.theme.list.scrollIndicatorColor + } strongSelf.presentationDataPromise.set(.single(presentationData)) let authorizationPreviousHidden = strongSelf.authorizationNode.isHidden diff --git a/submodules/ContactListUI/Sources/InviteContactsCountPanelNode.swift b/submodules/ContactListUI/Sources/InviteContactsCountPanelNode.swift index 2abe674765..021c4acf53 100644 --- a/submodules/ContactListUI/Sources/InviteContactsCountPanelNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsCountPanelNode.swift @@ -3,22 +3,23 @@ import UIKit import AsyncDisplayKit import Display import TelegramPresentationData -import SolidRoundedButtonNode +import ComponentFlow +import ButtonComponent +import EdgeEffect final class InviteContactsCountPanelNode: ASDisplayNode { private let theme: PresentationTheme private let strings: PresentationStrings + private let action: () -> Void - private let separatorNode: ASDisplayNode - private let button: SolidRoundedButtonNode - + private let edgeEffectView = EdgeEffectView() + private let button = ComponentView() + private var validLayout: (CGFloat, CGFloat, CGFloat)? var count: Int = 0 { didSet { if self.count != oldValue && self.count > 0 { - self.button.title = self.strings.Contacts_InviteContacts(Int32(self.count)) - if let (width, sideInset, bottomInset) = self.validLayout { let _ = self.updateLayout(width: width, sideInset: sideInset, bottomInset: bottomInset, transition: .immediate) } @@ -29,37 +30,75 @@ final class InviteContactsCountPanelNode: ASDisplayNode { init(theme: PresentationTheme, strings: PresentationStrings, action: @escaping () -> Void) { self.theme = theme self.strings = strings + self.action = action - self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor - - self.button = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), height: 48.0, cornerRadius: 10.0) - super.init() - - self.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor - - self.addSubnode(self.button) - self.addSubnode(self.separatorNode) - - self.button.pressed = { - action() - } } func updateLayout(width: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { self.validLayout = (width, sideInset, bottomInset) - let topInset: CGFloat = 9.0 - var bottomInset = bottomInset - bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0) - let buttonInset: CGFloat = 16.0 + sideInset - let buttonWidth = width - buttonInset * 2.0 - let buttonHeight = self.button.updateLayout(width: buttonWidth, transition: transition) - transition.updateFrame(node: self.button, frame: CGRect(x: buttonInset, y: topInset, width: buttonWidth, height: buttonHeight)) + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0) + let height: CGFloat = 52.0 + buttonInsets.bottom - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) + let edgeEffectHeight: CGFloat = height + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update( + content: self.theme.list.plainBackgroundColor, + blur: true, + rect: edgeEffectFrame, + edge: .bottom, + edgeSize: edgeEffectFrame.height, + transition: ComponentTransition(transition) + ) + if self.edgeEffectView.superview == nil { + self.view.addSubview(self.edgeEffectView) + } - return topInset + buttonHeight + bottomInset + let buttonTransition: ComponentTransition = .easeInOut(duration: 0.2) + let buttonSize = self.button.update( + transition: buttonTransition, + component: AnyComponent( + ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: self.theme.list.itemCheckColors.fillColor, + foreground: self.theme.list.itemCheckColors.foregroundColor, + pressedColor: self.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(ButtonTextContentComponent( + text: self.strings.Contacts_InviteContacts(Int32(self.count)), + badge: 0, + textColor: self.theme.list.itemCheckColors.foregroundColor, + badgeBackground: self.theme.list.itemCheckColors.foregroundColor, + badgeForeground: self.theme.list.itemCheckColors.fillColor, + badgeStyle: .roundedRectangle, + badgeIconName: nil, + combinedAlignment: true + )) + ), + isEnabled: true, + action: { [weak self] in + guard let self else { + return + } + self.action() + } + ) + ), + environment: {}, + containerSize: CGSize(width: width - sideInset * 2.0 - buttonInsets.left - buttonInsets.right, height: 52.0) + ) + let buttonFrame = CGRect(origin: CGPoint(x: sideInset + buttonInsets.left, y: 0.0), size: buttonSize) + if let buttonView = self.button.view { + if buttonView.superview == nil { + self.view.addSubview(buttonView) + } + transition.updateFrame(view: buttonView, frame: buttonFrame) + } + return height } } diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index e4150b8966..d46b90abf3 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -830,7 +830,6 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { if let rightLabelText = item.rightLabelText { let rightLabelTextLayoutAndApplyValue = makeRightLabelTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: rightLabelText.text, font: statusFont, textColor: rightLabelText.color), maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 20.0, height: 100.0))) rightLabelTextLayoutAndApply = rightLabelTextLayoutAndApplyValue - rightInset -= 6.0 + rightLabelTextLayoutAndApplyValue.0.size.width } var searchAdIcon: UIImage? diff --git a/submodules/DatePickerNode/Sources/DatePickerNode.swift b/submodules/DatePickerNode/Sources/DatePickerNode.swift index fbf04197e6..91f4af24e9 100644 --- a/submodules/DatePickerNode/Sources/DatePickerNode.swift +++ b/submodules/DatePickerNode/Sources/DatePickerNode.swift @@ -460,12 +460,12 @@ public final class DatePickerNode: ASDisplayNode { self.dateButtonNode = HighlightableButtonNode() self.dateButtonNode.clipsToBounds = true self.dateButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor - self.dateButtonNode.cornerRadius = 9.0 + self.dateButtonNode.cornerRadius = 18.0 self.timeButtonNode = HighlightableButtonNode() self.timeButtonNode.clipsToBounds = true self.timeButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor - self.timeButtonNode.cornerRadius = 9.0 + self.timeButtonNode.cornerRadius = 18.0 super.init() @@ -828,7 +828,7 @@ public final class DatePickerNode: ASDisplayNode { var timeSize = self.timeButtonNode.measure(size) timeSize.width += 24.0 timeSize.height = 36.0 - self.timeButtonNode.frame = CGRect(x: size.width - timeSize.width - 4.0, y: 4.0, width: timeSize.width, height: timeSize.height) + self.timeButtonNode.frame = CGRect(x: size.width - timeSize.width - 10.0, y: 4.0, width: timeSize.width, height: timeSize.height) let dateString = stringForMediumDate(timestamp: Int32(date.timeIntervalSince1970), strings: self.strings, dateTimeFormat: self.dateTimeFormat, withTime: false) self.dateButtonNode.setTitle(dateString, with: Font.with(size: 17.0, traits: .monospacedNumbers), with: self.state.displayingDateSelection ? self.theme.accentColor : self.theme.textColor, for: .normal) @@ -836,7 +836,7 @@ public final class DatePickerNode: ASDisplayNode { var dateSize = self.dateButtonNode.measure(size) dateSize.width += 24.0 dateSize.height = 36.0 - self.dateButtonNode.frame = CGRect(x: size.width - timeSize.width - 4.0 - 4.0 - dateSize.width, y: 4.0, width: dateSize.width, height: dateSize.height) + self.dateButtonNode.frame = CGRect(x: size.width - timeSize.width - 10.0 - 4.0 - dateSize.width, y: 4.0, width: dateSize.width, height: dateSize.height) let daysSideInset: CGFloat = 12.0 let cellSize: CGFloat = floor((constrainedSize.width - daysSideInset * 2.0) / 7.0) @@ -1021,7 +1021,7 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick if component == 1 { string = "\(self.yearRange.startIndex + row)" } else { - string = stringForMonth(strings: self.strings, month: Int32(row)) + string = stringForNominativeMonth(strings: self.strings, month: Int32(row)) } return NSAttributedString(string: string, font: Font.medium(15.0), textColor: self.theme.textColor) } diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 3ae4a36178..a1060351eb 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -1322,6 +1322,12 @@ public class GalleryController: ViewController, StandalonePresentableController, self.dismiss(forceAway: false) } + func willDismiss() { + if let chatController = self.baseNavigationController?.topViewController as? ChatController { + chatController.updatePushedTransition(0.0, transition: .immediate) + } + } + func dismiss(forceAway: Bool) { var animatedOutNode = true var animatedOutInterface = false diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 841b3c939b..51a087ab2e 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -733,14 +733,17 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { guard let self, case let .data(data) = value, data.complete, isImage, let image = UIImage(contentsOfFile: data.path) else { return } - let controller = context.sharedContext.makeStickerEditorScreen(context: context, source: image, mode: .generic(canSend: self.sendSticker != nil), transitionArguments: (self.imageNode.view, self.imageNode.bounds, self.imageNode.image), completion: { [weak self] file, _, commit in - self?.sendSticker?(.standalone(media: file)) + let sendSticker = self.sendSticker + let controller = context.sharedContext.makeStickerEditorScreen(context: context, source: image, mode: .generic(canSend: self.sendSticker != nil), transitionArguments: (self.imageNode.view, self.imageNode.bounds, self.imageNode.image), completion: { file, _, commit in + sendSticker?(.standalone(media: file)) commit() }, cancelled: {}) - guard let galleryController = self.galleryController(), let navigationController = self.baseNavigationController() else { + guard let galleryController = self.galleryController() as? GalleryController, let navigationController = self.baseNavigationController() else { return } + galleryController.willDismiss() (navigationController.topViewController as? ViewController)?.present(controller, in: .window(.root)) + (controller as? MediaEditorScreen)?.customNavigationController = navigationController self.imageNode.isHidden = true Queue.mainQueue().after(0.5, { galleryController.dismiss() diff --git a/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift b/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift index 13fc11eb8d..79564f02cb 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift @@ -388,7 +388,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry { case let .timeCustomPicker(_, dateTimeFormat, date, displayingDateSelection, displayingTimeSelection, enabled): let _ = enabled let title = presentationData.strings.InviteLink_Create_TimeLimitExpiryTime - return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: { + return ItemListDatePickerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, date: date, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: { arguments.updateState({ state in var updatedState = state updatedState.pickingExpiryDate = !updatedState.pickingExpiryDate diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index 860398c94c..3ffe04fef9 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -118,8 +118,9 @@ public class ItemListPeerActionItem: ListViewItem, ItemListItem { } } -public final class ItemListPeerActionItemNode: ListViewItemNode { +public final class ItemListPeerActionItemNode: ListViewItemNode, ItemListItemNode { private let backgroundNode: ASDisplayNode + private let highlightNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode @@ -143,6 +144,9 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true + self.highlightNode = ASDisplayNode() + self.highlightNode.isLayerBacked = true + self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true @@ -178,6 +182,20 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { self.iconDisposable.dispose() } + public func displayHighlight() { + if self.backgroundNode.supernode != nil { + self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode) + } else { + self.insertSubnode(self.highlightNode, at: 0) + } + + Queue.mainQueue().after(1.2, { + self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + self.highlightNode.removeFromSupernode() + }) + }) + } + public func asyncLayout() -> (_ item: ItemListPeerActionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) @@ -275,6 +293,7 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { } strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor } + strongSelf.highlightNode.backgroundColor = item.presentationData.theme.list.itemSearchHighlightColor } let _ = titleApply() @@ -357,6 +376,7 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) + strongSelf.highlightNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset - separatorRightInset - params.rightInset, height: separatorHeight))) diff --git a/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift b/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift index 4bbd49de8f..130c1bfb5c 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift @@ -7,13 +7,15 @@ import TelegramPresentationData public class ItemListPlaceholderItem: ListViewItem, ItemListItem { let theme: PresentationTheme + let systemStyle: ItemListSystemStyle let text: String public let sectionId: ItemListSectionId let style: ItemListStyle public let tag: ItemListItemTag? - public init(theme: PresentationTheme, text: String, sectionId: ItemListSectionId, style: ItemListStyle, tag: ItemListItemTag? = nil) { + public init(theme: PresentationTheme, systemStyle: ItemListSystemStyle = .legacy, text: String, sectionId: ItemListSectionId, style: ItemListStyle, tag: ItemListItemTag? = nil) { self.theme = theme + self.systemStyle = systemStyle self.text = text self.sectionId = sectionId self.style = style @@ -198,7 +200,7 @@ public class ItemListPlaceholderItemNode: ListViewItemNode, ItemListItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift index b3b368ecfa..4c57a77bcb 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift @@ -113,9 +113,10 @@ public class ItemListSectionHeaderItem: ListViewItem, ItemListItem { } } -public class ItemListSectionHeaderItemNode: ListViewItemNode { +public class ItemListSectionHeaderItemNode: ListViewItemNode, ItemListItemNode { private var item: ItemListSectionHeaderItem? + private let highlightNode: ASDisplayNode private let titleNode: TextNode private var badgeBackgroundLayer: SimpleLayer? private var badgeTextNode: TextNode? @@ -132,6 +133,11 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode { } public init() { + self.highlightNode = ASDisplayNode() + self.highlightNode.isLayerBacked = true + self.highlightNode.cornerRadius = 4.0 + self.highlightNode.clipsToBounds = true + self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false self.titleNode.contentMode = .left @@ -146,12 +152,22 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode { self.activateArea.accessibilityTraits = [.staticText, .header] super.init(layerBacked: false) - + self.addSubnode(self.titleNode) self.addSubnode(self.accessoryTextNode) self.addSubnode(self.activateArea) } + public func displayHighlight() { + self.insertSubnode(self.highlightNode, at: 0) + + Queue.mainQueue().after(1.2, { + self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + self.highlightNode.removeFromSupernode() + }) + }) + } + public func asyncLayout() -> (_ item: ItemListSectionHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeActionLayout = TextNode.asyncLayout(self.actionNode) @@ -230,7 +246,10 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode { strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height)) strongSelf.activateArea.accessibilityLabel = item.text + strongSelf.highlightNode.backgroundColor = item.presentationData.theme.list.itemSearchHighlightColor + strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: titleLayout.size) + strongSelf.highlightNode.frame = strongSelf.titleNode.frame.insetBy(dx: -3.0, dy: -2.0) if let (actionLayout, actionApply) = actionLayoutAndApply { let actionButtonNode: HighlightableButtonNode diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 571b440f73..df028bd6a0 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -452,6 +452,7 @@ public func storyLocationPickerController( completion(location, queryId, resultId, address, countryCode) }) present(locationPickerController, locationPickerController.mediaPickerContext) + return true } controller.navigationPresentation = .flatModal controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) diff --git a/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift b/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift index dfe78fa1cc..71f95913ff 100644 --- a/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift +++ b/submodules/MediaPasteboardUI/Sources/MediaPasteboardScreen.swift @@ -24,6 +24,7 @@ public func mediaPasteboardScreen( presentMediaPicker(.media(subjects), false, nil, nil, { mediaPicker, mediaPickerContext in present(mediaPicker, mediaPickerContext) }) + return true } controller.updateSelectionCount(subjects.count) controller.getSourceRect = getSourceRect diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 590397e791..3040bd4e98 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -3483,6 +3483,7 @@ public func wallpaperMediaPickerController( }) mediaPickerController.customSelection = completion present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) return controller @@ -3525,6 +3526,7 @@ public func mediaPickerController( } } present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.navigationPresentation = .flatModal controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -3658,6 +3660,7 @@ public func storyMediaPickerController( } } present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.willDismiss = { [weak selectionContext] in dismissed() @@ -3810,6 +3813,7 @@ public func stickerMediaPickerController( } } present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.willDismiss = { dismissed() @@ -4000,6 +4004,7 @@ public func avatarMediaPickerController( } } present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.willDismiss = { dismissed() @@ -4063,6 +4068,7 @@ public func coverMediaPickerController( controller?.dismiss(animated: true) } present(mediaPickerController, mediaPickerController.mediaPickerContext) + return true } controller.willDismiss = { dismissed() diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index d969182e81..c8bf38e2d1 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -319,11 +319,15 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { label = .none } - return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(participant.peer), presence: participant.presences[participant.peer.id].flatMap { EnginePeer.Presence($0) }, text: peerText.isEmpty ? .presence : .text(peerText, .secondary), label: label, editing: editing, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in - arguments.setPeerIdWithRevealedOptions(previousId, id) - }, removePeer: { peerId in - arguments.removeAdmin(peerId) - }) + let revealOptions = ItemListPeerItemRevealOptions(options:[ + .init(type: .destructive, title: presentationData.strings.Channel_Management_DismissAdmin, action: { arguments.removeAdmin(participant.peer.id) }) + ]) + + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(participant.peer), presence: participant.presences[participant.peer.id].flatMap { EnginePeer.Presence($0) }, text: peerText.isEmpty ? .presence : .text(peerText, .secondary), label: label, editing: editing, revealOptions: revealOptions, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in + arguments.setPeerIdWithRevealedOptions(previousId, id) + }, removePeer: { peerId in + arguments.removeAdmin(peerId) + }) case let .addAdmin(theme, text, editing): return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: editing, action: { arguments.addAdmin() diff --git a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift index fb83707153..2c0fe628f0 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift @@ -875,7 +875,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen return current } - if updateFlags == nil && updateTimeout == nil { + if updateFlags == nil && updateTimeout == nil && !editMember { if case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant { if maybeBanInfo == nil { updateFlags = defaultBannedRightsFlags @@ -1018,7 +1018,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen |> deliverOnMainQueue).start(error: { _ in }, completed: { - if previousRights == nil { + if previousRights == nil, !editMember { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.GroupPermission_AddSuccess, false)), nil) } @@ -1031,20 +1031,6 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen } applyRights() -// let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } -// let actionSheet = ActionSheetController(presentationData: presentationData) -// var items: [ActionSheetItem] = [] -// items.append(ActionSheetTextItem(title: presentationData.strings.GroupPermission_ApplyAlertText(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string)) -// items.append(ActionSheetButtonItem(title: presentationData.strings.GroupPermission_ApplyAlertAction, color: .accent, font: .default, enabled: true, action: { [weak actionSheet] in -// actionSheet?.dismissAnimated() -// applyRights() -// })) -// actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ -// ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in -// actionSheet?.dismissAnimated() -// }) -// ])]) -// presentControllerImpl?(actionSheet, nil) } } else { updateRankDisposable.set((updateRankSignal(peerId) diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 1aec3ba690..c8ca63b995 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -151,44 +151,78 @@ private final class ChannelMembersSearchEntry: Comparable, Identifiable { func item(context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchContainerInteraction) -> ListViewItem { switch self.content { - case let .peer(peer): - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) }), action: { _ in - interaction.peerSelected(peer, nil) - }) - case let .participant(participant, label, revealActions, revealed, enabled): - let status: ContactsPeerItemStatus - if let label = label { - status = .custom(string: NSAttributedString(string: label), multiline: false, isActive: false, icon: nil) - } else if let presence = participant.presences[participant.peer.id], self.addIcon { - status = .presence(EnginePeer.Presence(presence), dateTimeFormat) + case let .peer(peer): + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) }), action: { _ in + interaction.peerSelected(peer, nil) + }) + case let .participant(participant, _, revealActions, revealed, enabled): + let status: ContactsPeerItemStatus + if let presence = participant.presences[participant.peer.id] { + status = .presence(EnginePeer.Presence(presence), dateTimeFormat) + } else { + status = .none + } + + var options: [ItemListPeerItemRevealOption] = [] + for action in revealActions { + options.append(ItemListPeerItemRevealOption(type: action.type, title: action.title, action: { + switch action.action { + case .promote: + interaction.promotePeer(participant) + break + case .restrict: + interaction.restrictPeer(participant) + break + case .remove: + interaction.removePeer(participant.peer.id) + break + } + })) + } + var actionIcon: ContactsPeerItemActionIcon = .none + if self.addIcon { + actionIcon = .add + } + + let label: String? + var labelColor = presentationData.theme.list.itemSecondaryTextColor + var labelBackground = false + switch participant.participant { + case let .creator(_, _, rank): + label = rank ?? presentationData.strings.GroupInfo_LabelOwner + labelBackground = true + labelColor = UIColor(rgb: 0x956ac8) + case let .member(_, _, adminInfo, _, rank, _): + if let _ = adminInfo { + label = rank ?? presentationData.strings.GroupInfo_LabelAdmin + labelBackground = true + labelColor = UIColor(rgb: 0x49a355) } else { - status = .none + label = rank } - - var options: [ItemListPeerItemRevealOption] = [] - for action in revealActions { - options.append(ItemListPeerItemRevealOption(type: action.type, title: action.title, action: { - switch action.action { - case .promote: - interaction.promotePeer(participant) - break - case .restrict: - interaction.restrictPeer(participant) - break - case .remove: - interaction.removePeer(participant.peer.id) - break - } - })) - } - var actionIcon: ContactsPeerItemActionIcon = .none - if self.addIcon { - actionIcon = .add - } - - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(participant.peer), chatPeer: EnginePeer(participant.peer)), status: status, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: revealed), options: options, actionIcon: actionIcon, index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) }), action: { _ in + } + + return ContactsPeerItem( + presentationData: ItemListPresentationData(presentationData), + systemStyle: .glass, + sortOrder: nameSortOrder, + displayOrder: nameDisplayOrder, + context: context, + peerMode: .peer, + peer: .peer(peer: EnginePeer(participant.peer), chatPeer: EnginePeer(participant.peer)), + status: status, + rightLabelText: label.flatMap { .init(text: $0, color: labelColor, hasBackground: labelBackground) }, + enabled: enabled, + selection: .none, + editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: revealed), + options: options, + actionIcon: actionIcon, + index: nil, + header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) }), + action: { _ in interaction.peerSelected(EnginePeer(participant.peer), participant) - }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in + }, + setPeerIdWithRevealedOptions: { peerId, fromPeerId in interaction.setPeerIdWithRevealedOptions(RevealedPeerId(peerId: participant.peer.id, section: self.section), fromPeerId.flatMap({ RevealedPeerId(peerId: $0, section: self.section) })) }) } @@ -538,17 +572,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon } } - var label: String? var enabled = true - if case .searchMembers = mode { - switch participant.participant { - case .creator: - label = presentationData.strings.Channel_Management_LabelOwner - default: - break - } - } - if state.removingParticipantIds.contains(participant.peer.id) { enabled = false } @@ -564,7 +588,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon } } - entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: peerActions, revealed: state.revealedPeerId == RevealedPeerId(peerId: participant.peer.id, section: section), enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat)) + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: nil, revealActions: peerActions, revealed: state.revealedPeerId == RevealedPeerId(peerId: participant.peer.id, section: section), enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat)) index += 1 } @@ -771,18 +795,8 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon var enabled = true if case .banAndPromoteActions = mode { if case .creator = participant.participant { - label = presentationData.strings.Channel_Management_LabelOwner enabled = false } - } else if case .searchMembers = mode { - switch participant.participant { - case .creator: - label = presentationData.strings.Channel_Management_LabelOwner - case let .member(_, _, adminInfo, _, _, _): - if adminInfo != nil { - label = presentationData.strings.Channel_Management_LabelEditor - } - } } if state.removingParticipantIds.contains(participant.peer.id) { @@ -878,16 +892,14 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon section = .none } - var label: String? var enabled = true if case .banAndPromoteActions = mode { if case .creator = participant.participant { - label = presentationData.strings.Channel_Management_LabelOwner enabled = false } } - entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], revealed: false, enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat, addIcon: addIcon)) + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: nil, revealActions: [], revealed: false, enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat, addIcon: addIcon)) index += 1 } } @@ -1050,18 +1062,8 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon var enabled = true if case .banAndPromoteActions = mode { if case .creator = participant.participant { - label = presentationData.strings.Channel_Management_LabelOwner enabled = false } - } else if case .searchMembers = mode { - switch participant.participant { - case .creator: - label = presentationData.strings.Channel_Management_LabelOwner - case let .member(_, _, adminInfo, _, _, _): - if adminInfo != nil { - label = presentationData.strings.Channel_Management_LabelEditor - } - } } if state.removingParticipantIds.contains(participant.peer.id) { @@ -1072,7 +1074,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon case .searchAdmins: switch participant.participant { case .creator: - label = presentationData.strings.Channel_Management_LabelOwner + break case let .member(_, _, adminInfo, _, _, _): if let adminInfo = adminInfo { if let peer = participant.peers[adminInfo.promotedBy] { diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index bd04ccb360..d03290333d 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -610,6 +610,7 @@ public func allGroupPermissionList(peer: EnginePeer, expandMedia: Bool) -> [(Tel (.banSendMedia, .banMembers), (.banAddMembers, .banMembers), (.banPinMessages, .pinMessages), + (.banEditRank, .editRank), (.banManageTopics, .manageTopics), (.banChangeInfo, .changeInfo) ] diff --git a/submodules/PremiumUI/Sources/CreateGiveawayController.swift b/submodules/PremiumUI/Sources/CreateGiveawayController.swift index 810c269d13..6271348638 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayController.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayController.swift @@ -433,7 +433,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case let .modeHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .giftPremium(_, title, subtitle, isSelected): - return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: .premium, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: .premium, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { var openSelection = false arguments.updateState { state in var updatedState = state @@ -448,7 +448,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } }) case let .giftStars(_, title, subtitle, isSelected): - return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: .stars, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: false, isSelected: isSelected, sectionId: self.section, action: { + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: .stars, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: false, isSelected: isSelected, sectionId: self.section, action: { arguments.updateState { state in var updatedState = state updatedState.mode = .starsGiveaway @@ -480,7 +480,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { icon = "Premium/PremiumStar" boosts = boostCount } - return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, sectionId: self.section, action: nil) + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, sectionId: self.section, action: nil) case let .starsHeader(_, text, additionalText): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section) case let .stars(_, _, stars, title, subtitle, label, isSelected, maxWinners): @@ -501,7 +501,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case let .subscriptionsHeader(_, text, additionalText): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section) case let .subscriptions(_, value, values): - return SubscriptionsCountItem(theme: presentationData.theme, strings: presentationData.strings, value: value, values: values, sectionId: self.section, updated: { value in + return SubscriptionsCountItem(theme: presentationData.theme, strings: presentationData.strings, systemStyle: .glass, value: value, values: values, sectionId: self.section, updated: { value in arguments.updateState { state in var updatedState = state if state.mode == .giveaway { @@ -536,7 +536,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case let .usersHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .usersAll(_, title, subtitle, isSelected): - return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { var openSelection = false arguments.updateState { state in var updatedState = state @@ -551,7 +551,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } }) case let .usersNew(_, title, subtitle, isSelected): - return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: { var openSelection = false arguments.updateState { state in var updatedState = state @@ -570,7 +570,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case let .durationHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .duration(_, _, months, title, subtitle, label, badge, isSelected): - return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: { + return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: { arguments.updateState { state in var updatedState = state updatedState.selectedMonths = months @@ -635,7 +635,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { }) case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate, displayingDateSelection, displayingTimeSelection): let title = presentationData.strings.BoostGift_DateEnds - return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: { + return ItemListDatePickerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: { var focus = false arguments.updateState({ state in var updatedState = state diff --git a/submodules/PremiumUI/Sources/CreateGiveawayFooterItem.swift b/submodules/PremiumUI/Sources/CreateGiveawayFooterItem.swift index 066dfb3536..bd5af379b5 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayFooterItem.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayFooterItem.swift @@ -7,6 +7,7 @@ import TelegramPresentationData import ItemListUI import PresentationDataUtils import ButtonComponent +import EdgeEffect final class CreateGiveawayFooterItem: ItemListControllerFooterItem { let theme: PresentationTheme @@ -42,8 +43,7 @@ final class CreateGiveawayFooterItem: ItemListControllerFooterItem { } final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode { - private let backgroundNode: NavigationBackgroundNode - private let separatorNode: ASDisplayNode + private let edgeEffectView = EdgeEffectView() private let button = ComponentView() private var validLayout: ContainerViewLayout? @@ -51,7 +51,6 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode { private var currentIsLoading = false var item: CreateGiveawayFooterItem { didSet { - self.updateItem() if let layout = self.validLayout { let _ = self.updateLayout(layout: layout, transition: .immediate) } @@ -60,49 +59,35 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode { init(item: CreateGiveawayFooterItem) { self.item = item - - self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.tabBar.backgroundColor) - self.separatorNode = ASDisplayNode() - + super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.separatorNode) - - self.updateItem() } - private func updateItem() { - self.backgroundNode.updateColor(color: self.item.theme.rootController.tabBar.backgroundColor, transition: .immediate) - self.separatorNode.backgroundColor = self.item.theme.rootController.tabBar.separatorColor - } - override func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateAlpha(node: self.backgroundNode, alpha: alpha) - transition.updateAlpha(node: self.separatorNode, alpha: alpha) } override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat { let hadLayout = self.validLayout != nil self.validLayout = layout - let buttonInset: CGFloat = 16.0 - let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0 - let inset: CGFloat = 9.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.intrinsicInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) + let height: CGFloat = 52.0 + buttonInsets.bottom - let insets = layout.insets(options: [.input]) - - var panelHeight: CGFloat = 50.0 + inset * 2.0 - let totalPanelHeight: CGFloat - if let inputHeight = layout.inputHeight, inputHeight > 0.0 { - totalPanelHeight = panelHeight + insets.bottom - } else { - totalPanelHeight = panelHeight + insets.bottom - panelHeight += insets.bottom + let edgeEffectHeight: CGFloat = height + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update( + content: self.item.theme.list.plainBackgroundColor, + blur: true, + rect: edgeEffectFrame, + edge: .bottom, + edgeSize: edgeEffectFrame.height, + transition: ComponentTransition(transition) + ) + if self.edgeEffectView.superview == nil { + self.view.addSubview(self.edgeEffectView) } - let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - totalPanelHeight), size: CGSize(width: layout.size.width, height: panelHeight)) - var buttonTransition: ComponentTransition = .easeInOut(duration: 0.2) if !hadLayout { buttonTransition = .immediate @@ -112,6 +97,7 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode { component: AnyComponent( ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: self.item.theme.list.itemCheckColors.fillColor, foreground: self.item.theme.list.itemCheckColors.foregroundColor, pressedColor: self.item.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) @@ -140,26 +126,21 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode { ) ), environment: {}, - containerSize: CGSize(width: buttonWidth, height: 50.0) + containerSize: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInsets.left - buttonInsets.right, height: 52.0) ) - if let view = self.button.view { - if view.superview == nil { - self.view.addSubview(view) + let buttonFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInsets.left, y: layout.size.height - buttonInsets.bottom - buttonSize.height), size: buttonSize) + if let buttonView = self.button.view { + if buttonView.superview == nil { + self.view.addSubview(buttonView) } - transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: panelFrame.minY + inset), size: buttonSize)) + transition.updateFrame(view: buttonView, frame: buttonFrame) } - - transition.updateFrame(node: self.backgroundNode, frame: panelFrame) - self.backgroundNode.update(size: panelFrame.size, transition: transition) - - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel))) - - return totalPanelHeight + return height } override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - if self.backgroundNode.frame.contains(point) { + if self.edgeEffectView.frame.contains(point) { return true } else { return false diff --git a/submodules/PremiumUI/Sources/CreateGiveawayHeaderItem.swift b/submodules/PremiumUI/Sources/CreateGiveawayHeaderItem.swift index f65c452521..3b40a7bdcc 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayHeaderItem.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayHeaderItem.swift @@ -9,6 +9,8 @@ import PresentationDataUtils import Markdown import ComponentFlow import PremiumStarComponent +import GlassBarButtonComponent +import BundleIconComponent final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem { let theme: PresentationTheme @@ -49,13 +51,10 @@ private let titleFont = Font.semibold(20.0) private let textFont = Font.regular(15.0) class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { - private let backgroundNode: NavigationBackgroundNode - private let separatorNode: ASDisplayNode private let titleNode: ImmediateTextNode private let textNode: ImmediateTextNode - private let cancelNode: HighlightableButtonNode - + private let cancelButton = ComponentView() private var hostView: ComponentHostView? private var component: AnyComponent? @@ -72,11 +71,7 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { init(item: CreateGiveawayHeaderItem) { self.item = item - - self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.navigationBar.blurredBackgroundColor) - - self.separatorNode = ASDisplayNode() - + self.titleNode = ImmediateTextNode() self.titleNode.isUserInteractionEnabled = false self.titleNode.contentMode = .left @@ -87,26 +82,15 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { self.textNode.contentMode = .left self.textNode.contentsScale = UIScreen.main.scale self.textNode.maximumNumberOfLines = 0 - - self.cancelNode = HighlightableButtonNode() - + super.init() self.addSubnode(self.textNode) - self.addSubnode(self.backgroundNode) - self.addSubnode(self.separatorNode) self.addSubnode(self.titleNode) - self.addSubnode(self.cancelNode) - - self.cancelNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) - + self.updateItem() } - - @objc private func cancelPressed() { - self.item.cancel() - } - + override func didLoad() { super.didLoad() @@ -128,23 +112,18 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { } func updateItem() { - self.backgroundNode.updateColor(color: self.item.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.separatorNode.backgroundColor = self.item.theme.rootController.navigationBar.separatorColor - let attributedTitle = NSAttributedString(string: self.item.title, font: titleFont, textColor: self.item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) let attributedText = NSAttributedString(string: self.item.text, font: textFont, textColor: self.item.theme.list.freeTextColor, paragraphAlignment: .center) self.titleNode.attributedText = attributedTitle self.textNode.attributedText = attributedText - - self.cancelNode.setAttributedTitle(NSAttributedString(string: self.item.strings.Common_Cancel, font: Font.regular(17.0), textColor: self.item.theme.rootController.navigationBar.accentTextColor), for: .normal) } override func updateContentOffset(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) { guard let layout = self.validLayout else { return } - let navigationHeight: CGFloat = 56.0 + let navigationHeight: CGFloat = 76.0 let statusBarHeight = layout.statusBarHeight ?? 0.0 let topInset : CGFloat = 0.0 @@ -155,10 +134,6 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta)) let titleScale = 1.0 - fraction * 0.18 - let topPanelAlpha = min(20.0, max(0.0, contentOffset - 95.0)) / 20.0 - transition.updateAlpha(node: self.backgroundNode, alpha: topPanelAlpha) - transition.updateAlpha(node: self.separatorNode, alpha: topPanelAlpha) - let starPosition = CGPoint( x: layout.size.width / 2.0, y: -contentOffset + 80.0 @@ -185,18 +160,34 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode { override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat { let leftInset: CGFloat = 24.0 - let navigationBarHeight: CGFloat = 56.0 let constrainedSize = CGSize(width: layout.size.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude) let titleSize = self.titleNode.updateLayout(constrainedSize) let textSize = self.textNode.updateLayout(constrainedSize) - - let cancelSize = self.cancelNode.measure(constrainedSize) - transition.updateFrame(node: self.cancelNode, frame: CGRect(origin: CGPoint(x: 16.0 + layout.safeInsets.left, y: floorToScreenPixels((navigationBarHeight - cancelSize.height) / 2.0)), size: cancelSize)) - - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight))) - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel))) - - self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition) + + let cancelButtonSize = self.cancelButton.update( + transition: .immediate, + component: AnyComponent( + GlassBarButtonComponent( + size: CGSize(width: 44.0, height: 44.0), + backgroundColor: nil, + isDark: self.item.theme.overallDarkAppearance, + state: .glass, + component: AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Navigation/Close", tintColor: self.item.theme.chat.inputPanel.panelControlColor))), + action: { [weak self] _ in + self?.item.cancel() + } + ) + ), + environment: {}, + containerSize: CGSize(width: 44.0, height: 44.0) + ) + let cancelButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: cancelButtonSize) + if let cancelButtonView = self.cancelButton.view { + if cancelButtonView.superview == nil { + self.view.addSubview(cancelButtonView) + } + cancelButtonView.frame = cancelButtonFrame + } let colors: [UIColor] let particleColor: UIColor? diff --git a/submodules/PremiumUI/Sources/GiftOptionItem.swift b/submodules/PremiumUI/Sources/GiftOptionItem.swift index eac888a1f6..c11806ae8d 100644 --- a/submodules/PremiumUI/Sources/GiftOptionItem.swift +++ b/submodules/PremiumUI/Sources/GiftOptionItem.swift @@ -53,6 +53,7 @@ public final class GiftOptionItem: ListViewItem, ItemListItem { } let presentationData: ItemListPresentationData + let systemStyle: ItemListSystemStyle let context: AccountContext let icon: Icon? let title: String @@ -68,8 +69,9 @@ public final class GiftOptionItem: ListViewItem, ItemListItem { public let sectionId: ItemListSectionId let action: (() -> Void)? - public init(presentationData: ItemListPresentationData, context: AccountContext, icon: Icon? = nil, title: String, titleFont: Font = .regular, titleBadge: String? = nil, subtitle: String?, subtitleFont: SubtitleFont = .regular, subtitleActive: Bool = false, label: Label? = nil, badge: String? = nil, isSelected: Bool? = nil, stars: Int64? = nil, sectionId: ItemListSectionId, action: (() -> Void)?) { + public init(presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .legacy, context: AccountContext, icon: Icon? = nil, title: String, titleFont: Font = .regular, titleBadge: String? = nil, subtitle: String?, subtitleFont: SubtitleFont = .regular, subtitleActive: Bool = false, label: Label? = nil, badge: String? = nil, isSelected: Bool? = nil, stars: Int64? = nil, sectionId: ItemListSectionId, action: (() -> Void)?) { self.presentationData = presentationData + self.systemStyle = systemStyle self.icon = icon self.context = context self.title = title @@ -518,7 +520,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size) diff --git a/submodules/PremiumUI/Sources/PremiumBoostScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostScreen.swift deleted file mode 100644 index 00a0a4caa5..0000000000 --- a/submodules/PremiumUI/Sources/PremiumBoostScreen.swift +++ /dev/null @@ -1,280 +0,0 @@ -import Foundation -import UIKit -import Display -import SwiftSignalKit -import TelegramCore -import AccountContext -import TelegramPresentationData -import UndoUI -import PresentationDataUtils - -private struct BoostState { - let level: Int32 - let currentLevelBoosts: Int32 - let nextLevelBoosts: Int32? - let boosts: Int32 - - func displayData(peer: EnginePeer, isCurrent: Bool, canBoostAgain: Bool, myBoostCount: Int32, currentMyBoostCount: Int32, replacedBoosts: Int32? = nil) -> (subject: PremiumLimitScreen.Subject, count: Int32) { - var currentLevel = self.level - var nextLevelBoosts = self.nextLevelBoosts - var currentLevelBoosts = self.currentLevelBoosts - var boosts = self.boosts - if let replacedBoosts { - boosts = max(currentLevelBoosts, boosts - replacedBoosts) - } - - if currentMyBoostCount > 0 && self.boosts == currentLevelBoosts { - currentLevel = max(0, currentLevel - 1) - nextLevelBoosts = currentLevelBoosts - currentLevelBoosts = max(0, currentLevelBoosts - 1) - } - - return ( - .storiesChannelBoost( - peer: peer, - boostSubject: .stories, - isCurrent: isCurrent, - level: currentLevel, - currentLevelBoosts: currentLevelBoosts, - nextLevelBoosts: nextLevelBoosts, - link: nil, - myBoostCount: myBoostCount, - canBoostAgain: canBoostAgain - ), - boosts - ) - } -} - -public func PremiumBoostScreen( - context: AccountContext, - contentContext: Any?, - peerId: EnginePeer.Id, - isCurrent: Bool, - status: ChannelBoostStatus?, - myBoostStatus: MyBoostStatus?, - replacedBoosts: (Int32, Int32)? = nil, - forceDark: Bool, - openPeer: @escaping (EnginePeer) -> Void, - presentController: @escaping (ViewController) -> Void, - pushController: @escaping (ViewController) -> Void, - dismissed: @escaping () -> Void -) { - let _ = (context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), - TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId) - ) - |> deliverOnMainQueue).startStandalone(next: { peer, accountPeer in - guard let peer, let accountPeer, let status else { - return - } - - let isPremium = accountPeer.isPremium - - var myBoostCount: Int32 = 0 - var currentMyBoostCount: Int32 = 0 - var availableBoosts: [MyBoostStatus.Boost] = [] - var occupiedBoosts: [MyBoostStatus.Boost] = [] - if let myBoostStatus { - for boost in myBoostStatus.boosts { - if let boostPeer = boost.peer { - if boostPeer.id == peer.id { - myBoostCount += 1 - } else { - occupiedBoosts.append(boost) - } - } else { - availableBoosts.append(boost) - } - } - } - - let boosts = max(Int32(status.boosts), myBoostCount) - let initialState = BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts) - let updatedState = Promise() - updatedState.set(.single(BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts + 1))) - - var updateImpl: (() -> Void)? - var dismissImpl: (() -> Void)? - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with({ $0 })) - let canBoostAgain = premiumConfiguration.boostsPerGiftCount > 0 - - let (initialSubject, initialCount) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 0, replacedBoosts: replacedBoosts?.0) - let controller = PremiumLimitScreen(context: context, subject: initialSubject, count: initialCount, forceDark: forceDark, action: { - let dismiss = false - updateImpl?() - return dismiss - }, - openPeer: { peer in - openPeer(peer) - }) - pushController(controller) - - if let (replacedBoosts, inChannels) = replacedBoosts { - currentMyBoostCount += 1 - let (subject, count) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 1, replacedBoosts: nil) - controller.updateSubject(subject, count: count) - - Queue.mainQueue().after(0.3) { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), presentationData.strings.ReassignBoost_OtherChannels(inChannels)).string, round: false, undoText: nil), elevatedLayout: false, position: .bottom, action: { _ in return true }) - controller.present(undoController, in: .current) - } - } - - controller.disposed = { - dismissed() - } - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - updateImpl = { [weak controller] in - if let _ = status.nextLevelBoosts { - if let availableBoost = availableBoosts.first { - currentMyBoostCount += 1 - myBoostCount += 1 - - let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]) - |> deliverOnMainQueue).startStandalone(completed: { - updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId) - |> map { status in - if let status { - return BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1)) - } else { - return nil - } - }) - }) - - let _ = (updatedState.get() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { state in - guard let state else { - return - } - let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount) - controller?.updateSubject(subject, count: count) - }) - - availableBoosts.removeFirst() - } else if !occupiedBoosts.isEmpty, let myBoostStatus { - if canBoostAgain { - var dismissReplaceImpl: (() -> Void)? - let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in - var channelIds = Set() - for boost in myBoostStatus.boosts { - if slots.contains(boost.slot) { - if let peer = boost.peer { - channelIds.insert(peer.id) - } - } - } - - let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone(completed: { - let _ = combineLatest( - queue: Queue.mainQueue(), - context.engine.peers.getChannelBoostStatus(peerId: peerId), - context.engine.peers.getMyBoostStatus() - ).startStandalone(next: { boostStatus, myBoostStatus in - dismissReplaceImpl?() - PremiumBoostScreen(context: context, contentContext: contentContext, peerId: peerId, isCurrent: isCurrent, status: boostStatus, myBoostStatus: myBoostStatus, replacedBoosts: (Int32(slots.count), Int32(channelIds.count)), forceDark: forceDark, openPeer: openPeer, presentController: presentController, pushController: pushController, dismissed: dismissed) - }) - }) - }) - dismissImpl?() - pushController(replaceController) - dismissReplaceImpl = { [weak replaceController] in - replaceController?.dismiss(animated: true) - } - } else if let boost = occupiedBoosts.first, let occupiedPeer = boost.peer { - if let cooldown = boost.cooldownUntil { - let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) - let timeout = cooldown - currentTime - let valueText = timeIntervalString(strings: presentationData.strings, value: timeout, usage: .afterTime, preferLowerValue: false) - let controller = textAlertController( - sharedContext: context.sharedContext, - updatedPresentationData: nil, - title: presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle, - text: presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string, - actions: [ - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) - ], - parseMarkdown: true - ) - presentController(controller) - } else { - let replaceController = replaceBoostConfirmationController(context: context, fromPeers: [occupiedPeer], toPeer: peer, commit: { - currentMyBoostCount += 1 - myBoostCount += 1 - let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [boost.slot]) - |> deliverOnMainQueue).startStandalone(completed: { [weak controller] in - let _ = (updatedState.get() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak controller] state in - guard let state else { - return - } - let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount) - controller?.updateSubject(subject, count: count) - }) - }) - }) - presentController(replaceController) - } - } else { - dismissImpl?() - } - } else { - if isPremium { - if !canBoostAgain { - dismissImpl?() - } else { - let controller = textAlertController( - sharedContext: context.sharedContext, - updatedPresentationData: nil, - title: presentationData.strings.ChannelBoost_MoreBoosts_Title, - text: presentationData.strings.ChannelBoost_MoreBoosts_Text(peer.compactDisplayTitle, "\(premiumConfiguration.boostsPerGiftCount)").string, - actions: [ - TextAlertAction(type: .defaultAction, title: presentationData.strings.ChannelBoost_MoreBoosts_Gift, action: { - dismissImpl?() - - Queue.mainQueue().after(0.4) { - let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil) - pushController(controller) - } - }), - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Close, action: {}) - ], - actionLayout: .vertical, - parseMarkdown: true - ) - presentController(controller) - } - } else { - let controller = textAlertController( - sharedContext: context.sharedContext, - updatedPresentationData: nil, - title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle, - text: presentationData.strings.ChannelBoost_Error_PremiumNeededText, - actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { - dismissImpl?() - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .channelBoost(peerId), forceDark: forceDark, dismissed: nil) - pushController(controller) - }) - ], - parseMarkdown: true - ) - presentController(controller) - } - } - } else { - dismissImpl?() - } - } - dismissImpl = { [weak controller] in - controller?.dismissAnimated() - } - }) -} diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index f77c5ed42a..ddd15e4f70 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -1307,6 +1307,8 @@ private final class DemoSheetContent: CombinedComponent { buttonAnimationName = "premium_unlock" case .todo: buttonText = strings.Premium_PaidMessages_Proceed + case .copyProtection: + buttonText = strings.Premium_PaidMessages_Proceed default: buttonText = strings.Common_OK } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 30fe62074e..e0999df159 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1635,6 +1635,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { super.init() + self.newPerks = [PremiumPerk.copyProtection.identifier] + let premiumIntroConfiguration: Signal let accountPeer: Signal switch screenContext { diff --git a/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift b/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift index 861c43d7e7..b78317fec1 100644 --- a/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift +++ b/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift @@ -12,14 +12,16 @@ import PresentationDataUtils final class SubscriptionsCountItem: ListViewItem, ItemListItem { let theme: PresentationTheme let strings: PresentationStrings + let systemStyle: ItemListSystemStyle let value: Int32 let values: [Int32] let sectionId: ItemListSectionId let updated: (Int32) -> Void - init(theme: PresentationTheme, strings: PresentationStrings, value: Int32, values: [Int32], sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, systemStyle: ItemListSystemStyle = .legacy, value: Int32, values: [Int32], sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) { self.theme = theme self.strings = strings + self.systemStyle = systemStyle self.value = value self.values = values self.sectionId = sectionId @@ -229,7 +231,7 @@ private final class SubscriptionsCountItemNode: ListViewItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift index 82129ad887..8e667f296e 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift @@ -90,6 +90,7 @@ final class AutodownloadDataUsagePickerItem: ListViewItem, ItemListItem { private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode, ItemListItemNode { private let backgroundNode: ASDisplayNode + private let highlightNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let maskNode: ASImageNode @@ -113,6 +114,9 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode, ItemL self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true + self.highlightNode = ASDisplayNode() + self.highlightNode.isLayerBacked = true + self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true @@ -160,6 +164,20 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode, ItemL // } } + public func displayHighlight() { + if self.backgroundNode.supernode != nil { + self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode) + } else { + self.insertSubnode(self.highlightNode, at: 0) + } + + Queue.mainQueue().after(1.2, { + self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + self.highlightNode.removeFromSupernode() + }) + }) + } + func asyncLayout() -> (_ item: AutodownloadDataUsagePickerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeLowTextLayout = TextNode.asyncLayout(self.lowTextNode) let makeMediumTextLayout = TextNode.asyncLayout(self.mediumTextNode) @@ -193,6 +211,7 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode, ItemL strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.highlightNode.backgroundColor = item.theme.list.itemSearchHighlightColor if strongSelf.backgroundNode.supernode == nil { strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) @@ -234,6 +253,7 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode, ItemL strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) + strongSelf.highlightNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index 22484f4a11..e058f8985a 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -389,7 +389,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry { if enabled { arguments.resetAutomaticDownload() } - }) + }, tag: DataAndStorageEntryTag.automaticDownloadReset) case let .autoSaveHeader(text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .autoSaveItem(_, type, title, label, value): diff --git a/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift index 7b759464c4..4ea381815b 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift @@ -227,11 +227,11 @@ private enum IntentsSettingsControllerEntry: ItemListNodeEntry { case let .chatsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .suggestHeader(_, text): - return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section, tag: IntentsEntryTag.suggestBy) case let .suggestAll(_, text, value): return ItemListCheckboxItem(presentationData: presentationData, systemStyle: .glass, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.updateSettings { $0.withUpdatedOnlyShared(false) } - }, tag: IntentsEntryTag.suggestBy) + }) case let .suggestOnlyShared(_, text, value): return ItemListCheckboxItem(presentationData: presentationData, systemStyle: .glass, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.updateSettings { $0.withUpdatedOnlyShared(true) } diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift index a35d7821f9..2f7c2b0a88 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift @@ -81,6 +81,7 @@ final class ProxySettingsActionItem: ListViewItem, ItemListItem { private final class ProxySettingsActionItemNode: ListViewItemNode, ItemListItemNode { private let backgroundNode: ASDisplayNode + private let highlightNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode @@ -99,6 +100,9 @@ private final class ProxySettingsActionItemNode: ListViewItemNode, ItemListItemN self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true + self.highlightNode = ASDisplayNode() + self.highlightNode.isLayerBacked = true + self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true @@ -128,6 +132,20 @@ private final class ProxySettingsActionItemNode: ListViewItemNode, ItemListItemN self.addSubnode(self.titleNode) } + public func displayHighlight() { + if self.backgroundNode.supernode != nil { + self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode) + } else { + self.insertSubnode(self.highlightNode, at: 0) + } + + Queue.mainQueue().after(1.2, { + self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + self.highlightNode.removeFromSupernode() + }) + }) + } + func asyncLayout() -> (_ item: ProxySettingsActionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) @@ -177,6 +195,7 @@ private final class ProxySettingsActionItemNode: ListViewItemNode, ItemListItemN strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor + strongSelf.highlightNode.backgroundColor = item.presentationData.theme.list.itemSearchHighlightColor } let _ = titleApply() @@ -234,6 +253,7 @@ private final class ProxySettingsActionItemNode: ListViewItemNode, ItemListItemN strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) + strongSelf.highlightNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset - params.rightInset - separatorRightInset, height: separatorHeight))) diff --git a/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift b/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift index ff09f55252..984938fda7 100644 --- a/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift +++ b/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift @@ -295,7 +295,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry { case let .previews(_, text, value): return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.updatePreviews(value) - }) + }, tag: self.tag) case let .sound(_, text, value, sound): return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.openSound(sound) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 6f5a54cde6..0d423438f9 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -389,10 +389,11 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue { currentAmount = value } + let fractionAfterCommission = configuration.paidMessageCommissionPermille / 10 let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount( current: currentAmount, minValue: StarsAmount(value: 1, nanos: 0), - fractionAfterCommission: 80, kind: .privacy, + fractionAfterCommission: Int(fractionAfterCommission), kind: .privacy, completion: { amount in updateState { state in var state = state diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift index c20613b23e..0730050b3b 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -359,7 +359,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry { case let .terminateAllWebSessions(_, text): return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.blockDestructiveIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: { arguments.terminateAllWebSessions() - }) + }, tag: RecentSessionsEntryTag.terminateOtherSessions) case let .currentAddDevice(_, text): return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.addDeviceIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: { arguments.addDevice() @@ -880,7 +880,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: emptyStateItem, crossfadeState: crossfadeState, animateChanges: animateChanges, scrollEnabled: emptyStateItem == nil) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: nil, emptyStateItem: emptyStateItem, crossfadeState: crossfadeState, animateChanges: animateChanges, scrollEnabled: emptyStateItem == nil) return (controllerState, (listState, arguments)) } |> afterDisposed { @@ -910,10 +910,14 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont var didFocusOnItem = false controller.afterTransactionCompleted = { [weak controller] in if !didFocusOnItem, let controller { - controller.forEachItemNode { itemNode in - if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag, tag.isEqual(to: focusOnItemTag) { + controller.forEachItemNode { [weak controller] listItemNode in + if let itemNode = listItemNode as? ItemListItemNode, let tag = itemNode.tag, tag.isEqual(to: focusOnItemTag) { didFocusOnItem = true itemNode.displayHighlight() + + Queue.mainQueue().after(0.3) { + controller?.ensureItemNodeVisible(listItemNode) + } } } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index ea4cafbc9f..8c9b007e92 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -638,7 +638,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { case let .peersInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .callsP2PHeader(_, text): - return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section, tag: SelectivePrivacyEntryTag.callsP2P) case let .callsP2PAlways(_, text, value): return ItemListCheckboxItem(presentationData: presentationData, systemStyle: .glass, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.updateCallP2PMode?(.everybody) diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 67ef6d1348..92cf70cf2f 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -1422,7 +1422,7 @@ private func stickerSearchableItems(context: AccountContext, archivedStickerPack } items.append( SettingsSearchableItem( - id: "appearance/stickers-and-emoji/large", + id: "appearance/stickers-and-emoji/large-emoji", title: strings.Appearance_LargeEmoji, alternate: synonyms(strings.SettingsSearch_Synonyms_Appearance_LargeEmoji), icon: icon, @@ -1474,7 +1474,7 @@ private func stickerSearchableItems(context: AccountContext, archivedStickerPack icon: icon, breadcrumbs: [strings.StickerPacksSettings_Emoji], present: { context, _, present in - presentStickerSettings(context, present, .emoji, .suggestOptions) + presentStickerSettings(context, present, .emoji, .suggestAnimatedEmoji) } ) ) @@ -2160,10 +2160,10 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac present(.push, dataPrivacyController(context: context, focusOnItemTag: itemTag)) } - let presentBlockUser: (AccountContext, (SettingsSearchableItemPresentation, ViewController?) -> Void) -> Void = { context, present in + let presentBlockUser: (AccountContext, (SettingsSearchableItemPresentation, ViewController?) -> Void, Bool) -> Void = { context, present, switchToContacts in let blockedPeersContext = BlockedPeersContext(account: context.account, subject: .blocked) let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeSavedMessages, .removeSearchHeader, .excludeRecent, .doNotSearchMessages], title: presentationData.strings.BlockedUsers_SelectUserTitle)) + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeSavedMessages, .removeSearchHeader, .excludeRecent, .doNotSearchMessages], title: presentationData.strings.BlockedUsers_SelectUserTitle, immediatelySwitchToContacts: switchToContacts)) controller.peerSelected = { [weak controller] peer, _ in let peerId = peer.id @@ -2242,7 +2242,7 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac breadcrumbs: [strings.Settings_PrivacySettings, strings.Settings_BlockedUsers], isVisible: false, present: { context, _, present in - presentBlockUser(context, present) + presentBlockUser(context, present, false) } ) ) @@ -2253,7 +2253,7 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac breadcrumbs: [strings.Settings_PrivacySettings, strings.Settings_BlockedUsers], isVisible: false, present: { context, _, present in - presentBlockUser(context, present) + presentBlockUser(context, present, false) } ) ) @@ -2264,7 +2264,7 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac breadcrumbs: [strings.Settings_PrivacySettings, strings.Settings_BlockedUsers], isVisible: false, present: { context, _, present in - presentBlockUser(context, present) + presentBlockUser(context, present, true) } ) ) @@ -3329,7 +3329,7 @@ private func dataSearchableItems(context: AccountContext) -> [SettingsSearchable } ), SettingsSearchableItem( - id: "data/max-cache", + id: "data/storage/max-cache", icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], isVisible: false, @@ -3621,7 +3621,7 @@ private func dataSearchableItems(context: AccountContext) -> [SettingsSearchable } ), SettingsSearchableItem( - id: "data/use-less-data", + id: "data/less-data-calls", title: strings.CallSettings_UseLessData, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_CallsUseLessData), icon: icon, @@ -3754,7 +3754,7 @@ private func proxySearchableItems(context: AccountContext, servers: [ProxyServer ) items.append( SettingsSearchableItem( - id: "data/use-proxy", + id: "data/proxy/use-proxy", icon: icon, isVisible: false, present: { context, _, present in @@ -3988,7 +3988,7 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Themes_Title], present: { context, _, present in - let controller = themePickerController(context: context) + let controller = themePickerController(context: context, focusOnItemTag: .edit) present(.push, controller) } ), @@ -4388,17 +4388,7 @@ func settingsSearchableItems( let activeWebSessionsContext = webSessionsContext |> mapToSignal { webSessionsContext -> Signal in if let webSessionsContext = webSessionsContext { - return webSessionsContext.state - |> map { state -> WebSessionsContext? in - if !state.sessions.isEmpty { - return webSessionsContext - } else { - return nil - } - } - |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs !== rhs - }) + return .single(webSessionsContext) } else { return .single(nil) } @@ -4417,6 +4407,7 @@ func settingsSearchableItems( activeSessionsContext, activeWebSessionsContext ) + |> deliverOnMainQueue |> map { canAddAccount, localizations, @@ -4615,7 +4606,29 @@ func searchSettingsItems(items: [SettingsSearchableItem], query: String) -> [Set } public func handleSettingsPathUrl(context: AccountContext, path: String, navigationController: NavigationController) { - let _ = (settingsSearchableItems(context: context) + var notificationExceptionsList: Signal = .single(nil) + var activeSessionsContext: Signal = .single(nil) + var webSessionsContext: Signal = .single(nil) + + if path.hasPrefix("devices") { + activeSessionsContext = .single(context.engine.privacy.activeSessions()) + } else if path.hasPrefix("privacy/active-websites") { + webSessionsContext = .single(context.engine.privacy.webSessions()) + } else if path.hasPrefix("notifications") { + if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface, let current = rootController.getNotificationExceptions() { + notificationExceptionsList = current.get() + } else { + notificationExceptionsList = context.engine.peers.notificationExceptionsList() + |> map(Optional.init) + } + } + + let _ = (settingsSearchableItems( + context: context, + notificationExceptionsList: notificationExceptionsList, + activeSessionsContext: activeSessionsContext, + webSessionsContext: webSessionsContext + ) |> take(1) |> deliverOnMainQueue).start(next: { items in guard let item = items.first(where: { $0.id == AnyHashable(path) }) else { diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 00dbd99fc1..02355eb3f1 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -71,6 +71,7 @@ public enum InstalledStickerPacksEntryTag: ItemListItemTag { case suggestOptions case largeEmoji case dynamicOrder + case suggestAnimatedEmoji public func isEqual(to other: ItemListItemTag) -> Bool { if let other = other as? InstalledStickerPacksEntryTag, self == other { @@ -430,7 +431,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { case let .suggestAnimatedEmoji(text, value): return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleSuggestAnimatedEmoji(value) - }) + }, tag: InstalledStickerPacksEntryTag.suggestAnimatedEmoji) case let .suggestAnimatedEmojiInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .packsTitle(_, text): diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index 475bd10a4a..24c0d62467 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -275,7 +275,7 @@ private enum ThemePickerControllerEntry: ItemListNodeEntry { case let .editTheme(theme, text): return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.editThemeIcon(theme), title: text, sectionId: self.section, height: .generic, editing: false, action: { arguments.editCurrentTheme() - }) + }, tag: ThemeSettingsEntryTag.edit) case let .createTheme(theme, text): return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, height: .generic, editing: false, action: { arguments.createNewTheme() @@ -1294,6 +1294,21 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme presentCrossfadeControllerImpl?(true) }) } + + if let focusOnItemTag { + var didFocusOnItem = false + controller.afterTransactionCompleted = { [weak controller] in + if !didFocusOnItem, let controller { + controller.forEachItemNode { itemNode in + if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag, tag.isEqual(to: focusOnItemTag) { + didFocusOnItem = true + itemNode.displayHighlight() + } + } + } + } + } + return controller } @@ -1326,7 +1341,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } private func iconColors(theme: PresentationTheme) -> [String: UIColor] { - let accentColor = theme.actionSheet.controlAccentColor + let accentColor = theme.chat.inputPanel.panelControlColor var colors: [String: UIColor] = [:] colors["Sunny.Path 14.Path.Stroke 1"] = accentColor colors["Sunny.Path 15.Path.Stroke 1"] = accentColor diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 9b85255901..5e35e60c0d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -110,6 +110,7 @@ public enum ThemeSettingsEntryTag: ItemListItemTag { case sendWithCmdEnter case tapForNextMedia case nightMode + case edit public func isEqual(to other: ItemListItemTag) -> Bool { if let other = other as? ThemeSettingsEntryTag, self == other { diff --git a/submodules/StatisticsUI/Sources/BoostHeaderItem.swift b/submodules/StatisticsUI/Sources/BoostHeaderItem.swift index 34f192ec51..1588f1a1fb 100644 --- a/submodules/StatisticsUI/Sources/BoostHeaderItem.swift +++ b/submodules/StatisticsUI/Sources/BoostHeaderItem.swift @@ -14,6 +14,7 @@ import MultilineTextComponent import BundleIconComponent import PlainButtonComponent import AccountContext +import GlassBackgroundComponent final class BoostHeaderItem: ItemListControllerHeaderItem { let context: AccountContext @@ -291,9 +292,9 @@ private final class BoostHeaderComponent: CombinedComponent { let progress = Child(PremiumLimitDisplayComponent.self) let text = Child(MultilineTextComponent.self) - let boostButton = Child(PlainButtonComponent.self) - let giveawayButton = Child(PlainButtonComponent.self) - let featuresButton = Child(PlainButtonComponent.self) + let boostButton = Child(HeaderButtonComponent.self) + let giveawayButton = Child(HeaderButtonComponent.self) + let featuresButton = Child(HeaderButtonComponent.self) return { context in let size = context.availableSize @@ -393,18 +394,14 @@ private final class BoostHeaderComponent: CombinedComponent { ) let minButtonWidth: CGFloat = 112.0 -// let buttonSpacing = (size.width - sideInset * 2.0 - minButtonWidth * 3.0) / 2.0 let buttonHeight: CGFloat = 58.0 - + let buttonColor = UIColor(rgb: 0x908eff) + let boostButton = boostButton.update( - component: PlainButtonComponent( - content: AnyComponent( - BoostButtonComponent( - iconName: "Premium/Boosts/Boost", - title: component.strings.ChannelBoost_Header_Boost - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: component.strings.ChannelBoost_Header_Boost, + buttonColor: buttonColor, + iconName: "Premium/Boosts/Boost", action: { component.openBoost() } @@ -417,14 +414,10 @@ private final class BoostHeaderComponent: CombinedComponent { ) let giveawayButton = giveawayButton.update( - component: PlainButtonComponent( - content: AnyComponent( - BoostButtonComponent( - iconName: "Premium/Boosts/Giveaway", - title: component.strings.ChannelBoost_Header_Giveaway - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: component.strings.ChannelBoost_Header_Giveaway, + buttonColor: buttonColor, + iconName: "Premium/Boosts/Giveaway", action: { component.createGiveaway() } @@ -437,14 +430,10 @@ private final class BoostHeaderComponent: CombinedComponent { ) let featuresButton = featuresButton.update( - component: PlainButtonComponent( - content: AnyComponent( - BoostButtonComponent( - iconName: "Premium/Boosts/Features", - title: component.strings.ChannelBoost_Header_Features - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: component.strings.ChannelBoost_Header_Features, + buttonColor: buttonColor, + iconName: "Premium/Boosts/Features", action: { component.openFeatures() } @@ -537,3 +526,167 @@ private final class BoostButtonComponent: CombinedComponent { } } } + +private final class HeaderButtonComponent: Component { + let title: String + let buttonColor: UIColor + let iconName: String + let isLocked: Bool + let action: () -> Void + + public init( + title: String, + buttonColor: UIColor, + iconName: String, + isLocked: Bool = false, + action: @escaping () -> Void + ) { + self.title = title + self.buttonColor = buttonColor + self.iconName = iconName + self.isLocked = isLocked + self.action = action + } + + static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool { + if lhs.title != rhs.title { + return false + } + if lhs.buttonColor != rhs.buttonColor { + return false + } + if lhs.iconName != rhs.iconName { + return false + } + if lhs.isLocked != rhs.isLocked { + return false + } + return true + } + + final class View: UIView { + private var component: HeaderButtonComponent? + private weak var componentState: EmptyComponentState? + + private let backgroundView = GlassBackgroundView() + private let title = ComponentView() + private let icon = ComponentView() + private let lockIcon = ComponentView() + private let button = HighlightTrackingButton() + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addSubview(self.button) + + self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func buttonPressed() { + if let component = self.component { + component.action() + } + } + + func update(component: HeaderButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.componentState = state + + let bounds = CGRect(origin: .zero, size: availableSize) + + self.backgroundView.update(size: bounds.size, cornerRadius: 16.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)), isInteractive: true, transition: transition) + transition.setFrame(view: self.backgroundView, frame: bounds) + + let iconSize = self.icon.update( + transition: transition, + component: AnyComponent( + BundleIconComponent( + name: component.iconName, + tintColor: UIColor.white + ) + ), + environment: {}, + containerSize: availableSize + ) + if let iconView = self.icon.view { + if iconView.superview == nil { + iconView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(iconView) + } + iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: floorToScreenPixels(22.0 - iconSize.height * 0.5)), size: iconSize) + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.title, + font: Font.regular(11.0), + textColor: UIColor.white, + paragraphAlignment: .natural + )), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - 16.0, height: availableSize.height) + ) + + var totalTitleWidth = titleSize.width + var titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0 + if component.isLocked { + let titleSpacing: CGFloat = 3.0 + + let lockIconSize = self.lockIcon.update( + transition: transition, + component: AnyComponent( + BundleIconComponent( + name: "Chat List/StatusLockIcon", + tintColor: .white + ) + ), + environment: {}, + containerSize: availableSize + ) + totalTitleWidth += lockIconSize.width + titleSpacing + titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0 + + if let lockIconView = self.lockIcon.view { + if lockIconView.superview == nil { + lockIconView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(lockIconView) + } + lockIconView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - lockIconSize.height * 0.5)), size: lockIconSize) + } + titleOriginX += lockIconSize.width + titleSpacing + } + + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(titleView) + } + titleView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - titleSize.height * 0.5)), size: titleSize) + } + + self.button.frame = bounds + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 0eee76ebcc..a0f5a6df6b 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -932,7 +932,7 @@ private enum StatsEntry: ItemListNodeEntry { let .adsProceedsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) case let .overview(_, stats): - return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: false, stats: stats, sectionId: self.section, style: .blocks) + return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: false, stats: stats, sectionId: self.section, style: .blocks) case let .growthGraph(_, _, _, graph, type), let .followersGraph(_, _, _, graph, type), let .notificationsGraph(_, _, _, graph, type), @@ -943,15 +943,15 @@ private enum StatsEntry: ItemListNodeEntry { let .reactionsByEmotionGraph(_, _, _, graph, type), let .storyReactionsByEmotionGraph(_, _, _, graph, type), let .adsImpressionsGraph(_, _, _, graph, type): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks) + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, sectionId: self.section, style: .blocks) case let .adsTonRevenueGraph(_, _, _, graph, type, rate): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks) + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks) case let .adsStarsRevenueGraph(_, _, _, graph, type, rate): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks) + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks) case let .postInteractionsGraph(_, _, _, graph, type), let .instantPageInteractionsGraph(_, _, _, graph, type), let .storyInteractionsGraph(_, _, _, graph, type): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, getDetailsData: { date, completion in let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in if let graph = graph, case let .Loaded(_, data) = graph { completion(data) @@ -1035,7 +1035,7 @@ private enum StatsEntry: ItemListNodeEntry { arguments.openBoost(boost) }) case let .boostersExpand(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandBoosters() }) case let .boostLevel(_, count, level, position): @@ -1046,15 +1046,15 @@ private enum StatsEntry: ItemListNodeEntry { return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: isGroup, stats: stats, sectionId: self.section, style: .blocks) case let .boostLink(_, link): let invite: ExportedInvitation = .link(link: link, title: nil, isPermanent: false, requestApproval: false, isRevoked: false, adminId: PeerId(0), date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil) - return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: { + return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: { arguments.copyBoostLink(link) }, shareAction: { arguments.shareBoostLink(link) }, contextAction: nil, viewAction: nil, openCallAction: nil, tag: nil) case let .boostersPlaceholder(_, text): - return ItemListPlaceholderItem(theme: presentationData.theme, text: text, sectionId: self.section, style: .blocks) + return ItemListPlaceholderItem(theme: presentationData.theme, systemStyle: .glass, text: text, sectionId: self.section, style: .blocks) case let .boostGifts(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addBoostsIcon(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.addBoostsIcon(theme), title: title, sectionId: self.section, editing: false, action: { arguments.openGifts() }) case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway): @@ -1200,7 +1200,7 @@ private enum StatsEntry: ItemListNodeEntry { label.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: label.string)) } - return ItemListDisclosureItem(presentationData: presentationData, title: "", attributedTitle: title, label: "", attributedLabel: label, labelStyle: .coloredText(labelColor), additionalDetailLabel: detailText, additionalDetailLabelColor: detailColor, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: "", attributedTitle: title, label: "", attributedLabel: label, labelStyle: .coloredText(labelColor), additionalDetailLabel: detailText, additionalDetailLabelColor: detailColor, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: { arguments.openTonTransaction(transaction) }) case let .adsStarsTransaction(_, _, transaction): @@ -1208,7 +1208,7 @@ private enum StatsEntry: ItemListNodeEntry { arguments.openStarsTransaction(transaction) }, sectionId: self.section, style: .blocks) case let .adsTransactionsExpand(theme, title, stars): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandTransactions(stars) }) case let .adsCpmToggle(_, title, minLevel, value): @@ -1219,7 +1219,7 @@ private enum StatsEntry: ItemListNodeEntry { level: Int(minLevel) )) } - return ItemListSwitchItem(presentationData: presentationData, title: title, titleBadgeComponent: badgeComponent, value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, titleBadgeComponent: badgeComponent, value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in if value != nil { arguments.updateCpmEnabled(updatedValue) } else { @@ -1229,7 +1229,7 @@ private enum StatsEntry: ItemListNodeEntry { arguments.presentCpmLocked() }) case .earnStarsInfo: - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: { arguments.openEarnStars() }) } diff --git a/submodules/StatisticsUI/Sources/GroupStatsController.swift b/submodules/StatisticsUI/Sources/GroupStatsController.swift index 324a0a5eaa..7109e9d382 100644 --- a/submodules/StatisticsUI/Sources/GroupStatsController.swift +++ b/submodules/StatisticsUI/Sources/GroupStatsController.swift @@ -381,7 +381,7 @@ private enum StatsEntry: ItemListNodeEntry { let .topInvitersTitle(_, text, dates): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section) case let .overview(_, stats): - return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: true, stats: stats, sectionId: self.section, style: .blocks) + return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: true, stats: stats, sectionId: self.section, style: .blocks) case let .growthGraph(_, _, _, graph, type), let .membersGraph(_, _, _, graph, type), let .newMembersBySourceGraph(_, _, _, graph, type), @@ -390,7 +390,7 @@ private enum StatsEntry: ItemListNodeEntry { let .actionsGraph(_, _, _, graph, type), let .topHoursGraph(_, _, _, graph, type), let .topWeekdaysGraph(_, _, _, graph, type): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks) + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, sectionId: self.section, style: .blocks) case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed, canPromote): var textComponents: [String] = [] if topPoster.messageCount > 0 { @@ -410,13 +410,13 @@ private enum StatsEntry: ItemListNodeEntry { })) } } - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId) }, removePeer: { _ in }) case let .topPostersExpand(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandTopPosters() }) case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed, canPromote): @@ -441,13 +441,13 @@ private enum StatsEntry: ItemListNodeEntry { })) } } - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId) }, removePeer: { _ in }) case let .topAdminsExpand(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandTopAdmins() }) case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed, canPromote): @@ -464,13 +464,13 @@ private enum StatsEntry: ItemListNodeEntry { })) } } - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId) }, removePeer: { _ in }) case let .topInvitersExpand(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandTopInviters() }) } diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index 2e8e688e4d..a1d3cf316b 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -160,9 +160,9 @@ private enum StatsEntry: ItemListNodeEntry { let .publicForwardsTitle(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .overview(_, stats, storyViews, publicShares): - return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: false, stats: stats as? Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks) + return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: false, stats: stats as? Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks) case let .interactionsGraph(_, _, _, graph, type, noInitialZoom), let .reactionsGraph(_, _, _, graph, type, noInitialZoom): - return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in + return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in if let graph = graph, case let .Loaded(_, data) = graph { completion(data) @@ -197,7 +197,7 @@ private enum StatsEntry: ItemListNodeEntry { reactions = Int32(story.views?.reactedCount ?? 0) isStory = true } - return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: item, views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: { + return StatsMessageItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, peer: peer, item: item, views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: { switch item { case let .message(message): arguments.openMessage(message.id) diff --git a/submodules/StatisticsUI/Sources/StatsGraphItem.swift b/submodules/StatisticsUI/Sources/StatsGraphItem.swift index 0ab64e4546..8fb221d919 100644 --- a/submodules/StatisticsUI/Sources/StatsGraphItem.swift +++ b/submodules/StatisticsUI/Sources/StatsGraphItem.swift @@ -14,6 +14,7 @@ import ListItemComponentAdaptor public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { let presentationData: ItemListPresentationData + let systemStyle: ItemListSystemStyle let graph: StatsGraph let type: ChartType let noInitialZoom: Bool @@ -22,8 +23,9 @@ public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponent public let sectionId: ItemListSectionId let style: ItemListStyle - public init(presentationData: ItemListPresentationData, graph: StatsGraph, type: ChartType, noInitialZoom: Bool = false, conversionRate: Double = 1.0, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { + public init(presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .legacy, graph: StatsGraph, type: ChartType, noInitialZoom: Bool = false, conversionRate: Double = 1.0, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { self.presentationData = presentationData + self.systemStyle = systemStyle self.graph = graph self.type = type self.noInitialZoom = noInitialZoom @@ -215,6 +217,7 @@ public final class StatsGraphItemNode: ListViewItemNode { contentSize.height += visibilityHeight } contentSize.height += 7.0 + contentSize.height += 8.0 let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in @@ -280,9 +283,9 @@ public final class StatsGraphItemNode: ListViewItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.chartContainerNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: contentSize.height)) + strongSelf.chartContainerNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: contentSize.height - 8.0)) strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: 0.0, y: item.type == .hourlyStep ? -40.0 : 0.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 750.0)) - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 641da989c6..b7aa9dc8fb 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -18,6 +18,7 @@ import AvatarNode public class StatsMessageItem: ListViewItem, ItemListItem { let context: AccountContext let presentationData: ItemListPresentationData + let systemStyle: ItemListSystemStyle let peer: Peer let item: StatsPostItem let views: Int32 @@ -30,9 +31,10 @@ public class StatsMessageItem: ListViewItem, ItemListItem { let openStory: (UIView) -> Void let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { + init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.presentationData = presentationData + self.systemStyle = systemStyle self.peer = peer self.item = item self.views = views @@ -576,7 +578,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift index 789526910c..40a5a34050 100644 --- a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift +++ b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift @@ -40,9 +40,10 @@ extension StarsRevenueStats: Stats { } -class StatsOverviewItem: ListViewItem, ItemListItem { +final class StatsOverviewItem: ListViewItem, ItemListItem { let context: AccountContext let presentationData: ItemListPresentationData + let systemStyle: ItemListSystemStyle let isGroup: Bool let stats: Stats? let additionalStats: Stats? @@ -51,9 +52,10 @@ class StatsOverviewItem: ListViewItem, ItemListItem { let sectionId: ItemListSectionId let style: ItemListStyle - init(context: AccountContext, presentationData: ItemListPresentationData, isGroup: Bool, stats: Stats?, additionalStats: Stats? = nil, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { + init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, isGroup: Bool, stats: Stats?, additionalStats: Stats? = nil, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { self.context = context self.presentationData = presentationData + self.systemStyle = systemStyle self.isGroup = isGroup self.stats = stats self.additionalStats = additionalStats @@ -968,7 +970,7 @@ class StatsOverviewItemNode: ListViewItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil strongSelf.backgroundNode.frame = 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))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 9772f2bc4c..e3b99332ab 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1559,7 +1559,7 @@ public final class AccountViewTracker { let peerId = slice[i].0 let value = result[i] transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in - var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, chatTheme: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil, greetingMessage: nil, awayMessage: nil, connectedBot: nil, businessIntro: .unknown, birthday: nil, personalChannel: .unknown, botPreview: nil, starGiftsCount: nil, starRefProgram: nil, verification: nil, sendPaidMessageStars: nil, disallowedGifts: [], botGroupAdminRights: nil, botChannelAdminRights: nil, starRating: nil, pendingStarRating: nil, mainProfileTab: nil, savedMusic: nil, note: nil) + var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, chatTheme: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil, greetingMessage: nil, awayMessage: nil, connectedBot: nil, businessIntro: .unknown, birthday: nil, personalChannel: .unknown, botPreview: nil, starGiftsCount: nil, starRefProgram: nil, verification: nil, sendPaidMessageStars: nil, disallowedGifts: [], botGroupAdminRights: nil, botChannelAdminRights: nil, starRating: nil, pendingStarRating: nil, mainProfileTab: nil, savedMusic: nil, note: nil, myCopyProtectionEnableDate: nil) var flags = cachedData.flags var sendPaidMessageStars = cachedData.sendPaidMessageStars switch value { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index eba6983325..fcecbd8d40 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -1299,6 +1299,7 @@ public final class CachedUserData: CachedPeerData { public let mainProfileTab: TelegramProfileTab? public let savedMusic: TelegramMediaFile? public let note: Note? + public let myCopyProtectionEnableDate: Int32? public let peerIds: Set public let messageIds: Set @@ -1348,9 +1349,10 @@ public final class CachedUserData: CachedPeerData { self.mainProfileTab = nil self.savedMusic = nil self.note = nil + self.myCopyProtectionEnableDate = nil } - public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, chatTheme: ChatTheme?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts?, botGroupAdminRights: TelegramChatAdminRights?, botChannelAdminRights: TelegramChatAdminRights?, starRating: TelegramStarRating?, pendingStarRating: TelegramStarPendingRating?, mainProfileTab: TelegramProfileTab?, savedMusic: TelegramMediaFile?, note: Note?) { + public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, chatTheme: ChatTheme?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts?, botGroupAdminRights: TelegramChatAdminRights?, botChannelAdminRights: TelegramChatAdminRights?, starRating: TelegramStarRating?, pendingStarRating: TelegramStarPendingRating?, mainProfileTab: TelegramProfileTab?, savedMusic: TelegramMediaFile?, note: Note?, myCopyProtectionEnableDate: Int32?) { self.about = about self.botInfo = botInfo self.editableBotInfo = editableBotInfo @@ -1392,6 +1394,7 @@ public final class CachedUserData: CachedPeerData { self.mainProfileTab = mainProfileTab self.savedMusic = savedMusic self.note = note + self.myCopyProtectionEnableDate = myCopyProtectionEnableDate self.peerIds = Set() @@ -1489,6 +1492,8 @@ public final class CachedUserData: CachedPeerData { } self.note = decoder.decodeCodable(Note.self, forKey: "note") + + self.myCopyProtectionEnableDate = decoder.decodeOptionalInt32ForKey("myCopyProtectionEnableDate") } public func encode(_ encoder: PostboxEncoder) { @@ -1666,6 +1671,12 @@ public final class CachedUserData: CachedPeerData { } else { encoder.encodeNil(forKey: "note") } + + if let myCopyProtectionEnableDate = self.myCopyProtectionEnableDate { + encoder.encodeInt32(myCopyProtectionEnableDate, forKey: "myCopyProtectionEnableDate") + } else { + encoder.encodeNil(forKey: "myCopyProtectionEnableDate") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -1742,172 +1753,179 @@ public final class CachedUserData: CachedPeerData { if other.note != self.note { return false } + if other.myCopyProtectionEnableDate != self.myCopyProtectionEnableDate { + return false + } return other.about == self.about && other.botInfo == self.botInfo && other.editableBotInfo == self.editableBotInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.chatTheme == other.chatTheme && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.fallbackPhoto == other.fallbackPhoto && self.voiceMessagesAvailable == other.voiceMessagesAvailable && self.flags == other.flags && self.wallpaper == other.wallpaper } public func withUpdatedAbout(_ about: String?) -> CachedUserData { - return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedEditableBotInfo(_ editableBotInfo: EditableBotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedChatTheme(_ chatTheme: ChatTheme?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPhoto(_ photo: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPersonalPhoto(_ personalPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedFallbackPhoto(_ fallbackPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedWallpaper(_ wallpaper: TelegramWallpaper?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedFlags(_ flags: CachedUserFlags) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBusinessHours(_ businessHours: TelegramBusinessHours?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBusinessLocation(_ businessLocation: TelegramBusinessLocation?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedGreetingMessage(_ greetingMessage: TelegramBusinessGreetingMessage?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedAwayMessage(_ awayMessage: TelegramBusinessAwayMessage?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedConnectedBot(_ connectedBot: TelegramAccountConnectedBot?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBusinessIntro(_ businessIntro: TelegramBusinessIntro?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: .known(businessIntro), birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: .known(businessIntro), birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBirthday(_ birthday: TelegramBirthday?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPersonalChannel(_ personalChannel: TelegramPersonalChannel?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: .known(personalChannel), botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: .known(personalChannel), botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBotPreview(_ botPreview: BotPreview?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedStarGiftsCount(_ starGiftsCount: Int32?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedStarRefProgram(_ starRefProgram: TelegramStarRefProgram?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedVerification(_ verification: PeerVerification?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedSendPaidMessageStars(_ sendPaidMessageStars: StarsAmount?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedDisallowedGifts(_ disallowedGifts: TelegramDisallowedGifts) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBotGroupAdminRights(_ botGroupAdminRights: TelegramChatAdminRights?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedBotChannelAdminRights(_ botChannelAdminRights: TelegramChatAdminRights?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedStarRating(_ starRating: TelegramStarRating?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedPendingStarRating(_ pendingStarRating: TelegramStarPendingRating?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedMainProfileTab(_ mainProfileTab: TelegramProfileTab?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: mainProfileTab, savedMusic: self.savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedSavedMusic(_ savedMusic: TelegramMediaFile?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: savedMusic, note: self.note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: savedMusic, note: self.note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) } public func withUpdatedNote(_ note: Note?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: note) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: note, myCopyProtectionEnableDate: self.myCopyProtectionEnableDate) + } + + public func withUpdatedMyCopyProtectionEnableDate(_ myCopyProtectionEnableDate: Int32?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic, note: self.note, myCopyProtectionEnableDate: myCopyProtectionEnableDate) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index 943fa5b84c..a793b7d779 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -2311,6 +2311,34 @@ public extension TelegramEngine.EngineData.Item { } } + public struct MyCopyProtectionEnabled: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Bool + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.flags.contains(.myCopyProtectionEnabled) + } else { + return false + } + } + } + public struct BotPreview: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = CachedUserData.BotPreview? diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift index 0f88652490..e9304ce58f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift @@ -36,12 +36,14 @@ func _internal_toggleMessageCopyProtection(account: Account, peerId: PeerId, ena transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in if let previous = current as? CachedUserData { var updatedFlags = previous.flags + var updatedMyCopyProtectionEnableDate: Int32? if enabled { - updatedFlags.insert(.copyProtectionEnabled) + updatedFlags.insert(.myCopyProtectionEnabled) + updatedMyCopyProtectionEnableDate = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) } else { - updatedFlags.remove(.copyProtectionEnabled) + updatedFlags.remove(.myCopyProtectionEnabled) } - return previous.withUpdatedFlags(updatedFlags) + return previous.withUpdatedFlags(updatedFlags).withUpdatedMyCopyProtectionEnableDate(updatedMyCopyProtectionEnableDate) } return current }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift index 4373226157..37bb72ce2b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift @@ -12,7 +12,7 @@ public enum UpdateChatRankError { case notParticipant } -func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { +func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, messageId: MessageId?, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { return account.postbox.transaction { transaction -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> in if let user = transaction.getPeer(userId), let inputUser = apiInputPeer(user), let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { let currentParticipant: Signal @@ -57,7 +57,6 @@ func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, return nil } else { let updatedParticipant = currentParticipant?.withUpdated(rank: rank) ?? .member(id: userId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: rank, subscriptionUntilDate: nil) - var peers: [PeerId: Peer] = [:] var presences: [PeerId: PeerPresence] = [:] peers[user.id] = user @@ -69,6 +68,30 @@ func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, peers[peer.id] = peer } } + let historyView = transaction.getMessagesHistoryViewState(input: .single(peerId: peerId, threadId: nil), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 50, clipHoles: true, anchor: .upperBound, namespaces: .just(Set([Namespaces.Message.Cloud]))) + var messageIds: [MessageId] = [] + if let messageId { + messageIds.append(messageId) + } + for entry in historyView.entries { + if entry.message.id != messageId, let author = entry.message.author, author.id == userId { + messageIds.append(entry.message.id) + } + } + for messageId in messageIds { + transaction.updateMessage(messageId, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } + var attributes = currentMessage.attributes + attributes.removeAll(where: { $0 is ParticipantRankMessageAttribute }) + if let rank, !rank.isEmpty { + attributes.append(ParticipantRankMessageAttribute(rank: rank)) + } + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + } return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: user, peers: peers, presences: presences)) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 4b3dfda9b2..1370ffcfc5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -617,8 +617,8 @@ public extension TelegramEngine { return _internal_getFutureCreatorAfterLeave(account: self.account, peerId: peerId) } - public func updateChatRank(peerId: PeerId, userId: PeerId, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { - return _internal_updateChatRank(account: account, peerId: peerId, userId: userId, rank: rank) + public func updateChatRank(peerId: PeerId, userId: PeerId, messageId: MessageId? = nil, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { + return _internal_updateChatRank(account: account, peerId: peerId, userId: userId, messageId: messageId, rank: rank) } public func terminateSecretChat(peerId: PeerId, requestRemoteHistoryRemoval: Bool) -> Signal { diff --git a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift index 1254868edb..2fd373977b 100644 --- a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift @@ -47,6 +47,37 @@ public func shortStringForDayOfWeek(strings: PresentationStrings, day: Int32) -> } } +public func stringForNominativeMonth(strings: PresentationStrings, month: Int32) -> String { + switch month { + case 0: + return strings.Month_NomJanuary + case 1: + return strings.Month_NomFebruary + case 2: + return strings.Month_NomMarch + case 3: + return strings.Month_NomApril + case 4: + return strings.Month_NomMay + case 5: + return strings.Month_NomJune + case 6: + return strings.Month_NomJuly + case 7: + return strings.Month_NomAugust + case 8: + return strings.Month_NomSeptember + case 9: + return strings.Month_NomOctober + case 10: + return strings.Month_NomNovember + case 11: + return strings.Month_NomDecember + default: + return "" + } +} + public func stringForMonth(strings: PresentationStrings, month: Int32) -> String { switch month { case 0: diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift index d781f5bf4c..a294b89d53 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift @@ -216,7 +216,7 @@ private func attachmentFileControllerEntries(presentationData: PresentationData, } entries.append(.selectFromFiles(presentationData.theme, presentationData.strings.Attachment_SelectFromFiles)) if hasScan { - entries.append(.scanDocument(presentationData.theme, "Scan Document")) + entries.append(.scanDocument(presentationData.theme, presentationData.strings.Attachment_ScanDocument)) } let listTitle: String @@ -691,6 +691,7 @@ public func storyAudioPickerController( dismissImpl?() }) as! AttachmentFileControllerImpl present(filePickerController, filePickerController.mediaPickerContext) + return true } controller.navigationPresentation = .flatModal controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileEmptyItem.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileEmptyItem.swift index d8f20e23bd..5065c353a9 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileEmptyItem.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileEmptyItem.swift @@ -122,7 +122,7 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo insets.top += -60.0 imageSize = CGSize(width: 112.0, height: 112.0) } else { - insets.top += 30.0 //-160.0 + insets.top += 120.0 } let imageSpacing: CGFloat = 12.0 diff --git a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AccountSwitchComponent.swift b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AccountSwitchComponent.swift index ab6dbf3193..9437dbfc42 100644 --- a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AccountSwitchComponent.swift +++ b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AccountSwitchComponent.swift @@ -82,6 +82,8 @@ final class AccountSwitchComponent: Component { func update(component: AccountSwitchComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.component = component + self.button.isUserInteractionEnabled = component.canSwitch + let size = CGSize(width: component.canSwitch ? 76.0 : 44.0, height: 44.0) let avatarSize = self.avatar.update( diff --git a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift index 4efb10536e..82e35f591c 100644 --- a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift +++ b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift @@ -34,14 +34,14 @@ private final class AuthConfirmationSheetContent: CombinedComponent { let context: AccountContext let requestSubject: MessageActionUrlSubject let subject: MessageActionUrlAuthResult - let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void let cancel: (Bool) -> Void init( context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, - completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void, + completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void, cancel: @escaping (Bool) -> Void ) { self.context = context @@ -61,13 +61,15 @@ private final class AuthConfirmationSheetContent: CombinedComponent { final class State: ComponentState { private let context: AccountContext private let requestSubject: MessageActionUrlSubject - private let subject: MessageActionUrlAuthResult - private let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + fileprivate var subject: MessageActionUrlAuthResult + private let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void private let disposables = DisposableSet() + private let accountInUseDisposable = MetaDisposable() - var peer: EnginePeer? + var canSwitchAccount = false var forcedAccount: (AccountContext, EnginePeer)? + var peer: EnginePeer? fileprivate var inProgress = false var allowWrite = true @@ -77,7 +79,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { var matchCodes: [String]? var selectedMatchCode: String? - init(context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void) { + init(context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void) { self.context = context self.requestSubject = requestSubject self.subject = subject @@ -113,23 +115,43 @@ private final class AuthConfirmationSheetContent: CombinedComponent { } } - if case let .request(_, _, _, _, _, userIdHint) = self.subject, let userIdHint, userIdHint != context.account.peerId { + if case let .request(_, _, _, _, _, userIdHint) = self.subject { + let isTestEnvironment = context.account.testingEnvironment let _ = (activeAccountsAndPeers(context: self.context, includePrimary: true) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] primary, other in guard let self else { return } + + var accountCount = 0 for (accountContext, peer, _) in other { - if peer.id == userIdHint { - self.forcedAccount = (accountContext, peer) - self.updated() - - accountContext.account.shouldBeServiceTaskMaster.set(.single(.now)) - let _ = accountContext.engine.messages.requestMessageActionUrlAuth(subject: requestSubject).start() - break + if accountContext.account.testingEnvironment == isTestEnvironment { + accountCount += 1 + } + if let userIdHint, userIdHint != context.account.peerId { + if peer.id == userIdHint { + self.forcedAccount = (accountContext, peer) + + self.accountInUseDisposable.set(self.context.sharedContext.setAccountUserInterfaceInUse(accountContext.account.id)) + + let _ = (accountContext.engine.messages.requestMessageActionUrlAuth(subject: requestSubject) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let self, case .request = result else { + return + } + self.subject = result + if case let .request(_, _, _, flags, _, _) = result, !flags.contains(.showMatchCodesFirst) { + self.displayEmoji = false + self.matchCodes = nil + } + self.updated() + }) + } } } + self.canSwitchAccount = accountCount > 0 + self.updated() }) } } @@ -138,9 +160,37 @@ private final class AuthConfirmationSheetContent: CombinedComponent { self.disposables.dispose() if !self.inProgress { - if let (context, _) = self.forcedAccount { - context.account.shouldBeServiceTaskMaster.set(.single(.never)) - } + self.accountInUseDisposable.dispose() + } + } + + func complete(matchCode: String?) { + guard case let .request(_, _, _, flags, _, _) = self.subject else { + return + } + + var allowWrite = false + if flags.contains(.requestWriteAccess) && self.allowWrite { + allowWrite = true + } + + let accountContext = self.forcedAccount?.0 ?? self.context + guard let accountPeer = self.forcedAccount?.1 ?? self.peer else { + return + } + + if flags.contains(.requestPhoneNumber) { + self.displayPhoneNumberConfirmation(commit: { sharePhoneNumber in + self.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode), self.accountInUseDisposable) + self.inProgress = true + self.selectedMatchCode = matchCode + self.updated() + }) + } else { + self.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: false, matchCode: matchCode), self.accountInUseDisposable) + self.inProgress = true + self.selectedMatchCode = matchCode + self.updated() } } @@ -165,7 +215,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { return } - self.completion(accountContext, accountPeer, .failed) + self.completion(accountContext, accountPeer, .failed, self.accountInUseDisposable) } }) } @@ -206,6 +256,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { } let context = self.context + let isTestEnvironment = context.account.testingEnvironment let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let items: Signal<[ContextMenuItem], NoError> = activeAccountsAndPeers(context: self.context, includePrimary: true) |> take(1) @@ -218,9 +269,8 @@ private final class AuthConfirmationSheetContent: CombinedComponent { guard let self else { return } - if let (context, _) = self.forcedAccount { - context.account.shouldBeServiceTaskMaster.set(.single(.never)) - } + + self.accountInUseDisposable.set(nil) self.forcedAccount = nil self.updated() @@ -229,7 +279,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { } for (accountContext, peer, _) in other { - guard !existingIds.contains(peer.id) else { + guard !existingIds.contains(peer.id), accountContext.account.testingEnvironment == isTestEnvironment else { continue } items.append(.custom(AccountPeerContextItem(context: accountContext, account: accountContext.account, peer: peer, action: { [weak self] _, f in @@ -237,14 +287,19 @@ private final class AuthConfirmationSheetContent: CombinedComponent { guard let self else { return } - if let (context, _) = self.forcedAccount { - context.account.shouldBeServiceTaskMaster.set(.single(.never)) - } - - accountContext.account.shouldBeServiceTaskMaster.set(.single(.now)) + self.accountInUseDisposable.set(self.context.sharedContext.setAccountUserInterfaceInUse(accountContext.account.id)) self.forcedAccount = (accountContext, peer) self.updated() + + let _ = (accountContext.engine.messages.requestMessageActionUrlAuth(subject: self.requestSubject) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let self, case .request = result else { + return + } + self.subject = result + self.updated() + }) }), true)) } @@ -279,7 +334,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value let component = context.component - let theme = environment.theme + let theme = environment.theme.withModalBlocksBackground() let strings = environment.strings let state = context.state if state.controller == nil { @@ -288,7 +343,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { let presentationData = context.component.context.sharedContext.currentPresentationData.with { $0 } - guard case let .request(domain, bot, clientData, flags, matchCodes, _) = component.subject else { + guard case let .request(domain, bot, clientData, flags, matchCodes, _) = state.subject else { fatalError() } @@ -323,7 +378,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { context: state.forcedAccount?.0 ?? component.context, theme: environment.theme, peer: state.forcedAccount?.1 ?? peer, - canSwitch: true, + canSwitch: state.canSwitchAccount, isVisible: true, action: { [weak state] sourceView in state?.presentAccountSwitchMenu(sourceView: sourceView) @@ -340,36 +395,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { let titleFont = Font.bold(24.0) let textFont = Font.regular(15.0) let boldTextFont = Font.semibold(15.0) - - let complete: (String?) -> Void = { [weak state] matchCode in - guard let state else { - return - } - var allowWrite = false - if flags.contains(.requestWriteAccess) && state.allowWrite { - allowWrite = true - } - - let accountContext = state.forcedAccount?.0 ?? component.context - guard let accountPeer = state.forcedAccount?.1 ?? state.peer else { - return - } - - if flags.contains(.requestPhoneNumber) { - state.displayPhoneNumberConfirmation(commit: { sharePhoneNumber in - component.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode)) - state.inProgress = true - state.selectedMatchCode = matchCode - state.updated() - }) - } else { - component.completion(accountContext, accountPeer, .accept(allowWriteAccess: allowWrite, sharePhoneNumber: false, matchCode: matchCode)) - state.inProgress = true - state.selectedMatchCode = matchCode - state.updated() - } - } - + var contentHeight: CGFloat = 32.0 if state.displayEmoji, let matchCodes = state.matchCodes { @@ -379,7 +405,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { component: MultilineTextComponent( text: .markdown(text: strings.AuthConfirmation_Emoji_Title, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.controlAccentColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })), horizontalAlignment: .center, - maximumNumberOfLines: 2, + maximumNumberOfLines: 3, lineSpacing: 0.2 ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), @@ -393,9 +419,16 @@ private final class AuthConfirmationSheetContent: CombinedComponent { contentHeight += emojiTitle.size.height contentHeight += 16.0 + let emojiDescriptionString: String + if flags.contains(.showMatchCodesFirst) { + emojiDescriptionString = strings.AuthConfirmation_Emoji_DescriptionFirst(domain).string + } else { + emojiDescriptionString = strings.AuthConfirmation_Emoji_Description + } + let emojiDescription = emojiDescription.update( component: MultilineTextComponent( - text: .markdown(text: strings.AuthConfirmation_Emoji_Description, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })), + text: .markdown(text: emojiDescriptionString, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })), horizontalAlignment: .center, maximumNumberOfLines: 3, lineSpacing: 0.2 @@ -451,7 +484,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { ) } - let subject = component.subject + let subject = state.subject let emoji = emojis[code].update( component: AnyComponent( PlainButtonComponent( @@ -466,7 +499,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { if case let .request(_, _, _, flags, _, _) = subject, flags.contains(.showMatchCodesFirst) { state.checkMatchCode(code) } else { - complete(code) + state.complete(matchCode: code) } }, isEnabled: state.selectedMatchCode == nil, @@ -752,7 +785,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { guard let accountPeer = state.forcedAccount?.1 ?? state.peer else { return } - component.completion(accountContext, accountPeer, .decline) + component.completion(accountContext, accountPeer, .decline, EmptyDisposable) component.cancel(true) } ), @@ -786,7 +819,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent { state.matchCodes = matchCodes.shuffled() state.updated(transition: .spring(duration: 0.4)) } else { - complete(state.selectedMatchCode) + state.complete(matchCode: state.selectedMatchCode) } } ), @@ -813,13 +846,13 @@ private final class AuthConfirmationSheetComponent: CombinedComponent { let context: AccountContext let requestSubject: MessageActionUrlSubject let subject: MessageActionUrlAuthResult - let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void init( context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, - completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void ) { self.context = context self.requestSubject = requestSubject @@ -914,13 +947,13 @@ public class AuthConfirmationScreen: ViewControllerComponentContainer { private let context: AccountContext private let requestSubject: MessageActionUrlSubject private let subject: MessageActionUrlAuthResult - fileprivate let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + fileprivate let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void public init( context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, - completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result, Disposable) -> Void ) { self.context = context self.requestSubject = requestSubject diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index 5ce697f49f..6c92a201d3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -258,7 +258,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { Queue.mainQueue().after(0.5) { if let presentationInterfaceState = self.presentationInterfaceState, let peer = presentationInterfaceState.renderedPeer?.peer { var canEditRank = false - if let channel = peer as? TelegramChannel, channel.hasPermission(.editRank) { + if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.editRank) { canEditRank = true } else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { canEditRank = true diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 0fc88aaf4c..e5de189c31 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -722,6 +722,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var didChangeFromPendingToSent: Bool = false private var authorNameColor: UIColor? + private var authorRank: CachedChannelAdminRank? private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer? @@ -3619,7 +3620,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI disablesComments: disablesComments, suggestedPostInfoNodeLayout: suggestedPostInfoNodeLayout, alignment: alignment, - isSidePanelOpen: isSidePanelOpen + isSidePanelOpen: isSidePanelOpen, + authorRank: authorRank ) }) } @@ -3685,7 +3687,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI disablesComments: Bool, suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)?, alignment: ChatMessageBubbleContentAlignment, - isSidePanelOpen: Bool + isSidePanelOpen: Bool, + authorRank: CachedChannelAdminRank? ) -> Void { guard let strongSelf = selfReference.value else { return @@ -3721,6 +3724,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.disablesComments = disablesComments strongSelf.authorNameColor = authorNameColor + strongSelf.authorRank = authorRank strongSelf.replyRecognizer?.allowBothDirections = false//!item.context.sharedContext.immediateExperimentalUISettings.unidirectionalSwipeToReply strongSelf.view.disablesInteractiveTransitionGestureRecognizer = false//!item.context.sharedContext.immediateExperimentalUISettings.unidirectionalSwipeToReply @@ -6651,30 +6655,21 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI item.controllerInteraction.openGroupBoostInfo(peer.id, boostCount) } @objc private func rankButtonPressed() { - guard let item = self.item, let peer = item.message.author else { + guard let item = self.item, let peer = item.message.author, let authorRank = self.authorRank else { return } - - var role: ChatRankInfoScreenRole = .member var rank: String = "" - if case let .message(message, _, _, attributes, _) = item.content { - switch attributes.rank { - case let .creator(rankValue): - role = .creator - rank = rankValue ?? "" - case let .admin(rankValue): - role = .admin - rank = rankValue ?? "" - case let .member(rankValue): - role = .member - rank = rankValue ?? "" - default: - break - } - - if rank.isEmpty, let attribute = message.attributes.first(where: { $0 is ParticipantRankMessageAttribute }) as? ParticipantRankMessageAttribute, !attribute.rank.isEmpty { - rank = attribute.rank - } + var role: ChatRankInfoScreenRole = .member + switch authorRank { + case let .creator(rankValue): + role = .creator + rank = rankValue ?? "" + case let .admin(rankValue): + role = .admin + rank = rankValue ?? "" + case let .member(rankValue): + role = .member + rank = rankValue ?? "" } item.controllerInteraction.openRankInfo(EnginePeer(peer), role, rank) } diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index ad3ab67515..124269c80a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -4837,12 +4837,18 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if let strongSelf = self { strongSelf.formatAttributesLink(strongSelf) } - }, - UIAction(title: self.strings?.TextFormat_Date ?? "Date", image: nil) { [weak self] (action) in + } + ]) + + if hasSpoilers { + children.append(UIAction(title: self.strings?.TextFormat_Date ?? "Date", image: nil) { [weak self] (action) in if let strongSelf = self { strongSelf.formatAttributesDate(strongSelf) } - }, + }) + } + + children.append(contentsOf: [ UIAction(title: self.strings?.TextFormat_Strikethrough ?? "Strikethrough", image: nil) { [weak self] (action) in if let strongSelf = self { strongSelf.formatAttributesStrikethrough(strongSelf) diff --git a/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift b/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift index 7acc7ab7ff..6bbc4ad256 100644 --- a/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift +++ b/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift @@ -97,6 +97,13 @@ private final class ChatParticipantRightsContent: CombinedComponent { self.disposable?.dispose() } + func animateError() { + guard let controller = self.controller as? ChatParticipantRightsScreen else { + return + } + controller.animateError() + } + func complete() { guard let controller = self.controller as? ChatParticipantRightsScreen else { return @@ -118,7 +125,7 @@ private final class ChatParticipantRightsContent: CombinedComponent { return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value let component = context.component - let theme = environment.theme + let theme = environment.theme.withModalBlocksBackground() let strings = environment.strings let state = context.state if state.controller == nil { @@ -252,6 +259,13 @@ private final class ChatParticipantRightsContent: CombinedComponent { state.rank = value state.updated(transition: .easeInOut(duration: 0.2)) }, + shouldUpdateText: { [weak state] text in + if text.containsEmoji { + state?.animateError() + return false + } + return true + }, onReturn: { [weak state] in guard let state else { return @@ -472,6 +486,7 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer { ) self.navigationPresentation = .flatModal + self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) } required public init(coder aDecoder: NSCoder) { @@ -503,22 +518,35 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } if let navigationController = self.navigationController as? NavigationController { Queue.mainQueue().after(0.5) { + var hadRank = false + if case let .rank(_, _, rank, _) = self.subject, !(rank ?? "").isEmpty { + hadRank = true + } + var title: String? - var text: String + var text: String? if let rank { - title = presentationData.strings.Chat_TagUpdated_Added + title = hadRank ? presentationData.strings.Chat_TagUpdated_Edited : presentationData.strings.Chat_TagUpdated_Added text = rank - } else { + } else if hadRank { text = presentationData.strings.Chat_TagUpdated_Removed } - let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true}) - (navigationController.topViewController as? ViewController)?.present(toastController, in: .current) + if let text { + let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true}) + (navigationController.topViewController as? ViewController)?.present(toastController, in: .current) + } } } self.dismissAnimated() } + fileprivate func animateError() { + if let view = self.node.hostView.findTaggedView(tag: rankFieldTag) as? ListTextFieldItemComponent.View { + view.animateError() + } + } + public override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift index 739395939d..0433ca3db5 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift @@ -243,6 +243,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component { } contentHeight += 62.0 + var added = false let datePicker: DatePickerNode if let current = self.datePicker { datePicker = current @@ -251,13 +252,13 @@ private final class ChatScheduleTimeSheetContentComponent: Component { datePicker.updateTheme(DatePickerTheme(theme: environment.theme)) } } else { + added = true datePicker = DatePickerNode( theme: DatePickerTheme(theme: environment.theme), strings: strings, dateTimeFormat: environment.dateTimeFormat, hasValueRow: false ) - datePicker.date = self.date datePicker.valueUpdated = { [weak self] date in if let self { self.date = date @@ -293,6 +294,10 @@ private final class ChatScheduleTimeSheetContentComponent: Component { datePicker.maximumDate = maxDate } + if added { + datePicker.date = self.date + } + let constrainedWidth = min(390.0, availableSize.width) let cellSize = floor((constrainedWidth - 12.0 * 2.0) / 7.0) let pickerHeight = 59.0 + cellSize * 6.0 @@ -378,7 +383,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component { var repeatValueFrame = CGRect() if case .format = component.mode { - + contentHeight += 8.0 } else { transition.setFrame(layer: self.bottomSeparator, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: UIScreenPixel))) self.bottomSeparator.backgroundColor = environment.theme.list.itemBlocksSeparatorColor.cgColor @@ -683,18 +688,29 @@ private final class ChatScheduleTimeSheetContentComponent: Component { if component.context.isPremium { self.repeatPeriod = value } else { + var text = strings.ScheduleMessage_PremiumRequired_Text + let pattern = #"\*\*(.*?)\*\*"# + if let regex = try? NSRegularExpression(pattern: pattern, options: []) { + let range = NSRange(text.startIndex.., transition: ComponentTransition) -> CGSize { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } - -// static var body: Body { -// let background = Child(RoundedRectangle.self) -// let title = Child(MultilineTextComponent.self) -// let icon = Child(BundleIconComponent.self) -// let lockIcon = Child(BundleIconComponent.self) -// -// return { context in -// let component = context.component -// -// let background = background.update( -// component: RoundedRectangle( -// color: UIColor.white.withAlphaComponent(0.16), -// cornerRadius: 16.0 -// ), -// availableSize: context.availableSize, -// transition: .immediate -// ) -// context.add(background -// .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) -// ) -// -// let icon = icon.update( -// component: BundleIconComponent( -// name: component.iconName, -// tintColor: UIColor.white -// ), -// availableSize: context.availableSize, -// transition: .immediate -// ) -// context.add(icon -// .position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0)) -// ) -// -// let title = title.update( -// component: MultilineTextComponent( -// text: .plain(NSAttributedString( -// string: component.title, -// font: Font.regular(11.0), -// textColor: UIColor.white, -// paragraphAlignment: .natural -// )), -// horizontalAlignment: .center, -// maximumNumberOfLines: 1 -// ), -// availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height), -// transition: .immediate -// ) -// var totalTitleWidth = title.size.width -// var titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 -// if component.isLocked { -// let titleSpacing: CGFloat = 3.0 -// let lockIcon = lockIcon.update( -// component: BundleIconComponent( -// name: "Chat List/StatusLockIcon", -// tintColor: UIColor.white -// ), -// availableSize: context.availableSize, -// transition: .immediate -// ) -// totalTitleWidth += lockIcon.size.width + titleSpacing -// titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 -// context.add(lockIcon -// .position(CGPoint(x: titleOriginX + lockIcon.size.width / 2.0, y: 42.0)) -// ) -// titleOriginX += lockIcon.size.width + titleSpacing -// } -// context.add(title -// .position(CGPoint(x: titleOriginX + title.size.width / 2.0, y: 42.0)) -// ) -// -// return context.availableSize -// } -// } } private struct GiftViewConfiguration { diff --git a/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift b/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift index db1bb62322..b4f89aa2ee 100644 --- a/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift +++ b/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift @@ -9,6 +9,7 @@ import DatePickerNode public class ItemListDatePickerItem: ListViewItem, ItemListItem { let presentationData: ItemListPresentationData + let systemStyle: ItemListSystemStyle let dateTimeFormat: PresentationDateTimeFormat let date: Int32? let minDate: Int32? @@ -28,6 +29,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem { public init( presentationData: ItemListPresentationData, + systemStyle: ItemListSystemStyle = .legacy, dateTimeFormat: PresentationDateTimeFormat, date: Int32?, minDate: Int32? = nil, @@ -43,6 +45,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem { tag: ItemListItemTag? = nil ) { self.presentationData = presentationData + self.systemStyle = systemStyle self.dateTimeFormat = dateTimeFormat self.date = date self.minDate = minDate @@ -158,14 +161,14 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode { let width = params.width - params.leftInset - params.rightInset let constrainedWidth = min(390.0, width) let cellSize = floor((constrainedWidth - 12.0 * 2.0) / 7.0) - let pickerHeight = 122.0 + cellSize * 6.0 + let pickerHeight = 132.0 + cellSize * 6.0 let height: CGFloat if item.displayingDateSelection { height = pickerHeight } else if item.displayingTimeSelection { height = 260.0 } else { - height = 44.0 + height = 56.0 } switch item.style { case .plain: @@ -244,7 +247,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode { strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil transition.updateFrame(node: strongSelf.backgroundNode, frame: 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)))) transition.updateFrame(node: strongSelf.maskNode, frame: strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)) @@ -291,7 +294,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode { datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) } let datePickerSize = CGSize(width: width, height: pickerHeight) - datePickerNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - datePickerSize.width) / 2.0), y: 0.0), size: datePickerSize) + datePickerNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - datePickerSize.width) / 2.0), y: 6.0), size: datePickerSize) datePickerNode.updateLayout(size: datePickerSize, transition: .immediate) transition.updateFrame(node: strongSelf.containerNode, frame: CGRect(origin: .zero, size: CGSize(width: params.width, height: contentSize.height))) diff --git a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift index 3866013073..62e55dcc78 100644 --- a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift @@ -37,6 +37,7 @@ public final class ListTextFieldItemComponent: Component { public let returnKeyType: UIReturnKeyType public let contentInsets: UIEdgeInsets public let updated: ((String) -> Void)? + public let shouldUpdateText: (String) -> Bool public let onReturn: (() -> Void)? public let tag: AnyObject? @@ -52,6 +53,7 @@ public final class ListTextFieldItemComponent: Component { returnKeyType: UIReturnKeyType = .default, contentInsets: UIEdgeInsets = .zero, updated: ((String) -> Void)?, + shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, onReturn: (() -> Void)? = nil, tag: AnyObject? = nil ) { @@ -66,6 +68,7 @@ public final class ListTextFieldItemComponent: Component { self.returnKeyType = returnKeyType self.contentInsets = contentInsets self.updated = updated + self.shouldUpdateText = shouldUpdateText self.onReturn = onReturn self.tag = tag } @@ -147,6 +150,14 @@ public final class ListTextFieldItemComponent: Component { preconditionFailure() } + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + guard let component = self.component else { + return true + } + let newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) + return component.shouldUpdateText(newText) + } + public func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.component?.onReturn?() return true diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 3c8c774478..190e1983e7 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -975,8 +975,6 @@ public final class MediaEditor { if let _ = textureSourceResult.player { self.updateRenderChain() -// let _ = image -// self.maybeGeneratePersonSegmentation(image) } if let _ = self.values.audioTrack { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index eebd5bd372..23635bb062 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -423,11 +423,7 @@ public final class MediaEditorVideoExport { duration = CMTime(seconds: 5.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) } } - if self.configuration.values.videoBounce, case .video = self.subject { - self.durationValue = CMTimeAdd(duration, duration) - } else { - self.durationValue = duration - } + self.durationValue = duration let _ = (combineLatest(signals) |> deliverOn(self.queue)).start(next: { [weak self] additionalInputs in @@ -516,49 +512,17 @@ public final class MediaEditorVideoExport { self.mainVideoScale = scale self.mainVideoOffset = offset self.textureRotation = rotation - var mainTimeRange = CMTimeRange(start: .zero, duration: asset.duration) - if let timeRange = self.configuration.timeRange { - mainTimeRange = timeRange - } else if isStory && asset.duration.seconds > 60.0 { - mainTimeRange = CMTimeRange(start: .zero, duration: CMTime(seconds: 60.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) - } if let videoAssetTrack = asset.tracks(withMediaType: .video).first { if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { mainVideoTrack = compositionTrack compositionTrack.preferredTransform = videoAssetTrack.preferredTransform - - try? compositionTrack.insertTimeRange(mainTimeRange, of: videoAssetTrack, at: .zero) - - if self.configuration.values.videoBounce { - let sourceFrameRate: Float - if videoAssetTrack.nominalFrameRate > 0.0 { - sourceFrameRate = videoAssetTrack.nominalFrameRate - } else if self.configuration.frameRate > 0.0 { - sourceFrameRate = self.configuration.frameRate - } else { - sourceFrameRate = 30.0 - } - let roundedFrameRate = max(1, Int(round(sourceFrameRate))) - let frameDuration = CMTime(value: 1, timescale: Int32(roundedFrameRate)) - - var insertionTime = mainTimeRange.duration - var cursor = CMTimeSubtract(CMTimeRangeGetEnd(mainTimeRange), frameDuration) - while CMTimeCompare(cursor, mainTimeRange.start) >= 0 { - let remaining = CMTimeSubtract(CMTimeRangeGetEnd(mainTimeRange), cursor) - let segmentDuration = CMTimeCompare(frameDuration, remaining) <= 0 ? frameDuration : remaining - if segmentDuration.seconds > 0.0 { - let segmentRange = CMTimeRange(start: cursor, duration: segmentDuration) - try? compositionTrack.insertTimeRange(segmentRange, of: videoAssetTrack, at: insertionTime) - insertionTime = CMTimeAdd(insertionTime, segmentDuration) - } - cursor = CMTimeSubtract(cursor, frameDuration) - } - } + + try? compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: videoAssetTrack, at: .zero) } } if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, !self.configuration.values.videoIsMuted { if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { - try? compositionTrack.insertTimeRange(mainTimeRange, of: audioAssetTrack, at: .zero) + try? compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioAssetTrack, at: .zero) if let volume = self.configuration.values.videoVolume, volume != 1.0 { let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack) @@ -568,7 +532,7 @@ public final class MediaEditorVideoExport { } } } - if !self.configuration.values.videoBounce, let timeRange = self.configuration.timeRange { + if let timeRange = self.configuration.timeRange { readerRange = timeRange } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 4aba7aafd0..47bb5140f4 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3958,16 +3958,18 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self.stickerMaskWrapperView.addSubview(self.stickerMaskPreviewView) self.stickerMaskDrawingView = stickerMaskDrawingView - let previewSize = self.previewView.bounds.size - self.stickerMaskWrapperView.frame = CGRect(origin: .zero, size: previewSize) - self.stickerMaskPreviewView.frame = CGRect(origin: .zero, size: previewSize) - - let maskScale = previewSize.width / min(maskDrawingSize.width, maskDrawingSize.height) - self.initialMaskScale = maskScale - self.initialMaskPosition = CGPoint(x: previewSize.width / 2.0, y: previewSize.height / 2.0) - stickerMaskDrawingView.bounds = CGRect(origin: .zero, size: maskDrawingSize) - - self.updateMaskDrawingView(position: .zero, scale: 1.0, rotation: 0.0) + Queue.mainQueue().justDispatch { + let previewSize = self.previewView.bounds.size + self.stickerMaskWrapperView.frame = CGRect(origin: .zero, size: previewSize) + self.stickerMaskPreviewView.frame = CGRect(origin: .zero, size: previewSize) + + let maskScale = previewSize.width / min(maskDrawingSize.width, maskDrawingSize.height) + self.initialMaskScale = maskScale + self.initialMaskPosition = CGPoint(x: previewSize.width / 2.0, y: previewSize.height / 2.0) + stickerMaskDrawingView.bounds = CGRect(origin: .zero, size: maskDrawingSize) + + self.updateMaskDrawingView(position: .zero, scale: 1.0, rotation: 0.0) + } } private func updateMaskDrawingView(position: CGPoint, scale: CGFloat, rotation: CGFloat) { @@ -5384,6 +5386,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID ) if trackId != 0 && !isCollage { + items.append(.separator) items.append( .action( ContextMenuActionItem( @@ -6742,6 +6745,16 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID fileprivate let customTarget: EnginePeer.Id? let forwardSource: (EnginePeer, EngineStoryItem)? + public weak var customNavigationController: UINavigationController? + + var effectiveNavigationController: UINavigationController? { + if let navigationController = self.navigationController { + return navigationController + } else { + return self.customNavigationController + } + } + let initialCaption: NSAttributedString? let initialPrivacy: EngineStoryPrivacy? let initialMediaAreas: [MediaArea]? @@ -7806,13 +7819,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID return true } if pack.count >= 120 { - let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.MediaEditor_StickersTooMuch, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in - if case .info = action, let self { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: { - - }) - self.push(controller) - } + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.MediaEditor_StickersTooMuch, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { _ in return false }) self.hapticFeedback.error() @@ -8151,7 +8158,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self?.stickerUploadDisposable.set(nil) }) case let .complete(resource, _): - let navigationController = self.navigationController as? NavigationController + let navigationController = self.effectiveNavigationController as? NavigationController let result: MediaEditorScreenImpl.Result switch action { @@ -8180,7 +8187,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID parentController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: self.context, file: file, loop: true, title: nil, text: presentationData.strings.Conversation_StickerAddedToFavorites, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } case .addToStickerPack, .createStickerPack: - if let (packReference, packTitle) = packReferenceAndTitle, let navigationController = self.navigationController as? NavigationController { + if let (packReference, packTitle) = packReferenceAndTitle, let navigationController { Queue.mainQueue().after(0.2) { let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], actionTitle: nil, isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: self.sendSticker, actionPerformed: nil) (navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift index a5642fb04e..a9e3991b4c 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift @@ -96,9 +96,9 @@ private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCu super.init() self.addSubnode(self.scrollNode) - for separatorNode in self.separatorNodes { - self.scrollNode.addSubnode(separatorNode) - } +// for separatorNode in self.separatorNodes { +// self.scrollNode.addSubnode(separatorNode) +// } for actionNode in self.actionNodes { self.scrollNode.addSubnode(actionNode) } @@ -171,7 +171,7 @@ private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCu } func canBeHighlighted() -> Bool { - return self.isActionEnabled + return false } func updateIsHighlighted(isHighlighted: Bool) { diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift index 166f22d825..71f461ab79 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift @@ -239,7 +239,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll var navigationHeight: CGFloat = minimizedNavigationHeight if !isExpanded { - navigationHeight += insets.bottom + navigationHeight += insets.bottom + 3.0 } let headerFrame = CGRect(origin: .zero, size: CGSize(width: size.width, height: navigationHeight)) diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift index 65e25bff1b..b8fde11302 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift @@ -311,13 +311,13 @@ final class MinimizedHeaderNode: ASDisplayNode { transition.updateFrame(view: view, frame: closeButtonFrame) } - transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: size.width, height: 243.0))) + transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: size.width, height: 246.0))) //self.backgroundView.update(size: CGSize(width: size.width, height: 243.0), color: self.theme.navigationBar.opaqueBackgroundColor, topCornerRadius: 25.0, bottomCornerRadius: 62.0, transition: .immediate) transition.updateAlpha(layer: self.progressView.layer, alpha: isExpanded && self.progress != nil ? 1.0 : 0.0) if let progress = self.progress { - self.progressView.frame = CGRect(origin: .zero, size: CGSize(width: size.width * CGFloat(progress), height: 243.0)) + self.progressView.frame = CGRect(origin: .zero, size: CGSize(width: size.width * CGFloat(progress), height: 246.0)) } } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift index 5667c8cf25..bfbc701147 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift @@ -147,7 +147,20 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { case .admin: label = presentationData.strings.GroupInfo_LabelAdmin case .member: - label = nil + var canEditRank = false + if item.member.id == item.context.accountPeerId { + if let channel = item.enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { + canEditRank = true + } else if let group = item.enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { + canEditRank = true + } + } + if canEditRank { + label = presentationData.strings.GroupInfo_AddRank + labelColor = presentationData.theme.list.itemAccentColor + } else { + label = nil + } } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index 8cccbdae48..10e3ff045f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -27,6 +27,7 @@ private struct PeerMembersListTransaction { enum PeerMembersListAction { case open + case editRank case promote case restrict case remove @@ -107,7 +108,20 @@ private enum PeerMembersListEntry: Comparable, Identifiable { case .admin: label = presentationData.strings.GroupInfo_LabelAdmin case .member: - label = nil + var canEditRank = false + if member.id == context.account.peerId { + if let channel = enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { + canEditRank = true + } else if let group = enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { + canEditRank = true + } + } + if canEditRank { + label = presentationData.strings.GroupInfo_AddRank + labelColor = presentationData.theme.list.itemAccentColor + } else { + label = nil + } } } @@ -125,7 +139,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: enclosingPeer, member: member) var options: [ItemListPeerItemRevealOption] = [] - if actions.contains(.promote) && enclosingPeer is TelegramChannel{ + if actions.contains(.promote) && enclosingPeer is TelegramChannel { options.append(ItemListPeerItemRevealOption(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: { action(member, .promote) })) @@ -161,6 +175,10 @@ private enum PeerMembersListEntry: Comparable, Identifiable { status = .custom(string: NSAttributedString(string: botStatus, font: Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize * 14.0 / 17.0)), textColor: presentationData.theme.list.itemSecondaryTextColor), multiline: false, isActive: false, icon: nil) } + var canEditRank = false + if actions.contains(.editRank) { + canEditRank = true + } return ContactsPeerItem( presentationData: ItemListPresentationData(presentationData), style: .plain, @@ -182,8 +200,12 @@ private enum PeerMembersListEntry: Comparable, Identifiable { index: nil, header: nil, hideBackground: true, - action: member.peer.id == context.account.peerId ? nil : { _ in - action(member, .open) + action: member.peer.id == context.account.peerId && !canEditRank ? nil : { _ in + if member.peer.id == context.account.peerId && canEditRank { + action(member, .editRank) + } else { + action(member, .open) + } }, disabledAction: nil, setPeerIdWithRevealedOptions: { _, _ in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift index 890c446de7..00af60cf77 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift @@ -71,7 +71,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode { |> take(1) let combinedSignal: Signal - if readyWhenGalleryLoads { + if !"".isEmpty, readyWhenGalleryLoads { combinedSignal = combineLatest(queue: .mainQueue(), avatarReady, galleryReady diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 0b321e2d99..ac1a186146 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -2191,6 +2191,16 @@ func peerInfoScreenData( } } +func peerInfoIsCopyProtected(data: PeerInfoScreenData) -> Bool { + var isCopyProtected = false + if let cachedUserData = data.cachedData as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) { + isCopyProtected = true + } else if let peer = data.peer, peer.isCopyProtectionEnabled { + isCopyProtected = true + } + return isCopyProtected +} + func canEditPeerInfo(context: AccountContext, peer: Peer?, chatLocation: ChatLocation, threadData: MessageHistoryThreadData?) -> Bool { if context.account.peerId == peer?.id { return true @@ -2278,7 +2288,7 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member: if channel.hasPermission(.addAdmins) { result.insert(.promote) } - if channel.hasPermission(.editRank) { + if channel.hasPermission(.manageRanks) { result.insert(.editRank) } } @@ -2289,7 +2299,7 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member: if channel.hasPermission(.addAdmins) { result.insert(.promote) } - if channel.hasPermission(.editRank) { + if channel.hasPermission(.manageRanks) { result.insert(.editRank) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index db476e4706..fbfeabc8f3 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -452,7 +452,7 @@ private final class PeerInfoPendingPane { chatLocationContextHolder = Atomic(value: nil) } - var captureProtected = data.peer?.isCopyProtectionEnabled ?? false + var captureProtected = peerInfoIsCopyProtected(data: data) let paneNode: PeerInfoPaneNode switch key { case .gifts: diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index d0436d81b6..89cea5a76d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -724,8 +724,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } - var items: [ContextMenuItem] = [] + var isCopyProtected = false + if let cachedUserData = strongSelf.data?.cachedData as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) { + isCopyProtected = true + } + var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, _ in c?.dismiss(completion: { if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController { @@ -777,7 +781,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }))) } - if message.isCopyProtected() { + if message.isCopyProtected() || isCopyProtected { } else if message.id.peerId.namespace != Namespaces.Peer.SecretChat && message.minAutoremoveOrClearTimeout == nil { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, _ in @@ -882,6 +886,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } if let previewData = previewData { + var isCopyProtected = false + if let cachedUserData = strongSelf.data?.cachedData as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) { + isCopyProtected = true + } + let context = strongSelf.context let strings = strongSelf.presentationData.strings let items = strongSelf.context.sharedContext.chatAvailableMessageActions(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id], keepUpdated: false) @@ -929,7 +938,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) }))) - if message.isCopyProtected() { + if message.isCopyProtected() || isCopyProtected { } else if message.id.peerId.namespace != Namespaces.Peer.SecretChat { items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, f in @@ -1444,6 +1453,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.performMemberAction(member: member, action: .promote) case .restrict: strongSelf.performMemberAction(member: member, action: .restrict) + case .editRank: + strongSelf.performMemberAction(member: member, action: .editRank) case .remove: strongSelf.performMemberAction(member: member, action: .remove) case let .openStories(sourceView): @@ -2805,11 +2816,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - if let peer = data.peer, peer.isCopyProtectionEnabled { - setLayerDisableScreenshots(self.layer, true) - } else { - setLayerDisableScreenshots(self.layer, false) - } + setLayerDisableScreenshots(self.layer, peerInfoIsCopyProtected(data: data)) } func scrollToTop() { @@ -6334,6 +6341,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc return self.controllerNode.twoStepAuthData } + public var notificationExceptions: Promise { + return self.controllerNode.notificationExceptions + } + override public var customNavigationData: CustomViewControllerNavigationData? { get { if !self.isSettings { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift index 8046ae477c..ef1ebc78ce 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift @@ -31,7 +31,17 @@ extension PeerInfoScreenNode { } if actions.contains(.editRank) { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionEditRank, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + let actionTitle: String + if case .admin = member.role { + actionTitle = self.presentationData.strings.GroupInfo_ActionEditAdminRank + } else { + if let rank = member.rank, !rank.isEmpty { + actionTitle = self.presentationData.strings.GroupInfo_ActionEditRank + } else { + actionTitle = self.presentationData.strings.GroupInfo_ActionAddRank + } + } + items.append(.action(ContextMenuActionItem(text: actionTitle, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in c?.dismiss { guard let self else { return @@ -42,7 +52,11 @@ extension PeerInfoScreenNode { } if actions.contains(.promote) && enclosingPeer is TelegramChannel { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.GroupInfo_ActionPromote, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + var actionTitle: String = self.presentationData.strings.GroupInfo_ActionPromote + if case .admin = member.role { + actionTitle = self.presentationData.strings.GroupInfo_ActionEditAdmin + } + items.append(.action(ContextMenuActionItem(text: actionTitle, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in c?.dismiss { guard let self else { return diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift index ce7209b833..2ea49ad1ed 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift @@ -13,6 +13,7 @@ import NotificationExceptionsScreen import ShareController import TranslateUI import TelegramNotices +import AlertComponent extension PeerInfoScreenNode { func performButtonAction(key: PeerInfoHeaderButtonKey, buttonNode: PeerInfoHeaderButtonNode?, gesture: ContextGesture?) { @@ -816,9 +817,26 @@ extension PeerInfoScreenNode { }) } } else { - let _ = self.context.engine.peers.toggleMessageCopyProtection(peerId: user.id, enabled: false).start() + let action = { + let _ = self.context.engine.peers.toggleMessageCopyProtection(peerId: user.id, enabled: false).start() + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, scrollToEndIfExists: true, completion: { _ in })) + } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, peerNearbyData: nil, completion: { _ in })) + let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + let timeout: Int32 = self.context.account.testingEnvironment ? 300 : 86400 + if let cachedUserData = self.data?.cachedData as? CachedUserData, !cachedUserData.flags.contains(.copyProtectionEnabled), let date = cachedUserData.myCopyProtectionEnableDate, currentTime < date + timeout { + action() + } else { + let peerName = self.data?.peer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "" + let alertController = AlertScreen(context: self.context, configuration: .init(actionAlignment: .vertical), title: self.presentationData.strings.EnableSharing_Title, text: self.presentationData.strings.EnableSharing_Text(peerName).string, actions: [ + .init(title: self.presentationData.strings.EnableSharing_SendRequest, type: .default, action: { + action() + }), + .init(title: self.presentationData.strings.Common_Cancel) + ]) + self.controller?.present(alertController, in: .window(.root)) + } } }))) } diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift index a5ab5c2553..c81e76a89c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift @@ -479,10 +479,8 @@ private final class SheetContent: Component { contentHeight += itemSize.height } - contentHeight += 31.0 - - contentHeight += 82.0 - + contentHeight += 113.0 + return CGSize(width: availableSize.width, height: contentHeight) } } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 8c370154d6..111bf64dec 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -68,6 +68,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private let requestPeerType: [ReplyMarkupButtonRequestPeerType]? let multipleSelectionLimit: Int32? private let hasCreation: Bool + let immediatelySwitchToContacts: Bool let immediatelyActivateMultipleSelection: Bool override public var _presentedInModal: Bool { @@ -110,6 +111,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.selectForumThreads = params.selectForumThreads self.requestPeerType = params.requestPeerType self.hasCreation = params.hasCreation + self.immediatelySwitchToContacts = params.immediatelySwitchToContacts self.immediatelyActivateMultipleSelection = params.immediatelyActivateMultipleSelection self.multipleSelectionLimit = params.multipleSelectionLimit diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 076445ecca..874fe745d8 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -848,6 +848,18 @@ final class PeerSelectionControllerNode: ASDisplayNode { } } + override func didLoad() { + super.didLoad() + + if let controller = self.controller, controller.immediatelySwitchToContacts { + self.segmentedControlSelectedIndex = 1 + if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.containerLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) + } + self.indexChanged(1) + } + } + func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData self.updateThemeAndStrings() diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift index c59e3649ea..0871ba3e2e 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift @@ -389,6 +389,7 @@ public func standaloneColorPickerController( } colorPickerController.openGallery = openGallery present(colorPickerController, colorPickerController.mediaPickerContext) + return true } controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) return controller diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index eca8edd4dd..cdf8d57c88 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1888,7 +1888,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { } attachmentController.requestController = { [weak self, weak view, weak attachmentController] type, completion in guard let self, let view, let component = view.component else { - return + return true } switch type { case .gallery: @@ -1897,7 +1897,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { if let controller = existingController { completion(controller, controller.mediaPickerContext) controller.prepareForReuse() - return + return true } self.presentMediaPicker( view: view, @@ -1925,13 +1925,14 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) } ) + return true case .file: self.controllerNavigationDisposable.set(nil) let existingController = currentFilesController.with { $0 } if let controller = existingController as? AttachmentContainable, let mediaPickerContext = controller.mediaPickerContext { completion(controller, mediaPickerContext) controller.prepareForReuse() - return + return true } let theme = component.theme let updatedPresentationData: (initial: PresentationData, signal: Signal) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }) @@ -1970,13 +1971,14 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { if let controller = controller as? AttachmentContainable, let mediaPickerContext = controller.mediaPickerContext { completion(controller, mediaPickerContext) } + return true case .location: self.controllerNavigationDisposable.set(nil) let existingController = currentLocationController.with { $0 } if let controller = existingController { completion(controller, controller.mediaPickerContext) controller.prepareForReuse() - return + return true } let selfPeerId: EnginePeer.Id if case let .channel(peer) = peer, case .broadcast = peer.info { @@ -2010,6 +2012,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { let _ = currentLocationController.swap(controller) }) + return true case .contact: let theme = component.theme let updatedPresentationData: (initial: PresentationData, signal: Signal) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }) @@ -2170,6 +2173,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { })) } })) + return true case .gift: /*let premiumGiftOptions = strongSelf.presentationInterfaceState.premiumGiftOptions if !premiumGiftOptions.isEmpty { @@ -2226,6 +2230,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { default: break } + return true } let present = { [weak self, weak view] in guard let self, let view, let controller = view.component?.controller() else { diff --git a/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift b/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift index a1c9465026..3571e1927e 100644 --- a/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift +++ b/submodules/TelegramUI/Components/VolumeSliderContextItem/Sources/VolumeSliderContextItem.swift @@ -155,11 +155,21 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto } private func updateValue(transition: ContainedViewLayoutTransition = .immediate) { - let width = self.frame.width + let sideInset: CGFloat = 10.0 + let width = self.frame.width - sideInset * 2.0 let range = self.maxValue - self.minValue - let value = self.value - transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value / range * width, height: self.frame.height))) + let value = (self.value - self.minValue) / range + var foregroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: value * width, height: self.frame.height)) + var foregroundOffset: CGFloat = 0.0 + if foregroundFrame.width < 40.0 { + foregroundOffset = (40.0 - foregroundFrame.width) * 0.5 + foregroundFrame = foregroundFrame.insetBy(dx: 0.0, dy: foregroundOffset) + } + transition.updateFrameAdditive(node: self.foregroundNode, frame: foregroundFrame) + transition.updateSublayerTransformOffset(layer: self.foregroundNode.layer, offset: CGPoint(x: 0.0, y: -foregroundOffset)) + + transition.updateCornerRadius(node: self.foregroundNode, cornerRadius: min(20.0, foregroundFrame.width * 0.5)) let stringValue = "\(Int(self.value * 100.0))%" diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json index c646c9b3ef..409b8e192b 100644 --- a/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "disablesharing.pdf", + "filename" : "spy.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/disablesharing.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/spy.pdf similarity index 69% rename from submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/disablesharing.pdf rename to submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/spy.pdf index 321e09ee83..35358aa347 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/disablesharing.pdf and b/submodules/TelegramUI/Images.xcassets/Premium/Perk/NoForward.imageset/spy.pdf differ diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift index dba3277cd6..0a9d87e4b2 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift @@ -27,10 +27,14 @@ extension ChatControllerImpl { getCaptionPanelView: { [weak self] in return self?.getCaptionPanelView(isFile: false, hasTimer: false) }, - sendMessagesWithSignals: { [weak self] signals, _, _, _ in + sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in guard let self else { return } + let parameters = ChatSendMessageActionSheetController.SendParameters( + effect: nil, + textIsAboveMedia: isCaptionAbove + ) self.enqueueMediaMessages( fromGallery: false, signals: signals, @@ -38,7 +42,7 @@ extension ChatControllerImpl { silentPosting: false, scheduleTime: nil, replyToSubject: nil, - parameters: nil, + parameters: parameters, getAnimatedTransitionSource: nil, completion: {} ) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenDateContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenDateContextMenu.swift index 20fdf9a6bc..6eb88e48b4 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenDateContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenDateContextMenu.swift @@ -106,9 +106,24 @@ extension ChatControllerImpl: EKEventEditViewDelegate { OutgoingScheduleInfoMessageAttribute(scheduleTime: result.time, repeatPeriod: result.repeatPeriod) ] let forwardMessage: EnqueueMessage = .forward(source: message.id, threadId: nil, grouping: .auto, attributes: attributes, correlationId: nil) - let _ = forwardMessage let _ = enqueueMessages(account: self.context.account, peerId: self.context.account.peerId, messages: [forwardMessage]).start() - self.present(UndoOverlayController(presentationData: self.presentationData, content: .forward(savedMessages: true, text: self.presentationData.strings.Conversation_DateReminderSet), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + + let text = self.presentationData.strings.Conversation_DateReminderSet.replacingOccurrences(of: "[", with: "**").replacingOccurrences(of: "]()", with: "**") + self.present(UndoOverlayController(presentationData: self.presentationData, content: .forward(savedMessages: true, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { [weak self] action in + if let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .current) } ) self.push(controller) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1b4f6d80d1..329fe46866 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1393,6 +1393,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G message: message, mediaIndex: params.mediaIndex, standalone: standalone, + copyProtected: self.presentationInterfaceState.copyProtectionEnabled || self.presentationInterfaceState.myCopyProtectionEnabled, reverseMessageGalleryOrder: false, mode: mode, navigationController: self.effectiveNavigationController, dismissInput: { [weak self] in @@ -3066,8 +3067,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var skipConcealedAlert = false if let author = message?.author, author.isVerified { skipConcealedAlert = true + } else if let forwardInfo = message?.forwardInfo, let author = forwardInfo.author, author.isVerified { + skipConcealedAlert = true } - + if let message, let adAttribute = message.attributes.first(where: { $0 is AdMessageAttribute }) as? AdMessageAttribute { strongSelf.chatDisplayNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId, media: false, fullscreen: false) } @@ -5271,32 +5274,50 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openStarsPurchase: { [weak self] amount in self?.interfaceInteraction?.openStarsPurchase(amount) }, openRankInfo: { [weak self] peer, role, rank in - guard let self, let chatPeer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel else { + guard let self, let chatPeer = self.presentationInterfaceState.renderedPeer?.peer else { return } var canChange = false + var canEdit = false let chatParticipant = Promise() - if let defaultBannedRights = chatPeer.defaultBannedRights { - canChange = !defaultBannedRights.flags.contains(.banEditRank) - - if canChange { - chatParticipant.set(self.context.engine.peers.fetchChannelParticipant(peerId: chatPeer.id, participantId: self.context.account.peerId)) + + if let channel = chatPeer as? TelegramChannel { + canEdit = channel.hasPermission(.manageRanks) && role == .member + + if let defaultBannedRights = channel.defaultBannedRights { + canChange = !defaultBannedRights.flags.contains(.banEditRank) + + if canChange { + chatParticipant.set(self.context.engine.peers.fetchChannelParticipant(peerId: chatPeer.id, participantId: self.context.account.peerId)) + } + } + } else if let group = chatPeer as? TelegramGroup { + switch group.role { + case .creator: + canEdit = true + case .admin: + canEdit = false + default: + break + } + if let defaultBannedRights = group.defaultBannedRights { + canChange = !defaultBannedRights.flags.contains(.banEditRank) } } - let controller = self.context.sharedContext.makeChatRankInfoScreen( - context: self.context, - chatPeer: EnginePeer(chatPeer), - userPeer: peer, - role: role, - rank: rank, - canChange: canChange, - completion: { [weak self] in - guard let self else { - return - } - let _ = (chatParticipant.get() - |> deliverOnMainQueue).start(next: { [weak self] participant in - guard let self else { + + let openEdit: (EnginePeer.Id, String?, ChatRankInfoScreenRole) -> Void = { [weak self] peerId, rank, role in + guard let self else { + return + } + let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: peerId, rank: rank, role: role) + self.push(controller) + } + + if canEdit { + if chatPeer is TelegramChannel { + let _ = (self.context.engine.peers.fetchChannelParticipant(peerId: chatPeer.id, participantId: peer.id) + |> deliverOnMainQueue).start(next: { participant in + guard let participant else { return } var rank: String? = nil @@ -5308,15 +5329,45 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .member(_, _, adminInfo, _, rankValue, _): rank = rankValue role = adminInfo != nil ? .admin : .member - default: - break } - let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: self.context.account.peerId, rank: rank, role: role) - self.push(controller) + openEdit(peer.id, rank, role) }) + } else { + openEdit(peer.id, rank, role) } - ) - self.push(controller) + } else { + let controller = self.context.sharedContext.makeChatRankInfoScreen( + context: self.context, + chatPeer: EnginePeer(chatPeer), + userPeer: peer, + role: role, + rank: rank, + canChange: canChange, + completion: { [weak self] in + guard let self else { + return + } + let _ = (chatParticipant.get() + |> deliverOnMainQueue).start(next: { [weak self] participant in + guard let self, let participant else { + return + } + var rank: String? = nil + var role: ChatRankInfoScreenRole = .member + switch participant { + case let .creator(_, _, rankValue): + rank = rankValue + role = .creator + case let .member(_, _, adminInfo, _, rankValue, _): + rank = rankValue + role = adminInfo != nil ? .admin : .member + } + openEdit(self.context.account.peerId, rank, role) + }) + } + ) + self.push(controller) + } }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode)) controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 8127513a4b..23151e97f8 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -204,7 +204,7 @@ extension ChatControllerImpl { allButtons.insert(button, at: 1) } - if let user = peer as? TelegramUser, user.botInfo == nil { + if context.isPremium, shortcutMessageList.items.count > 0, let user = peer as? TelegramUser, user.botInfo == nil { if let index = buttons.firstIndex(where: { $0 == .location }) { buttons.insert(.quickReply, at: index + 1) } else { @@ -334,7 +334,7 @@ extension ChatControllerImpl { } attachmentController.requestController = { [weak self, weak attachmentController] type, completion in guard let strongSelf = self else { - return + return true } switch type { case .gallery: @@ -343,7 +343,7 @@ extension ChatControllerImpl { if let controller = existingController { completion(controller, controller.mediaPickerContext) controller.prepareForReuse() - return + return true } strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, enableMultiselection: enableMultiselection, present: { controller, mediaPickerContext in let _ = currentMediaController.swap(controller) @@ -359,13 +359,14 @@ extension ChatControllerImpl { } self?.enqueueMediaMessages(fromGallery: fromGallery, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) }) + return true case .file: strongSelf.controllerNavigationDisposable.set(nil) let existingController = currentFilesController.with { $0 } if let controller = existingController { completion(controller, controller.mediaPickerContext) controller.prepareForReuse() - return + return true } let controller = strongSelf.context.sharedContext.makeAttachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendFiles, presentGallery: { [weak self, weak attachmentController] in attachmentController?.dismiss(animated: true) @@ -388,13 +389,14 @@ extension ChatControllerImpl { let _ = currentFilesController.swap(controller) completion(controller, controller.mediaPickerContext) } + return true case .location: strongSelf.controllerNavigationDisposable.set(nil) let existingController = currentLocationController.with { $0 } if let controller = existingController { completion(controller, controller.mediaPickerContext) controller.prepareForReuse() - return + return true } let selfPeerId: PeerId if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { @@ -408,7 +410,7 @@ extension ChatControllerImpl { } else { selfPeerId = strongSelf.context.account.peerId } - ;let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: selfPeerId)) + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: selfPeerId)) |> deliverOnMainQueue).startStandalone(next: { selfPeer in guard let strongSelf = self, let selfPeer = selfPeer else { return @@ -447,6 +449,7 @@ extension ChatControllerImpl { let _ = currentLocationController.swap(controller) }) + return true case .contact: let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, style: .glass, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: .always, requirePhoneNumbers: true)) contactsController.presentScheduleTimePicker = { [weak self] completion in @@ -639,30 +642,34 @@ extension ChatControllerImpl { } } })) + return true case .poll: if let controller = strongSelf.configurePollCreation() as? AttachmentContainable { completion(controller, controller.mediaPickerContext) strongSelf.controllerNavigationDisposable.set(nil) } + return true case .todo: if strongSelf.context.isPremium { if let controller = strongSelf.configureTodoCreation() as? AttachmentContainable { completion(controller, controller.mediaPickerContext) strongSelf.controllerNavigationDisposable.set(nil) } + return true } else { var replaceImpl: ((ViewController) -> Void)? let demoController = strongSelf.context.sharedContext.makePremiumDemoController(context: strongSelf.context, subject: .todo, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .todo, forceDark: false, dismissed: nil) replaceImpl?(controller) }, dismissed: nil) - replaceImpl = { [weak demoController] c in + replaceImpl = { [weak self, weak demoController] c in + Queue.mainQueue().after(0.4) { + self?.attachmentController?.dismiss(animated: false) + } demoController?.replace(with: c) } strongSelf.push(demoController) - Queue.mainQueue().after(0.4) { - strongSelf.attachmentController?.dismiss(animated: false) - } + return false } case .gift: if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer, let starsContext = context.starsContext { @@ -681,6 +688,7 @@ extension ChatControllerImpl { let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: context.sharedContext.accountManager, peerId: peer.id, timestamp: Int32(Date().timeIntervalSince1970)).startStandalone() } } + return true case let .app(bot): if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { var payload: String? @@ -729,6 +737,7 @@ extension ChatControllerImpl { strongSelf.present(alertController, in: .window(.root)) } } + return true case .quickReply: let _ = (strongSelf.context.sharedContext.makeQuickReplySetupScreenInitialData(context: strongSelf.context) |> take(1) @@ -747,9 +756,11 @@ extension ChatControllerImpl { completion(controller, controller.mediaPickerContext) strongSelf.controllerNavigationDisposable.set(nil) }) + return true default: break } + return true } let present = { attachmentController.navigationPresentation = .flatModal diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 38d8072019..abeec27d4f 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -2047,13 +2047,19 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false for entry in view.additionalData { if case let .peer(_, maybePeer) = entry, let peer = maybePeer { - isCopyProtectionEnabled = peer.isCopyProtectionEnabled + if !isCopyProtectionEnabled { + isCopyProtectionEnabled = peer.isCopyProtectionEnabled + } if let channel = peer as? TelegramChannel { autoTranslate = channel.flags.contains(.autoTranslateEnabled) if let boostLevel = channel.approximateBoostLevel, boostLevel >= premiumConfiguration.minGroupAudioTranscriptionLevel { audioTranscriptionProvidedByBoost = true } } + } else if case let .cachedPeerData(_, cachedData) = entry, let cachedUserData = cachedData as? CachedUserData { + if !isCopyProtectionEnabled { + isCopyProtectionEnabled = cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) + } } } let alwaysDisplayTranscribeButton = ChatMessageItemAssociatedData.DisplayTranscribeButton( diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index bf858b81ae..51902108d4 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -2305,10 +2305,11 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer EngineDataMap(Set(messageIds.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.Peer.init)), EngineDataMap(Set(messageIds).map(TelegramEngine.EngineData.Item.Messages.Message.init)), EngineDataMap(Set(messageIds.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled.init)), + EngineDataMap(Set(messageIds.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.MyCopyProtectionEnabled.init)), TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId) ) |> take(keepUpdated ? Int.max : 1) - |> map { limitsConfiguration, peerMap, messageMap, copyProtectionMap, accountPeer -> ChatAvailableMessageActions in + |> map { limitsConfiguration, peerMap, messageMap, copyProtectionMap, myCopyProtectionMap, accountPeer -> ChatAvailableMessageActions in let isPremium: Bool if let accountPeer { isPremium = accountPeer.isPremium @@ -2350,8 +2351,10 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer } func isPeerCopyProtected(_ peerId: PeerId) -> Bool? { - if let copyProtection = copyProtectionMap[peerId] { - return copyProtection + let copyProtection = copyProtectionMap[peerId] + let myCopyProtection = myCopyProtectionMap[peerId] + if copyProtection == true || myCopyProtection == true { + return true } else { return nil } diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 5f5f9f17ab..87181921ff 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -24,6 +24,7 @@ import MultiAnimationRenderer import TranslateUI import ChatControllerInteraction import LegacyChatHeaderPanelComponent +import UIKitRuntimeUtils private enum PinnedMessageAnimation { case slideToTop @@ -86,6 +87,16 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let queue = Queue() + private var captureProtected: Bool = false { + didSet { + if self.captureProtected != oldValue { + if self.isNodeLoaded { + setLayerDisableScreenshots(self.contentContainer.layer, self.captureProtected) + } + } + } + } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if let buttonResult = self.buttonsContainer.hitTest(point.offsetBy(dx: -self.buttonsContainer.frame.minX, dy: -self.buttonsContainer.frame.minY), with: event) { return buttonResult @@ -281,6 +292,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } self.isReplyThread = isReplyThread + self.captureProtected = interfaceState.copyProtectionEnabled || interfaceState.myCopyProtectionEnabled + self.contextContainer.isGestureEnabled = !isReplyThread var actionTitle: String? diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index ddd2f2a67f..047253a186 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -157,9 +157,10 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController let previousTheme = self.presentationData.theme let previousStrings = self.presentationData.strings - self.presentationData = presentationData if case .glass = params.style { - self.presentationData = self.presentationData.withUpdated(theme: self.presentationData.theme.withModalBlocksBackground()) + self.presentationData = self.presentationData.withUpdated(theme: presentationData.theme.withModalBlocksBackground()) + } else { + self.presentationData = presentationData } if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 232fde6576..520bf4841c 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -237,7 +237,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { params.present(controller, nil, .window(.root)) } else if let rootController = params.navigationController?.view.window?.rootViewController { let proceed = { - let canShare = !params.message.isCopyProtected() + let canShare = !params.message.isCopyProtected() && !params.copyProtected var useBrowserScreen = false if BrowserScreen.supportedDocumentMimeTypes.contains(file.mimeType) { useBrowserScreen = true diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index d6d50bdf75..1ce9cc44e2 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -1822,14 +1822,12 @@ func openResolvedUrlImpl( |> deliverOnMainQueue).start(next: { result in if case .request = result { var dismissImpl: (() -> Void)? - let controller = AuthConfirmationScreen(context: context, requestSubject: subject, subject: result, completion: { accountContext, accountPeer, authResult in + let controller = AuthConfirmationScreen(context: context, requestSubject: subject, subject: result, completion: { accountContext, accountPeer, authResult, disposable in switch authResult { case let .accept(allowWriteAccess, sharePhoneNumber, matchCode): let signal = accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode) |> afterDisposed { - if accountContext !== context { - accountContext.account.shouldBeServiceTaskMaster.set(.single(.never)) - } + disposable.dispose() } let _ = (signal diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 6b42e1a50a..57e4b54d36 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -951,20 +951,24 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur handleResolvedUrl(.postStory(section)) case "settings": if let lastComponent = parsedUrl.pathComponents.last { + let fullPath = parsedUrl.pathComponents.joined(separator: "/").replacingOccurrences(of: "//", with: "") var section: ResolvedUrl.SettingsSection? - switch lastComponent { - case "themes": - section = .legacy(.theme) - case "devices": - section = .legacy(.devices) - case "enable_log": - section = .legacy(.enableLog) - case "phone_privacy": - section = .legacy(.phonePrivacy) - case "login_email": - section = .legacy(.loginEmail) - default: - let fullPath = parsedUrl.pathComponents.joined(separator: "/").replacingOccurrences(of: "//", with: "") + if parsedUrl.pathComponents.count == 2 { + switch lastComponent { + case "themes": + section = .legacy(.theme) + case "devices": + section = .legacy(.devices) + case "enable_log": + section = .legacy(.enableLog) + case "phone_privacy": + section = .legacy(.phonePrivacy) + case "login_email": + section = .legacy(.loginEmail) + default: + section = .path(fullPath) + } + } else { section = .path(fullPath) } if let section { diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 367c883931..af415a12a1 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -397,7 +397,16 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let copyProtectionEnabled: Signal if case let .peer(peerId) = self.chatLocation { - copyProtectionEnabled = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled(id: peerId)) + if peerId.namespace == Namespaces.Peer.CloudUser { + copyProtectionEnabled = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled(id: peerId), + TelegramEngine.EngineData.Item.Peer.MyCopyProtectionEnabled(id: peerId) + ) |> map { copyProtectionEnabled, myCopyProtectionEnabled in + return copyProtectionEnabled || myCopyProtectionEnabled + } + } else { + copyProtectionEnabled = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled(id: peerId)) + } } else { copyProtectionEnabled = .single(false) } @@ -414,6 +423,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.savedIds = savedIds self.savedIdsPromise.set(.single(savedIds)) self.copyProtectionEnabled = copyProtectionEnabled + self.controlsNode.forceCopyProtected.set(copyProtectionEnabled) let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : .animated(duration: 0.5, curve: .spring) self.updateFloatingHeaderOffset(offset: self.floatingHeaderOffset ?? 0.0, transition: transition) diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 86550b5cee..5bba88db46 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -209,6 +209,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode { var getParentController: () -> ViewController? = { return nil } + let forceCopyProtected = ValuePromise(false) + private(set) var currentItemId: SharedMediaPlaylistItemId? private var displayData: SharedMediaPlaybackDisplayData? private var currentAlbumArtInitialized = false @@ -401,8 +403,11 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } }) - self.statusDisposable = (delayedStatus - |> deliverOnMainQueue).startStrict(next: { [weak self] value in + self.statusDisposable = combineLatest( + queue: Queue.mainQueue(), + delayedStatus, + self.forceCopyProtected.get() + ).startStrict(next: { [weak self] value, forceCopyProtected in guard let strongSelf = self else { return } @@ -492,11 +497,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if strongSelf.displayData != displayData { strongSelf.displayData = displayData - var canShare = true if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source { switch source { - case let .telegramFile(fileReference, isCopyProtected, _): - canShare = !isCopyProtected + case let .telegramFile(fileReference, _, _): strongSelf.currentFileReference = fileReference if let size = fileReference.media.size { strongSelf.scrubberNode.bufferingStatus = strongSelf.account.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource) @@ -511,8 +514,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode { strongSelf.scrubberNode.bufferingStatus = nil } strongSelf.updateLabels(transition: .immediate) - - strongSelf.shareNode.isHidden = !canShare + } + + if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source { + switch source { + case let .telegramFile(_, isCopyProtected, _): + strongSelf.shareNode.isHidden = isCopyProtected || forceCopyProtected + } } if itemUpdated { diff --git a/submodules/TelegramUI/Sources/PollResultsController.swift b/submodules/TelegramUI/Sources/PollResultsController.swift index b515269caf..c1b17478d2 100644 --- a/submodules/TelegramUI/Sources/PollResultsController.swift +++ b/submodules/TelegramUI/Sources/PollResultsController.swift @@ -276,7 +276,7 @@ private enum PollResultsEntry: ItemListNodeEntry { if let timestamp { let (timeString, dateString) = formatTimeAndDate(timestamp: timestamp, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) let labelString = NSMutableAttributedString() - if let dateString { + if let dateString, !dateString.isEmpty { labelString.append(NSAttributedString(string: "\(dateString)\n", font: Font.regular(presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0), textColor: presentationData.theme.list.itemSecondaryTextColor)) } labelString.append(NSAttributedString(string: "\(timeString)", font: Font.regular(presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) @@ -534,7 +534,7 @@ private func formatTimeAndDate(timestamp: Int32, strings: PresentationStrings, d let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday if dayDifference == 0 { - dateString = "" + dateString = nil } else if dayDifference == -1 { dateString = strings.Weekday_Yesterday.lowercased() } else { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 37a34fde0a..99a409a84b 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -3803,6 +3803,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { completion(file, emoji, { commit({}) }) + } else { + commit({}) } } as ([MediaEditorScreenImpl.Result], @escaping (@escaping () -> Void) -> Void) -> Void ) diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 2199dbfe8a..6b7e1e20f1 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -169,6 +169,10 @@ public final class TelegramRootController: NavigationController, TelegramRootCon return self.accountSettingsController?.twoStepAuthData } + public func getNotificationExceptions() -> Promise? { + return self.accountSettingsController?.notificationExceptions + } + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { let needsRootWallpaperBackgroundNode: Bool if case .regular = layout.metrics.widthClass { diff --git a/submodules/TranslateUI/Sources/TranslateScreen.swift b/submodules/TranslateUI/Sources/TranslateScreen.swift index 5d42165453..a7d9db798d 100644 --- a/submodules/TranslateUI/Sources/TranslateScreen.swift +++ b/submodules/TranslateUI/Sources/TranslateScreen.swift @@ -767,659 +767,6 @@ public final class TranslateScreen: ViewControllerComponentContainer { } } -//public class TranslateScreen: ViewController { -// final class Node: ViewControllerTracingNode, ASScrollViewDelegate, ASGestureRecognizerDelegate { -// private var presentationData: PresentationData -// private weak var controller: TranslateScreen? -// -// private let component: AnyComponent -// private let theme: PresentationTheme? -// -// let dim: ASDisplayNode -// let wrappingView: UIView -// let containerView: UIView -// let scrollView: UIScrollView -// let hostView: ComponentHostView -// -// private(set) var isExpanded = false -// private var panGestureRecognizer: UIPanGestureRecognizer? -// private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)? -// -// private var currentIsVisible: Bool = false -// private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? -// -// fileprivate var temporaryDismiss = false -// -// init(context: AccountContext, controller: TranslateScreen, component: AnyComponent, theme: PresentationTheme?) { -// self.presentationData = context.sharedContext.currentPresentationData.with { $0 } -// -// self.controller = controller -// -// self.component = component -// self.theme = theme -// -// let effectiveTheme = theme ?? self.presentationData.theme -// -// self.dim = ASDisplayNode() -// self.dim.alpha = 0.0 -// self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25) -// -// self.wrappingView = UIView() -// self.containerView = UIView() -// self.scrollView = UIScrollView() -// self.hostView = ComponentHostView() -// -// super.init() -// -// self.scrollView.delegate = self.wrappedScrollViewDelegate -// self.scrollView.showsVerticalScrollIndicator = false -// -// self.containerView.clipsToBounds = true -// self.containerView.backgroundColor = effectiveTheme.list.blocksBackgroundColor -// -// self.addSubnode(self.dim) -// -// self.view.addSubview(self.wrappingView) -// self.wrappingView.addSubview(self.containerView) -// self.containerView.addSubview(self.scrollView) -// self.scrollView.addSubview(self.hostView) -// } -// -// override func didLoad() { -// super.didLoad() -// -// let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) -// panRecognizer.delegate = self.wrappedGestureRecognizerDelegate -// panRecognizer.delaysTouchesBegan = false -// panRecognizer.cancelsTouchesInView = true -// self.panGestureRecognizer = panRecognizer -// self.wrappingView.addGestureRecognizer(panRecognizer) -// -// self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) -// -// self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) -// } -// -// @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { -// if case .ended = recognizer.state { -// self.controller?.dismiss(animated: true) -// } -// } -// -// override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { -// if let (layout, _) = self.currentLayout { -// if case .regular = layout.metrics.widthClass { -// return false -// } -// } -// return true -// } -// -// func scrollViewDidScroll(_ scrollView: UIScrollView) { -// let contentOffset = self.scrollView.contentOffset.y -// self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) -// } -// -// func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { -// if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer { -// return true -// } -// return false -// } -// -// private var isDismissing = false -// func animateIn() { -// ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear).updateAlpha(node: self.dim, alpha: 1.0) -// -// let targetPosition = self.containerView.center -// let startPosition = targetPosition.offsetBy(dx: 0.0, dy: self.bounds.height) -// -// self.containerView.center = startPosition -// let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) -// transition.animateView(allowUserInteraction: true, { -// self.containerView.center = targetPosition -// }, completion: { _ in -// }) -// } -// -// func animateOut(completion: @escaping () -> Void = {}) { -// self.isDismissing = true -// -// let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) -// positionTransition.updatePosition(layer: self.containerView.layer, position: CGPoint(x: self.containerView.center.x, y: self.bounds.height + self.containerView.bounds.height / 2.0), completion: { [weak self] _ in -// self?.controller?.dismiss(animated: false, completion: completion) -// }) -// let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) -// alphaTransition.updateAlpha(node: self.dim, alpha: 0.0) -// -// if !self.temporaryDismiss { -// self.controller?.updateModalStyleOverlayTransitionFactor(0.0, transition: positionTransition) -// } -// } -// -// func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ComponentTransition) { -// self.currentLayout = (layout, navigationHeight) -// -// if let controller = self.controller, let navigationBar = controller.navigationBar, navigationBar.view.superview !== self.wrappingView { -// self.containerView.addSubview(navigationBar.view) -// } -// -// self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0)) -// -// var effectiveExpanded = self.isExpanded -// if case .regular = layout.metrics.widthClass { -// effectiveExpanded = true -// } -// -// let isLandscape = layout.orientation == .landscape -// let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset -// let topInset: CGFloat -// if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments { -// if effectiveExpanded { -// topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset)) -// } else { -// topInset = max(0.0, panInitialTopInset + min(0.0, panOffset)) -// } -// } else { -// topInset = effectiveExpanded ? 0.0 : edgeTopInset -// } -// transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: nil) -// -// let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset) -// self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition) -// -// let clipFrame: CGRect -// if layout.metrics.widthClass == .compact { -// self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25) -// if isLandscape { -// self.containerView.layer.cornerRadius = 0.0 -// } else { -// self.containerView.layer.cornerRadius = 10.0 -// } -// -// if #available(iOS 11.0, *) { -// if layout.safeInsets.bottom.isZero { -// self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] -// } else { -// self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner] -// } -// } -// -// if isLandscape { -// clipFrame = CGRect(origin: CGPoint(), size: layout.size) -// } else { -// let coveredByModalTransition: CGFloat = 0.0 -// var containerTopInset: CGFloat = 10.0 -// if let statusBarHeight = layout.statusBarHeight { -// containerTopInset += statusBarHeight -// } -// -// let unscaledFrame = CGRect(origin: CGPoint(x: 0.0, y: containerTopInset - coveredByModalTransition * 10.0), size: CGSize(width: layout.size.width, height: layout.size.height - containerTopInset)) -// let maxScale: CGFloat = (layout.size.width - 16.0 * 2.0) / layout.size.width -// let containerScale = 1.0 * (1.0 - coveredByModalTransition) + maxScale * coveredByModalTransition -// let maxScaledTopInset: CGFloat = containerTopInset - 10.0 -// let scaledTopInset: CGFloat = containerTopInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition -// let containerFrame = unscaledFrame.offsetBy(dx: 0.0, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0)) -// -// clipFrame = CGRect(x: containerFrame.minX, y: containerFrame.minY, width: containerFrame.width, height: containerFrame.height) -// } -// } else { -// self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.4) -// self.containerView.layer.cornerRadius = 10.0 -// -// let verticalInset: CGFloat = 44.0 -// -// let maxSide = max(layout.size.width, layout.size.height) -// let minSide = min(layout.size.width, layout.size.height) -// let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) -// clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) -// } -// -// transition.setFrame(view: self.containerView, frame: clipFrame) -// transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: clipFrame.size), completion: nil) -// -// let environment = ViewControllerComponentContainer.Environment( -// statusBarHeight: 0.0, -// navigationHeight: navigationHeight, -// safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), -// additionalInsets: layout.additionalInsets, -// inputHeight: layout.inputHeight ?? 0.0, -// metrics: layout.metrics, -// deviceMetrics: layout.deviceMetrics, -// orientation: layout.metrics.orientation, -// isVisible: self.currentIsVisible, -// theme: self.theme ?? self.presentationData.theme, -// strings: self.presentationData.strings, -// dateTimeFormat: self.presentationData.dateTimeFormat, -// controller: { [weak self] in -// return self?.controller -// } -// ) -// var contentSize = self.hostView.update( -// transition: transition, -// component: self.component, -// environment: { -// environment -// }, -// forceUpdate: true, -// containerSize: CGSize(width: clipFrame.size.width, height: 10000.0) -// ) -// contentSize.height = max(layout.size.height - navigationHeight, contentSize.height) -// transition.setFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil) -// -// self.scrollView.contentSize = contentSize -// } -// -// private var didPlayAppearAnimation = false -// func updateIsVisible(isVisible: Bool) { -// if self.currentIsVisible == isVisible { -// return -// } -// self.currentIsVisible = isVisible -// -// guard let currentLayout = self.currentLayout else { -// return -// } -// self.containerLayoutUpdated(layout: currentLayout.layout, navigationHeight: currentLayout.navigationHeight, transition: .immediate) -// -// if !self.didPlayAppearAnimation { -// self.didPlayAppearAnimation = true -// self.animateIn() -// } -// } -// -// private var defaultTopInset: CGFloat { -// guard let (layout, _) = self.currentLayout else{ -// return 210.0 -// } -// if case .compact = layout.metrics.widthClass { -// var factor: CGFloat = 0.2488 -// if layout.size.width <= 320.0 { -// factor = 0.15 -// } -// return floor(max(layout.size.width, layout.size.height) * factor) -// } else { -// return 210.0 -// } -// } -// -// private func findScrollView(view: UIView?) -> (UIScrollView, ListView?)? { -// if let view = view { -// if let view = view as? UIScrollView { -// return (view, nil) -// } -// if let node = view.asyncdisplaykit_node as? ListView { -// return (node.scroller, node) -// } -// return findScrollView(view: view.superview) -// } else { -// return nil -// } -// } -// -// @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { -// guard let (layout, navigationHeight) = self.currentLayout else { -// return -// } -// -// let isLandscape = layout.orientation == .landscape -// let edgeTopInset = isLandscape ? 0.0 : defaultTopInset -// -// switch recognizer.state { -// case .began: -// let point = recognizer.location(in: self.view) -// let currentHitView = self.hitTest(point, with: nil) -// -// var scrollViewAndListNode = self.findScrollView(view: currentHitView) -// if scrollViewAndListNode?.0.frame.height == self.frame.width { -// scrollViewAndListNode = nil -// } -// let scrollView = scrollViewAndListNode?.0 -// let listNode = scrollViewAndListNode?.1 -// -// let topInset: CGFloat -// if self.isExpanded { -// topInset = 0.0 -// } else { -// topInset = edgeTopInset -// } -// -// self.panGestureArguments = (topInset, 0.0, scrollView, listNode) -// case .changed: -// guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else { -// return -// } -// let visibleContentOffset = listNode?.visibleContentOffset() -// let contentOffset = scrollView?.contentOffset.y ?? 0.0 -// -// var translation = recognizer.translation(in: self.view).y -// -// var currentOffset = topInset + translation -// -// let epsilon = 1.0 -// if case let .known(value) = visibleContentOffset, value <= epsilon { -// if let scrollView = scrollView { -// scrollView.bounces = false -// scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false) -// } -// } else if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon { -// scrollView.bounces = false -// scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) -// } else if let scrollView = scrollView { -// translation = panOffset -// currentOffset = topInset + translation -// if self.isExpanded { -// recognizer.setTranslation(CGPoint(), in: self.view) -// } else if currentOffset > 0.0 { -// scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) -// } -// } -// -// self.panGestureArguments = (topInset, translation, scrollView, listNode) -// -// if !self.isExpanded { -// if currentOffset > 0.0, let scrollView = scrollView { -// scrollView.panGestureRecognizer.setTranslation(CGPoint(), in: scrollView) -// } -// } -// -// var bounds = self.bounds -// if self.isExpanded { -// bounds.origin.y = -max(0.0, translation - edgeTopInset) -// } else { -// bounds.origin.y = -translation -// } -// bounds.origin.y = min(0.0, bounds.origin.y) -// self.bounds = bounds -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate) -// case .ended: -// guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else { -// return -// } -// self.panGestureArguments = nil -// -// let visibleContentOffset = listNode?.visibleContentOffset() -// let contentOffset = scrollView?.contentOffset.y ?? 0.0 -// -// let translation = recognizer.translation(in: self.view).y -// var velocity = recognizer.velocity(in: self.view) -// -// if self.isExpanded { -// if case let .known(value) = visibleContentOffset, value > 0.1 { -// velocity = CGPoint() -// } else if case .unknown = visibleContentOffset { -// velocity = CGPoint() -// } else if contentOffset > 0.1 { -// velocity = CGPoint() -// } -// } -// -// var bounds = self.bounds -// if self.isExpanded { -// bounds.origin.y = -max(0.0, translation - edgeTopInset) -// } else { -// bounds.origin.y = -translation -// } -// bounds.origin.y = min(0.0, bounds.origin.y) -// -// scrollView?.bounces = true -// -// let offset = currentTopInset + panOffset -// let topInset: CGFloat = edgeTopInset -// -// var dismissing = false -// if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0) { -// self.controller?.dismiss(animated: true, completion: nil) -// dismissing = true -// } else if self.isExpanded { -// if velocity.y > 300.0 || offset > topInset / 2.0 { -// self.isExpanded = false -// if let listNode = listNode { -// listNode.scroller.setContentOffset(CGPoint(), animated: false) -// } else if let scrollView = scrollView { -// scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) -// } -// -// let distance = topInset - offset -// let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance) -// let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(transition)) -// } else { -// self.isExpanded = true -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(.animated(duration: 0.3, curve: .easeInOut))) -// } -// } else if (velocity.y < -300.0 || offset < topInset / 2.0) { -// if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode { -// DispatchQueue.main.async { -// listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) -// } -// } -// -// let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset) -// let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) -// self.isExpanded = true -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(transition)) -// } else { -// if let listNode = listNode { -// listNode.scroller.setContentOffset(CGPoint(), animated: false) -// } else if let scrollView = scrollView { -// scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) -// } -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(.animated(duration: 0.3, curve: .easeInOut))) -// } -// -// if !dismissing { -// var bounds = self.bounds -// let previousBounds = bounds -// bounds.origin.y = 0.0 -// self.bounds = bounds -// self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) -// } -// case .cancelled: -// self.panGestureArguments = nil -// -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(.animated(duration: 0.3, curve: .easeInOut))) -// default: -// break -// } -// } -// -// func update(isExpanded: Bool, transition: ContainedViewLayoutTransition) { -// guard isExpanded != self.isExpanded else { -// return -// } -// self.isExpanded = isExpanded -// -// guard let (layout, navigationHeight) = self.currentLayout else { -// return -// } -// self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(transition)) -// } -// } -// -// var node: Node { -// return self.displayNode as! Node -// } -// -// private let context: AccountContext -// private let theme: PresentationTheme? -// private let component: AnyComponent -// private var isInitiallyExpanded = false -// -// private var currentLayout: ContainerViewLayout? -// -// public var pushController: (ViewController) -> Void = { _ in } -// public var presentController: (ViewController) -> Void = { _ in } -// -// public var wasDismissed: (() -> Void)? -// -// public convenience init(context: AccountContext, forceTheme: PresentationTheme? = nil, text: String, entities: [MessageTextEntity] = [], canCopy: Bool, fromLanguage: String?, toLanguage: String? = nil, isExpanded: Bool = false, ignoredLanguages: [String]? = nil) { -// let presentationData = context.sharedContext.currentPresentationData.with { $0 } -// -// var baseLanguageCode = presentationData.strings.baseLanguageCode -// let rawSuffix = "-raw" -// if baseLanguageCode.hasSuffix(rawSuffix) { -// baseLanguageCode = String(baseLanguageCode.dropLast(rawSuffix.count)) -// } -// -// let dontTranslateLanguages = effectiveIgnoredTranslationLanguages(context: context, ignoredLanguages: ignoredLanguages) -// -// var toLanguage = toLanguage ?? baseLanguageCode -// if toLanguage == fromLanguage { -// if fromLanguage == "en" { -// toLanguage = dontTranslateLanguages.first(where: { $0 != "en" }) ?? "en" -// } else { -// toLanguage = "en" -// } -// } -// -// toLanguage = normalizeTranslationLanguage(toLanguage) -// -// var copyTranslationImpl: ((String) -> Void)? -// var changeLanguageImpl: ((String, String, @escaping (String, String) -> Void) -> Void)? -// var expandImpl: (() -> Void)? -// self.init(context: context, component: TranslateScreenComponent(context: context, text: text, entities: entities, fromLanguage: fromLanguage, toLanguage: toLanguage, copyTranslation: !canCopy ? nil : { text in -// copyTranslationImpl?(text) -// }, changeLanguage: { fromLang, toLang, completion in -// changeLanguageImpl?(fromLang, toLang, completion) -// }, expand: { -// expandImpl?() -// }), theme: forceTheme) -// -// self.isInitiallyExpanded = isExpanded -// -// self.title = presentationData.strings.Translate_Title -// -// self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed)) -// -// self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) -// -// copyTranslationImpl = { [weak self] text in -// UIPasteboard.general.string = text -// let content = UndoOverlayContent.copy(text: presentationData.strings.Conversation_TextCopied) -// self?.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) -// self?.dismiss(animated: true, completion: nil) -// } -// -// changeLanguageImpl = { [weak self] fromLang, toLang, completion in -// let pushController = self?.pushController -// let presentController = self?.presentController -// let controller = languageSelectionController(context: context, forceTheme: forceTheme, fromLanguage: fromLang, toLanguage: toLang, completion: { fromLang, toLang in -// let controller = TranslateScreen(context: context, forceTheme: forceTheme, text: text, canCopy: canCopy, fromLanguage: fromLang, toLanguage: toLang, isExpanded: true, ignoredLanguages: ignoredLanguages) -// controller.pushController = pushController ?? { _ in } -// controller.presentController = presentController ?? { _ in } -// presentController?(controller) -// }) -// -// self?.node.temporaryDismiss = true -// self?.dismiss(animated: true, completion: nil) -// -// pushController?(controller) -// } -// -// expandImpl = { [weak self] in -// self?.node.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring)) -// if let currentLayout = self?.currentLayout { -// self?.containerLayoutUpdated(currentLayout, transition: .animated(duration: 0.4, curve: .spring)) -// } -// } -// } -// -// private init(context: AccountContext, component: C, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment { -// self.context = context -// self.component = AnyComponent(component) -// self.theme = theme -// -// var presentationData = context.sharedContext.currentPresentationData.with { $0 } -// if let theme { -// presentationData = presentationData.withUpdated(theme: theme) -// } -// super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) -// } -// -// required public init(coder aDecoder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// @objc private func cancelPressed() { -// self.dismiss(animated: true, completion: nil) -// } -// -// override open func loadDisplayNode() { -// self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme) -// if self.isInitiallyExpanded { -// (self.displayNode as! Node).update(isExpanded: true, transition: .immediate) -// } -// self.displayNodeDidLoad() -// } -// -// public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { -// self.view.endEditing(true) -// let wasDismissed = self.wasDismissed -// if flag { -// self.node.animateOut(completion: { -// super.dismiss(animated: false, completion: {}) -// wasDismissed?() -// completion?() -// }) -// } else { -// super.dismiss(animated: false, completion: {}) -// wasDismissed?() -// completion?() -// } -// } -// -// override open func viewDidAppear(_ animated: Bool) { -// super.viewDidAppear(animated) -// -// self.node.updateIsVisible(isVisible: true) -// } -// -// override open func viewDidDisappear(_ animated: Bool) { -// super.viewDidDisappear(animated) -// -// self.node.updateIsVisible(isVisible: false) -// } -// -// override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { -// var navigationLayout = self.navigationLayout(layout: layout) -// var navigationFrame = navigationLayout.navigationFrame -// -// var layout = layout -// if case .regular = layout.metrics.widthClass { -// let verticalInset: CGFloat = 44.0 -// let maxSide = max(layout.size.width, layout.size.height) -// let minSide = min(layout.size.width, layout.size.height) -// let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) -// let clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) -// navigationFrame.size.width = clipFrame.width -// layout.size = clipFrame.size -// } -// -// navigationFrame.size.height = 56.0 -// navigationLayout.navigationFrame = navigationFrame -// navigationLayout.defaultContentHeight = 56.0 -// -// layout.statusBarHeight = nil -// -// self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition) -// } -// -// override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { -// self.currentLayout = layout -// super.containerLayoutUpdated(layout, transition: transition) -// -// let navigationHeight: CGFloat = 56.0 -// -// self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(transition)) -// } -//} - public func presentTranslateScreen( context: AccountContext, text: String, diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 1fe93910a8..47f9cd7ab1 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -2002,7 +2002,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { var panelWidth = layout.size.width - leftMargin * 2.0 - layout.safeInsets.left - layout.safeInsets.right if self.appearance?.isNarrow == true { - panelWidth = 180.0 + panelWidth = 210.0 } var panelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - panelWidth) / 2.0), y: layout.size.height - contentHeight - insets.bottom - margin), size: CGSize(width: panelWidth, height: contentHeight)) diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index b2469ebd3f..a10bd445db 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -120,6 +120,7 @@ public enum ParsedInternalUrl { case messageLink(slug: String) case collectible(slug: String) case auction(slug: String) + case oauth(url: String) case externalUrl(url: String) } @@ -270,6 +271,18 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou return .peer(.id(peerId), .boost) } } + } else if peerName == "oauth" { + var token: String? + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "startapp" { + token = value + } + } + } + if let _ = token { + return .oauth(url: "https://t.me/\(query)") + } } else { for queryItem in queryItems { if let value = queryItem.value { @@ -1258,6 +1271,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) }) case let .externalUrl(url): return .single(.result(.externalUrl(url))) + case let .oauth(url): + return .single(.result(.oauth(url: url))) } } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 97eab2c5a1..0b7240470d 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -3666,14 +3666,14 @@ public final class WebAppController: ViewController, AttachmentContainable { self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: cancelButtonNode) } - let morehButtonNode: BarComponentHostNode + let moreButtonNode: BarComponentHostNode if let current = self.moreBarButtonNode { - morehButtonNode = current - morehButtonNode.component = moreComponent + moreButtonNode = current + moreButtonNode.component = moreComponent } else { - morehButtonNode = BarComponentHostNode(component: moreComponent, size: barButtonSize) - self.moreBarButtonNode = morehButtonNode - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: morehButtonNode) + moreButtonNode = BarComponentHostNode(component: moreComponent, size: barButtonSize) + self.moreBarButtonNode = moreButtonNode + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: moreButtonNode) } } @@ -4181,7 +4181,16 @@ public func standaloneWebAppController( getSourceRect: (() -> CGRect?)? = nil, verifyAgeCompletion: ((Int) -> Void)? = nil ) -> ViewController { - let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, isFullSize: params.fullSize, makeEntityInputView: { + let controller = AttachmentController( + context: context, + updatedPresentationData: updatedPresentationData, + chatLocation: .peer(id: params.peerId), + buttons: [.standalone], + initialButton: .standalone, + fromMenu: params.source == .menu, + hasTextInput: false, + isFullSize: params.fullSize, + makeEntityInputView: { return nil }) controller.requestController = { _, present in @@ -4192,6 +4201,7 @@ public func standaloneWebAppController( webAppController.requestSwitchInline = requestSwitchInline webAppController.verifyAgeCompletion = verifyAgeCompletion present(webAppController, webAppController.mediaPickerContext) + return true } controller.willDismiss = willDismiss controller.didDismiss = didDismiss diff --git a/versions.json b/versions.json index 83ea3fd866..08c7c722f1 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "12.5", + "app": "12.5.1", "xcode": "26.2", "bazel": "8.4.2:45e9388abf21d1107e146ea366ad080eb93cb6a5f3a4a3b048f78de0bc3faffa", "macos": "26"