diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 333768a1ac..edd8ef3a4c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -6978,3 +6978,9 @@ Sorry for the inconvenience."; "Notifications.On" = "On"; "Notifications.Off" = "Off"; + +"AuthSessions.View.Device" = "Device"; +"AuthSessions.View.Location" = "Location"; +"AuthSessions.View.IP" = "IP"; +"AuthSessions.View.TerminateSession" = "Terminate Session"; +"AuthSessions.View.Logout" = "Log Out"; diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift index b0ff2f0de5..96dd1dd82e 100644 --- a/submodules/CallListUI/Sources/CallListCallItem.swift +++ b/submodules/CallListUI/Sources/CallListCallItem.swift @@ -208,6 +208,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { self.backgroundNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true @@ -513,64 +514,6 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor } - switch item.style { - case .plain: - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - if strongSelf.topStripeNode.supernode != nil { - strongSelf.topStripeNode.removeFromSupernode() - } - if !last && strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 1) - } else if last && strongSelf.bottomStripeNode.supernode != nil { - strongSelf.bottomStripeNode.removeFromSupernode() - } - if strongSelf.maskNode.supernode != nil { - strongSelf.maskNode.removeFromSupernode() - } - transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))) - case .blocks: - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) - } - if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) - } - if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) - } - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { - case .sameSection(false): - strongSelf.topStripeNode.isHidden = true - default: - hasTopCorners = true - strongSelf.topStripeNode.isHidden = hasCorners - } - let bottomStripeInset: CGFloat - switch neighbors.bottom { - case .sameSection(false): - bottomStripeInset = leftInset - default: - bottomStripeInset = 0.0 - hasBottomCorners = true - strongSelf.bottomStripeNode.isHidden = hasCorners - } - - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : 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) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) - transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight))) - } - if let editableControlSizeAndApply = editableControlSizeAndApply { let editableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: CGSize(width: editableControlSizeAndApply.0, height: nodeLayout.contentSize.height)) if strongSelf.editableControlNode == nil { @@ -600,6 +543,65 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { }) } + switch item.style { + case .plain: + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode != nil { + strongSelf.topStripeNode.removeFromSupernode() + } + if !last && strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 1) + } else if last && strongSelf.bottomStripeNode.supernode != nil { + strongSelf.bottomStripeNode.removeFromSupernode() + } + if strongSelf.maskNode.supernode != nil { + strongSelf.maskNode.removeFromSupernode() + } + transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))) + case .blocks: + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.addSubnode(strongSelf.maskNode) + } + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = leftInset + default: + bottomStripeInset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : 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) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) + transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight))) + } + + transition.updateFrameAdditive(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 52.0, y: floor((contentSize.height - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter))) let _ = titleApply() diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift index 3115272611..cd2c1bf445 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift @@ -130,7 +130,9 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL self.bottomStripeNode = ASDisplayNode() self.bottomStripeNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.avatarNode = ASImageNode() self.avatarNode.isUserInteractionEnabled = false @@ -311,7 +313,7 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index aa38e59f3d..543b89ff79 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -139,6 +139,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN self.bottomStripeNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false @@ -315,7 +316,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 44e59fa632..8a916d1b0c 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -921,7 +921,12 @@ open class NavigationBar: ASDisplayNode { } } + public var isBackgroundVisible: Bool { + return self.backgroundNode.alpha == 1.0 + } + public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + let alpha = max(0.0, min(1.0, alpha)) transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15) transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15) } diff --git a/submodules/Display/Source/NavigationTransitionCoordinator.swift b/submodules/Display/Source/NavigationTransitionCoordinator.swift index 692f8fa287..b133473604 100644 --- a/submodules/Display/Source/NavigationTransitionCoordinator.swift +++ b/submodules/Display/Source/NavigationTransitionCoordinator.swift @@ -125,7 +125,11 @@ final class NavigationTransitionCoordinator { var dimInset: CGFloat = 0.0 if let bottomNavigationBar = self.bottomNavigationBar , self.inlineNavigationBarTransition { - dimInset = bottomNavigationBar.frame.maxY + if self.bottomNavigationBar?.isBackgroundVisible == false || self.topNavigationBar?.isBackgroundVisible == false { + + } else { + dimInset = bottomNavigationBar.frame.maxY + } } let containerSize = self.container.bounds.size diff --git a/submodules/Display/Source/TabBarController.swift b/submodules/Display/Source/TabBarController.swift index 64665cd01b..6f10b84788 100644 --- a/submodules/Display/Source/TabBarController.swift +++ b/submodules/Display/Source/TabBarController.swift @@ -273,6 +273,12 @@ open class TabBarController: ViewController { self.displayNodeDidLoad() } + public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + let alpha = max(0.0, min(1.0, alpha)) + transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.backgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.15) + } + private func updateSelectedIndex() { if !self.isNodeLoaded { return diff --git a/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift b/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift index 233701f3f6..e8baad0d86 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift @@ -220,7 +220,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry { let arguments = arguments as! InviteLinkEditControllerArguments switch self { case let .title(_, placeholder, value): - return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, sectionId: self.section, textUpdated: { value in + return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, maxLength: 32, sectionId: self.section, textUpdated: { value in arguments.updateState { state in var updatedState = state updatedState.title = value @@ -421,7 +421,7 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio timeLimit = .unlimited } - initialState = InviteLinkEditControllerState(title: "", usage: InviteLinkUsageLimit(value: usageLimit), time: timeLimit, requestApproval: invite.requestApproval, pickingTimeLimit: false, pickingUsageLimit: false) + initialState = InviteLinkEditControllerState(title: invite.title ?? "", usage: InviteLinkUsageLimit(value: usageLimit), time: timeLimit, requestApproval: invite.requestApproval, pickingTimeLimit: false, pickingUsageLimit: false) } else { initialState = InviteLinkEditControllerState(title: "", usage: .unlimited, time: .unlimited, requestApproval: false, pickingTimeLimit: false, pickingUsageLimit: false) } @@ -525,11 +525,13 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio expireDate = 0 } + let titleString = state.title.trimmingCharacters(in: .whitespacesAndNewlines) + let title = titleString.isEmpty ? nil : titleString let usageLimit = state.usage.value let requestNeeded = state.requestApproval if invite == nil { - let _ = (context.engine.peers.createPeerExportedInvitation(peerId: peerId, expireDate: expireDate, usageLimit: requestNeeded ? 0 : usageLimit, requestNeeded: requestNeeded) + let _ = (context.engine.peers.createPeerExportedInvitation(peerId: peerId, title: title, expireDate: expireDate, usageLimit: requestNeeded ? 0 : usageLimit, requestNeeded: requestNeeded) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) |> deliverOnMainQueue).start(next: { invite in completion?(invite) @@ -548,7 +550,7 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio dismissImpl?() return } - let _ = (context.engine.peers.editPeerExportedInvitation(peerId: peerId, link: initialInvite.link, expireDate: expireDate, usageLimit: requestNeeded ? 0 : usageLimit, requestNeeded: requestNeeded) + let _ = (context.engine.peers.editPeerExportedInvitation(peerId: peerId, link: initialInvite.link, title: title, expireDate: expireDate, usageLimit: requestNeeded ? 0 : usageLimit, requestNeeded: requestNeeded) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) |> deliverOnMainQueue).start(next: { invite in completion?(invite) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift index 9e55622cfc..eaf3fd7027 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift @@ -284,7 +284,7 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData, let mainInvite: ExportedInvitation? var isPublic = false if let peer = peer, let address = peer.addressName, !address.isEmpty && admin == nil { - mainInvite = ExportedInvitation(link: "t.me/\(address)", isPermanent: true, requestApproval: false, isRevoked: false, adminId: EnginePeer.Id(0), date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil) + mainInvite = ExportedInvitation(link: "t.me/\(address)", title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: EnginePeer.Id(0), date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil) isPublic = true } else if let invites = invites, let invite = invites.first(where: { $0.isPermanent && !$0.isRevoked }) { mainInvite = invite diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index 10a494e048..d2e5e47fa8 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -841,7 +841,7 @@ public final class InviteLinkViewController: ViewController { self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor self.headerBackgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor - self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.InviteLink_InviteLink, font: titleFont, textColor: self.presentationData.theme.actionSheet.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: titleFont, textColor: self.presentationData.theme.actionSheet.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: subtitleFont, textColor: self.presentationData.theme.list.itemSecondaryTextColor) let accentColor = self.presentationData.theme.actionSheet.controlAccentColor @@ -935,6 +935,7 @@ public final class InviteLinkViewController: ViewController { transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 68.0)) var titleText = self.presentationData.strings.InviteLink_InviteLink + var subtitleText = "" var subtitleColor = self.presentationData.theme.list.itemSecondaryTextColor @@ -970,6 +971,10 @@ public final class InviteLinkViewController: ViewController { } } + if let title = self.invite.title, !title.isEmpty { + titleText = title + } + self.titleNode.attributedText = NSAttributedString(string: titleText, font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: subtitleColor) diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift index f73bd7646f..c7b853565f 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift @@ -338,6 +338,9 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode { let inviteLink = item.invite?.link.replacingOccurrences(of: "https://", with: "") ?? "" var titleText = inviteLink + if let title = item.invite?.title, !title.isEmpty { + titleText = title + } var subtitleText: String = "" var timerValue: TimerNode.Value? diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift index 5a84ddde8e..0e10e300a2 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift @@ -385,7 +385,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode { avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.height / 2.0, width: targetRect.width, height: targetRect.height) avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.height / 2.0), size: CGSize()) avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0) - avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) + avatarListNode.topShadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) avatarListContainerNode.addSubnode(avatarListNode) avatarListContainerNode.addSubnode(avatarListNode.controlsClippingOffsetNode) @@ -675,7 +675,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode { strongSelf.avatarNode.frame = avatarFrame if let importer = item.importer, let peer = importer.peer.peer.flatMap({ EnginePeer($0) }) { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false, storeUnrounded: true) } transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.size)) diff --git a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift index 929a0d82ca..fca0a1693c 100644 --- a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift +++ b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift @@ -98,6 +98,7 @@ public class ItemListAddressItemNode: ListViewItemNode { private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode + private let maskNode: ASImageNode private let imageNode: TransformImageNode private let iconNode: ASImageNode private var selectionNode: ItemListSelectableControlNode? @@ -118,6 +119,9 @@ public class ItemListAddressItemNode: ListViewItemNode { self.bottomStripeNode = ASDisplayNode() self.bottomStripeNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true @@ -286,7 +290,10 @@ public class ItemListAddressItemNode: ListViewItemNode { if strongSelf.bottomStripeNode.supernode == nil { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0) } - + if strongSelf.maskNode.supernode != nil { + strongSelf.maskNode.removeFromSupernode() + } + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)) case .blocks: leftInset = 16.0 + params.leftInset @@ -300,11 +307,19 @@ public class ItemListAddressItemNode: ListViewItemNode { if strongSelf.bottomStripeNode.supernode == nil { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } + if strongSelf.maskNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false switch neighbors.top { case .sameSection(false): strongSelf.topStripeNode.isHidden = true default: - strongSelf.topStripeNode.isHidden = !item.displayDecorations + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners || !item.displayDecorations } let bottomStripeInset: CGFloat let bottomStripeOffset: CGFloat @@ -315,8 +330,14 @@ public class ItemListAddressItemNode: ListViewItemNode { default: bottomStripeInset = 0.0 bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : 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) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) } diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index bed1fb9b22..0836eb70e4 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -15,6 +15,7 @@ public enum ItemListPeerActionItemHeight { public enum ItemListPeerActionItemColor { case accent case destructive + case disabled } public class ItemListPeerActionItem: ListViewItem, ItemListItem { @@ -29,7 +30,7 @@ public class ItemListPeerActionItem: ListViewItem, ItemListItem { public let sectionId: ItemListSectionId let action: (() -> Void)? - public init(presentationData: ItemListPresentationData, icon: UIImage?, title: String, alwaysPlain: Bool = false, hasSeparator: Bool = true, sectionId: ItemListSectionId, height: ItemListPeerActionItemHeight = .peerList, color: ItemListPeerActionItemColor = .accent, editing: Bool, action: (() -> Void)?) { + public init(presentationData: ItemListPresentationData, icon: UIImage?, title: String, alwaysPlain: Bool = false, hasSeparator: Bool = true, sectionId: ItemListSectionId, height: ItemListPeerActionItemHeight = .peerList, color: ItemListPeerActionItemColor = .accent, editing: Bool = false, action: (() -> Void)?) { self.presentationData = presentationData self.icon = icon self.title = title @@ -182,6 +183,8 @@ class ItemListPeerActionItemNode: ListViewItemNode { textColor = item.presentationData.theme.list.itemAccentColor case .destructive: textColor = item.presentationData.theme.list.itemDestructiveColor + case .disabled: + textColor = item.presentationData.theme.list.itemDisabledTextColor } let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - editingOffset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index c697a5cd8d..1d209a662f 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -492,7 +492,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo self.bottomStripeNode = ASDisplayNode() self.bottomStripeNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.containerNode = ContextControllerSourceNode() @@ -991,7 +993,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noInsets diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index ed023b003b..733cbccd8d 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -204,6 +204,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { self.bottomStripeNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.imageNode = TransformImageNode() self.imageNode.isLayerBacked = !smartInvertColorsEnabled() @@ -659,7 +660,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 56395ba0f4..408cfeaea3 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -350,13 +350,17 @@ open class ItemListControllerNode: ASDisplayNode { if let strongSelf = self { var previousContentOffsetValue: CGFloat? - if let previousContentOffset = strongSelf.previousContentOffset, case let .known(value) = previousContentOffset { - previousContentOffsetValue = value + if let previousContentOffset = strongSelf.previousContentOffset { + if case let .known(value) = previousContentOffset { + previousContentOffsetValue = value + } else { + previousContentOffsetValue = 30.0 + } } switch offset { case let .known(value): let transition: ContainedViewLayoutTransition - if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 { + if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue >= 30.0 { transition = .animated(duration: 0.2, curve: .easeInOut) } else { transition = .immediate @@ -699,7 +703,11 @@ open class ItemListControllerNode: ASDisplayNode { if let validLayout = self.validLayout { updatedNode.updateLayout(layout: validLayout.0, navigationBarHeight: validLayout.1, transition: .immediate) } - self.insertSubnode(updatedNode, aboveSubnode: self.listNode) + if self.rightOverlayNode.supernode != nil { + self.insertSubnode(updatedNode, aboveSubnode: self.rightOverlayNode) + } else { + self.insertSubnode(updatedNode, aboveSubnode: self.listNode) + } updatedNode.activate() } } else { diff --git a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift index a46b433791..c236527181 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift @@ -139,6 +139,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode { self.backgroundNode.backgroundColor = .white self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true diff --git a/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift b/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift index 45cb7e2876..9788d068b0 100644 --- a/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift +++ b/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift @@ -141,7 +141,9 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode { self.bottomStripeNode = ASDisplayNode() self.bottomStripeNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.iconNode = TransformImageNode() diff --git a/submodules/LocationUI/Sources/LocationLiveListItem.swift b/submodules/LocationUI/Sources/LocationLiveListItem.swift index c42ffcfd35..49a4b12a8c 100644 --- a/submodules/LocationUI/Sources/LocationLiveListItem.swift +++ b/submodules/LocationUI/Sources/LocationLiveListItem.swift @@ -254,6 +254,7 @@ final class LocationLiveListItemNode: ListViewItemNode { if strongSelf.drivingButtonNode == nil { strongSelf.drivingButtonNode = SolidRoundedButtonNode(icon: generateTintedImage(image: UIImage(bundleImageName: "Location/DirectionsDriving"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor), theme: buttonTheme, fontSize: 15.0, height: 32.0, cornerRadius: 16.0) strongSelf.drivingButtonNode?.alpha = 0.0 + strongSelf.drivingButtonNode?.iconSpacing = 5.0 strongSelf.drivingButtonNode?.allowsGroupOpacity = true strongSelf.drivingButtonNode?.pressed = { [weak self] in if let item = self?.item { @@ -264,6 +265,7 @@ final class LocationLiveListItemNode: ListViewItemNode { strongSelf.transitButtonNode = SolidRoundedButtonNode(icon: generateTintedImage(image: UIImage(bundleImageName: "Location/DirectionsTransit"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor), theme: buttonTheme, fontSize: 15.0, height: 32.0, cornerRadius: 16.0) strongSelf.transitButtonNode?.alpha = 0.0 + strongSelf.transitButtonNode?.iconSpacing = 2.0 strongSelf.transitButtonNode?.allowsGroupOpacity = true strongSelf.transitButtonNode?.pressed = { [weak self] in if let item = self?.item { @@ -274,6 +276,7 @@ final class LocationLiveListItemNode: ListViewItemNode { strongSelf.walkingButtonNode = SolidRoundedButtonNode(icon: generateTintedImage(image: UIImage(bundleImageName: "Location/DirectionsWalking"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor), theme: buttonTheme, fontSize: 15.0, height: 32.0, cornerRadius: 16.0) strongSelf.walkingButtonNode?.alpha = 0.0 + strongSelf.walkingButtonNode?.iconSpacing = 2.0 strongSelf.walkingButtonNode?.allowsGroupOpacity = true strongSelf.walkingButtonNode?.pressed = { [weak self] in if let item = self?.item { diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index 66973b6a39..f47cb52ca4 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -447,7 +447,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { public let controlsContainerNode: ASDisplayNode public let controlsClippingNode: ASDisplayNode public let controlsClippingOffsetNode: ASDisplayNode - public let shadowNode: ASImageNode + public let topShadowNode: ASImageNode + public let bottomShadowNode: ASImageNode public let contentNode: ASDisplayNode let leftHighlightNode: ASDisplayNode @@ -631,10 +632,15 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.controlsClippingNode.isUserInteractionEnabled = false self.controlsClippingNode.clipsToBounds = true - self.shadowNode = ASImageNode() - self.shadowNode.displaysAsynchronously = false - self.shadowNode.displayWithoutProcessing = true - self.shadowNode.contentMode = .scaleToFill + self.topShadowNode = ASImageNode() + self.topShadowNode.displaysAsynchronously = false + self.topShadowNode.displayWithoutProcessing = true + self.topShadowNode.contentMode = .scaleToFill + + self.bottomShadowNode = ASImageNode() + self.bottomShadowNode.displaysAsynchronously = false + self.bottomShadowNode.displayWithoutProcessing = true + self.bottomShadowNode.contentMode = .scaleToFill do { let size = CGSize(width: 88.0, height: 88.0) @@ -656,13 +662,20 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() if let image = image { - self.shadowNode.image = generateImage(image.size, contextGenerator: { size, context in + self.topShadowNode.image = generateImage(image.size, contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.translateBy(x: size.width / 2.0, y: size.height / 2.0) context.rotate(by: -CGFloat.pi / 2.0) context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) }) + self.bottomShadowNode.image = generateImage(image.size, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context.rotate(by: CGFloat.pi / 2.0) + context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + }) } } } @@ -674,7 +687,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.addSubnode(self.contentNode) self.controlsContainerNode.addSubnode(self.highlightContainerNode) - self.controlsContainerNode.addSubnode(self.shadowNode) + self.controlsContainerNode.addSubnode(self.topShadowNode) + self.addSubnode(self.bottomShadowNode) self.controlsContainerNode.addSubnode(self.stripContainerNode) self.controlsClippingNode.addSubnode(self.controlsContainerNode) self.controlsClippingOffsetNode.addSubnode(self.controlsClippingNode) diff --git a/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift b/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift index d4c9836e35..66fcbf2133 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift @@ -32,7 +32,7 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { self.pushController = pushController self.dismissInput = dismissInput self.searchMode = searchMode - activityDisposable.set((activity.get() |> mapToSignal { value -> Signal in + self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal in if value { return .single(value) |> delay(0.2, queue: Queue.mainQueue()) } else { diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift index 8ca346d42a..9e0c500f75 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift @@ -50,8 +50,8 @@ extension _AdaptedPostboxEncoder.KeyedContainer: KeyedEncodingContainerProtocol let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) try! value.encode(to: innerEncoder) - let type = type(of: value) - let typeString = "\(type)" + let typeOfValue = type(of: value) + let typeString = "\(typeOfValue)" var isDictionary = false if typeString.hasPrefix("Dictionary<") { isDictionary = true diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index d4c4c62c47..8acb504124 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -94,6 +94,8 @@ final class ChangePhoneNumberController: ViewController, MFMailComposeViewContro strongSelf.controllerNode.updateCountryCode() } }) + + self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } override func viewWillAppear(_ animated: Bool) { diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift index 7ed7e6d773..8207382276 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift @@ -120,6 +120,8 @@ public final class ChangePhoneNumberIntroController: ViewController { self?.proceed() } self.displayNodeDidLoad() + + self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } public override func viewDidAppear(_ animated: Bool) { diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index 9b9fae40e0..d296d5c744 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -11,6 +11,7 @@ import ItemListUI import PresentationDataUtils import AccountContext import OpenInExternalAppUI +import ItemListPeerActionItem private final class DataAndStorageControllerArguments { let openStorageUsage: () -> Void @@ -322,12 +323,16 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/WiFi")?.precomposed(), title: text, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: { arguments.openAutomaticDownloadConnectionType(.wifi) }) - case let .automaticDownloadReset(_, text, enabled): - return ItemListActionItem(presentationData: presentationData, title: text, kind: enabled ? .generic : .disabled, alignment: .natural, sectionId: self.section, style: .blocks, action: { + case let .automaticDownloadReset(theme, text, enabled): + var icon = PresentationResourcesItemList.resetIcon(theme) + if !enabled { + icon = generateTintedImage(image: icon, color: theme.list.itemDisabledTextColor) + } + return ItemListPeerActionItem(presentationData: presentationData, icon: icon, title: text, sectionId: self.section, height: .generic, color: enabled ? .accent : .disabled, editing: false, action: { if enabled { arguments.resetAutomaticDownload() } - }, tag: DataAndStorageEntryTag.automaticDownloadReset) + }) case let .autoplayHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .autoplayGifs(_, text, value): diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift index d82ea03994..8dd03be9db 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift @@ -141,6 +141,7 @@ private final class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode { self.bottomStripeNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.infoIconNode = ASImageNode() self.infoIconNode.isLayerBacked = true @@ -369,7 +370,7 @@ private final class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) diff --git a/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift b/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift index ff32d26dab..c412e75d91 100644 --- a/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift +++ b/submodules/SettingsUI/Sources/NotificationsPeerCategoryController.swift @@ -1,6 +1,7 @@ import Foundation import UIKit import Display +import AsyncDisplayKit import SwiftSignalKit import Postbox import TelegramCore @@ -375,47 +376,41 @@ public enum NotificationsPeerCategory { private final class NotificationExceptionState : Equatable { let mode: NotificationExceptionMode - let isSearchMode: Bool let revealedPeerId: PeerId? let editing: Bool - init(mode: NotificationExceptionMode, isSearchMode: Bool = false, revealedPeerId: PeerId? = nil, editing: Bool = false) { + init(mode: NotificationExceptionMode, revealedPeerId: PeerId? = nil, editing: Bool = false) { self.mode = mode - self.isSearchMode = isSearchMode self.revealedPeerId = revealedPeerId self.editing = editing } func withUpdatedMode(_ mode: NotificationExceptionMode) -> NotificationExceptionState { - return NotificationExceptionState(mode: mode, isSearchMode: self.isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing) - } - - func withUpdatedSearchMode(_ isSearchMode: Bool) -> NotificationExceptionState { - return NotificationExceptionState(mode: self.mode, isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing) + return NotificationExceptionState(mode: mode, revealedPeerId: self.revealedPeerId, editing: self.editing) } func withUpdatedEditing(_ editing: Bool) -> NotificationExceptionState { - return NotificationExceptionState(mode: self.mode, isSearchMode: self.isSearchMode, revealedPeerId: self.revealedPeerId, editing: editing) + return NotificationExceptionState(mode: self.mode, revealedPeerId: self.revealedPeerId, editing: editing) } func withUpdatedRevealedPeerId(_ revealedPeerId: PeerId?) -> NotificationExceptionState { - return NotificationExceptionState(mode: self.mode, isSearchMode: self.isSearchMode, revealedPeerId: revealedPeerId, editing: self.editing) + return NotificationExceptionState(mode: self.mode, revealedPeerId: revealedPeerId, editing: self.editing) } func withUpdatedPeerSound(_ peer: Peer, _ sound: PeerMessageSound) -> NotificationExceptionState { - return NotificationExceptionState(mode: mode.withUpdatedPeerSound(peer, sound), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing) + return NotificationExceptionState(mode: mode.withUpdatedPeerSound(peer, sound), revealedPeerId: self.revealedPeerId, editing: self.editing) } func withUpdatedPeerMuteInterval(_ peer: Peer, _ muteInterval: Int32?) -> NotificationExceptionState { - return NotificationExceptionState(mode: mode.withUpdatedPeerMuteInterval(peer, muteInterval), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing) + return NotificationExceptionState(mode: mode.withUpdatedPeerMuteInterval(peer, muteInterval), revealedPeerId: self.revealedPeerId, editing: self.editing) } func withUpdatedPeerDisplayPreviews(_ peer: Peer, _ displayPreviews: PeerNotificationDisplayPreviews) -> NotificationExceptionState { - return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing) + return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), revealedPeerId: self.revealedPeerId, editing: self.editing) } static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool { - return lhs.mode == rhs.mode && lhs.isSearchMode == rhs.isSearchMode && lhs.revealedPeerId == rhs.revealedPeerId && lhs.editing == rhs.editing + return lhs.mode == rhs.mode && lhs.revealedPeerId == rhs.revealedPeerId && lhs.editing == rhs.editing } } @@ -714,7 +709,30 @@ public func notificationsPeerCategoryController(context: AccountContext, categor } } - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Notifications_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let leftNavigationButton: ItemListNavigationButton? + let rightNavigationButton: ItemListNavigationButton? + if !state.mode.peerIds.isEmpty { + if state.editing { + leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {}) + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + updateState { value in + return value.withUpdatedEditing(false) + } + }) + } else { + leftNavigationButton = nil + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { + updateState { value in + return value.withUpdatedEditing(true) + } + }) + } + } else { + leftNavigationButton = nil + rightNavigationButton = nil + } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Notifications_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: focusOnItemTag, initialScrollToItem: scrollToItem) return (controllerState, (listState, arguments)) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift index af1ebaef10..28de53a1a2 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift @@ -46,8 +46,9 @@ final class ItemListRecentSessionItem: ListViewItem, ItemListItem { let sectionId: ItemListSectionId let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void let removeSession: (Int64) -> Void + let action: (() -> Void)? - init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editable: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) { + init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editable: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, action: (() -> Void)?) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.session = session @@ -58,6 +59,7 @@ final class ItemListRecentSessionItem: ListViewItem, ItemListItem { self.sectionId = sectionId self.setSessionIdWithRevealedOptions = setSessionIdWithRevealedOptions self.removeSession = removeSession + self.action = action } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -97,11 +99,29 @@ final class ItemListRecentSessionItem: ListViewItem, ItemListItem { } } } + + public var selectable: Bool = true + public func selected(listView: ListView){ + listView.clearHighlightAnimated(true) + + if self.enabled { + self.action?() + } + } } private func iconForSession(_ session: RecentAccountSession) -> UIImage? { let platform = session.platform.lowercased() let device = session.deviceModel.lowercased() + if device.contains("chrome") && !device.contains("chromebook") { + return UIImage(bundleImageName: "Settings/Devices/Chrome") + } + if device.contains("brave") { + return UIImage(bundleImageName: "Settings/Devices/Brave") + } + if device.contains("vivaldi") { + return UIImage(bundleImageName: "Settings/Devices/Vivaldi") + } if device.contains("safari") { return UIImage(bundleImageName: "Settings/Devices/Safari") } @@ -111,12 +131,27 @@ private func iconForSession(_ session: RecentAccountSession) -> UIImage? { if device.contains("opera") { return UIImage(bundleImageName: "Settings/Devices/Opera") } + if platform.contains("android") { + return UIImage(bundleImageName: "Settings/Devices/Android") + } if platform.contains("ios") || platform.contains("macos") { return UIImage(bundleImageName: "Settings/Devices/iOS") } + if platform.contains("linux") { + return UIImage(bundleImageName: "Settings/Devices/Linux") + } + if platform.contains("windows") { + return UIImage(bundleImageName: "Settings/Devices/Windows") + } return nil } +private func trimmedLocationName(_ session: RecentAccountSession) -> String { + var country = session.country + country = country.replacingOccurrences(of: "United Arab Emirates", with: "UAE") + return country +} + class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode @@ -130,12 +165,25 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { private let appNode: TextNode private let locationNode: TextNode + private let containerNode: ASDisplayNode + override var controlsContainer: ASDisplayNode { + return self.containerNode + } + private let activateArea: AccessibilityAreaNode private var layoutParams: (ItemListRecentSessionItem, ListViewItemLayoutParams, ItemListNeighbors)? private var editableControlNode: ItemListEditableControlNode? + override public var canBeSelected: Bool { + if let item = self.layoutParams?.0, let _ = item.action { + return true + } else { + return false + } + } + init() { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true @@ -147,6 +195,9 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { self.bottomStripeNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + self.containerNode = ASDisplayNode() self.iconNode = ASImageNode() self.iconNode.cornerRadius = 7.0 @@ -174,10 +225,11 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) - self.addSubnode(self.iconNode) - self.addSubnode(self.titleNode) - self.addSubnode(self.appNode) - self.addSubnode(self.locationNode) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.iconNode) + self.containerNode.addSubnode(self.titleNode) + self.containerNode.addSubnode(self.appNode) + self.containerNode.addSubnode(self.locationNode) self.addSubnode(self.activateArea) } @@ -221,23 +273,23 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { titleAttributedString = NSAttributedString(string: "\(item.session.appName) \(item.session.appVersion)", font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) - var appString = "" + var deviceString = "" if !item.session.deviceModel.isEmpty { - appString = item.session.deviceModel + deviceString = item.session.deviceModel } if !item.session.platform.isEmpty { - if !appString.isEmpty { - appString += ", " + if !deviceString.isEmpty { + deviceString += ", " } - appString += item.session.platform + deviceString += item.session.platform } if !item.session.systemVersion.isEmpty { - if !appString.isEmpty { - appString += ", " + if !deviceString.isEmpty { + deviceString += ", " } - appString += item.session.systemVersion + deviceString += item.session.systemVersion } var updatedIcon: UIImage? @@ -245,7 +297,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { updatedIcon = iconForSession(item.session) } - appAttributedString = NSAttributedString(string: appString, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) + appAttributedString = NSAttributedString(string: deviceString, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) let label: String if item.session.isCurrent { @@ -255,7 +307,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { label = stringForRelativeActivityTimestamp(strings: item.presentationData.strings, dateTimeFormat: item.dateTimeFormat, relativeTimestamp: item.session.activityDate, relativeTo: timestamp) } - locationAttributedString = NSAttributedString(string: "\(item.session.country) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) + locationAttributedString = NSAttributedString(string: "\(trimmedLocationName(item.session)) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) let leftInset: CGFloat = 59.0 + params.leftInset @@ -403,7 +455,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) @@ -432,6 +484,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : 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) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) transition.updateFrame(node: 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, height: separatorHeight))) @@ -441,7 +494,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: appLayout.size)) transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.appNode.frame.maxY + textSpacing), size: locationLayout.size)) - strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 75.0 + UIScreenPixel + UIScreenPixel)) + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) @@ -452,6 +505,44 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { } } + override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { + super.setHighlighted(highlighted, at: point, animated: animated) + + if highlighted && (self.layoutParams?.0.enabled ?? false) { + self.highlightedBackgroundNode.alpha = 1.0 + if self.highlightedBackgroundNode.supernode == nil { + var anchorNode: ASDisplayNode? + if self.bottomStripeNode.supernode != nil { + anchorNode = self.bottomStripeNode + } else if self.topStripeNode.supernode != nil { + anchorNode = self.topStripeNode + } else if self.backgroundNode.supernode != nil { + anchorNode = self.backgroundNode + } + if let anchorNode = anchorNode { + self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) + } else { + self.addSubnode(self.highlightedBackgroundNode) + } + } + } else { + if self.highlightedBackgroundNode.supernode != nil { + if animated { + self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() + } + } + }) + self.highlightedBackgroundNode.alpha = 0.0 + } else { + self.highlightedBackgroundNode.removeFromSupernode() + } + } + } + } + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift index 716e4df93a..13e6d8353e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift @@ -42,8 +42,9 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { let sectionId: ItemListSectionId let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void let removeSession: (Int64) -> Void + let action: (() -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) { + init(context: AccountContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, action: (() -> Void)?) { self.context = context self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat @@ -56,6 +57,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { self.sectionId = sectionId self.setSessionIdWithRevealedOptions = setSessionIdWithRevealedOptions self.removeSession = removeSession + self.action = action } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -95,10 +97,26 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { } } } + + public var selectable: Bool = true + public func selected(listView: ListView){ + listView.clearHighlightAnimated(true) + + if self.enabled { + self.action?() + } + } } private let avatarFont = avatarPlaceholderFont(size: 11.0) +private func trimmedLocationName(_ session: WebAuthorization) -> String { + var country = session.region + country = country.replacingOccurrences(of: "United Arab Emirates", with: "UAE") + return country +} + + class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode @@ -111,12 +129,19 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { private let titleNode: TextNode private let appNode: TextNode private let locationNode: TextNode - private let labelNode: TextNode private var layoutParams: (ItemListWebsiteItem, ListViewItemLayoutParams, ItemListNeighbors)? private var editableControlNode: ItemListEditableControlNode? + override public var canBeSelected: Bool { + if let item = self.layoutParams?.0, let _ = item.action { + return true + } else { + return false + } + } + init() { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true @@ -128,6 +153,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.bottomStripeNode.isLayerBacked = true self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode.cornerRadius = 7.0 @@ -148,11 +174,6 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.locationNode.contentMode = .left self.locationNode.contentsScale = UIScreen.main.scale - self.labelNode = TextNode() - self.labelNode.isUserInteractionEnabled = false - self.labelNode.contentMode = .left - self.labelNode.contentsScale = UIScreen.main.scale - self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true @@ -162,7 +183,6 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.addSubnode(self.titleNode) self.addSubnode(self.appNode) self.addSubnode(self.locationNode) - self.addSubnode(self.labelNode) } func asyncLayout() -> (_ item: ItemListWebsiteItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { @@ -221,7 +241,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) let label = stringForRelativeActivityTimestamp(strings: item.presentationData.strings, dateTimeFormat: item.dateTimeFormat, relativeTimestamp: item.website.dateActive, relativeTo: timestamp) - locationAttributedString = NSAttributedString(string: "\(item.website.region) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) + locationAttributedString = NSAttributedString(string: "\(trimmedLocationName(item.website)) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) let leftInset: CGFloat = 59.0 + params.leftInset @@ -341,7 +361,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.addSubnode(strongSelf.maskNode) } let hasCorners = itemListHasRoundedBlockLayout(params) @@ -381,7 +401,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 30.0), size: appLayout.size)) transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 50.0), size: locationLayout.size)) - strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 75.0 + UIScreenPixel + UIScreenPixel)) + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) @@ -392,6 +412,44 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { } } + override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { + super.setHighlighted(highlighted, at: point, animated: animated) + + if highlighted && (self.layoutParams?.0.enabled ?? false) { + self.highlightedBackgroundNode.alpha = 1.0 + if self.highlightedBackgroundNode.supernode == nil { + var anchorNode: ASDisplayNode? + if self.bottomStripeNode.supernode != nil { + anchorNode = self.bottomStripeNode + } else if self.topStripeNode.supernode != nil { + anchorNode = self.topStripeNode + } else if self.backgroundNode.supernode != nil { + anchorNode = self.backgroundNode + } + if let anchorNode = anchorNode { + self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) + } else { + self.addSubnode(self.highlightedBackgroundNode) + } + } + } else { + if self.highlightedBackgroundNode.supernode != nil { + if animated { + self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() + } + } + }) + self.highlightedBackgroundNode.alpha = 0.0 + } else { + self.highlightedBackgroundNode.removeFromSupernode() + } + } + } + } + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } @@ -420,8 +478,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { } transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: params.leftInset + self.revealOffset + editingOffset + 16.0, y: self.avatarNode.frame.minY), size: self.avatarNode.bounds.size)) - transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: self.revealOffset + params.width - params.rightInset - self.labelNode.bounds.size.width - 15.0, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset + 20.0, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size)) + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size)) transition.updateFrame(node: self.appNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset, y: self.appNode.frame.minY), size: self.appNode.bounds.size)) transition.updateFrame(node: self.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset, y: self.locationNode.frame.minY), size: self.locationNode.bounds.size)) } 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 0605fa5d2d..e683015111 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -19,19 +19,25 @@ private final class RecentSessionsControllerArguments { let removeSession: (Int64) -> Void let terminateOtherSessions: () -> Void + let openSession: (RecentAccountSession) -> Void + let openWebSession: (WebAuthorization) -> Void + let removeWebSession: (Int64) -> Void let terminateAllWebSessions: () -> Void - + let addDevice: () -> Void let openOtherAppsUrl: () -> Void - init(context: AccountContext, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, terminateOtherSessions: @escaping () -> Void, removeWebSession: @escaping (Int64) -> Void, terminateAllWebSessions: @escaping () -> Void, addDevice: @escaping () -> Void, openOtherAppsUrl: @escaping () -> Void) { + init(context: AccountContext, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, terminateOtherSessions: @escaping () -> Void, openSession: @escaping (RecentAccountSession) -> Void, openWebSession: @escaping (WebAuthorization) -> Void, removeWebSession: @escaping (Int64) -> Void, terminateAllWebSessions: @escaping () -> Void, addDevice: @escaping () -> Void, openOtherAppsUrl: @escaping () -> Void) { self.context = context self.setSessionIdWithRevealedOptions = setSessionIdWithRevealedOptions self.removeSession = removeSession self.terminateOtherSessions = terminateOtherSessions + self.openSession = openSession + self.openWebSession = openWebSession + self.removeWebSession = removeWebSession self.terminateAllWebSessions = terminateAllWebSessions @@ -271,6 +277,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry { case let .currentSession(_, _, dateTimeFormat, session): return ItemListRecentSessionItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, session: session, enabled: true, editable: false, editing: false, revealed: false, sectionId: self.section, setSessionIdWithRevealedOptions: { _, _ in }, removeSession: { _ in + }, action: { + }) case let .terminateOtherSessions(theme, text): return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: { @@ -298,6 +306,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry { arguments.setSessionIdWithRevealedOptions(previousId, id) }, removeSession: { id in arguments.removeSession(id) + }, action: { + }) case let .pendingSessionsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) @@ -312,12 +322,16 @@ private enum RecentSessionsEntry: ItemListNodeEntry { arguments.setSessionIdWithRevealedOptions(previousId, id) }, removeSession: { id in arguments.removeSession(id) + }, action: { + arguments.openSession(session) }) case let .website(_, _, _, dateTimeFormat, nameDisplayOrder, website, peer, enabled, editing, revealed): return ItemListWebsiteItem(context: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in arguments.setSessionIdWithRevealedOptions(previousId, id) }, removeSession: { id in arguments.removeWebSession(id) + }, action: { + arguments.openWebSession(website) }) case let .devicesInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { action in @@ -588,6 +602,16 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }, openSession: { session in + let controller = RecentSessionScreen(context: context, subject: .session(session), remove: { + + }) + presentControllerImpl?(controller, nil) + }, openWebSession: { session in + let controller = RecentSessionScreen(context: context, subject: .website(session), remove: { + + }) + presentControllerImpl?(controller, nil) }, removeWebSession: { sessionId in updateState { return $0.withUpdatedRemovingSessionId(sessionId) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift new file mode 100644 index 0000000000..8a2b973fbd --- /dev/null +++ b/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift @@ -0,0 +1,506 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit +import AccountContext +import SolidRoundedButtonNode +import TelegramPresentationData +import TelegramUIPreferences +import TelegramStringFormatting +import PresentationDataUtils +import AnimationUI +import MergeLists +import MediaResources +import StickerResources +import AnimatedStickerNode +import TelegramAnimatedStickerNode + +private func closeButtonImage(theme: PresentationTheme) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(UIColor(rgb: 0x808084, alpha: 0.1).cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(theme.actionSheet.inputClearButtonColor.cgColor) + + context.move(to: CGPoint(x: 10.0, y: 10.0)) + context.addLine(to: CGPoint(x: 20.0, y: 20.0)) + context.strokePath() + + context.move(to: CGPoint(x: 20.0, y: 10.0)) + context.addLine(to: CGPoint(x: 10.0, y: 20.0)) + context.strokePath() + }) +} + +final class RecentSessionScreen: ViewController { + enum Subject { + case session(RecentAccountSession) + case website(WebAuthorization) + } + private var controllerNode: RecentSessionScreenNode { + return self.displayNode as! RecentSessionScreenNode + } + + private var animatedIn = false + + private let context: AccountContext + private let subject: RecentSessionScreen.Subject + private let remove: () -> Void + + private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + + var dismissed: (() -> Void)? + + var passthroughHitTestImpl: ((CGPoint) -> UIView?)? { + didSet { + if self.isNodeLoaded { + self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl + } + } + } + + init(context: AccountContext, subject: RecentSessionScreen.Subject, remove: @escaping () -> Void) { + self.context = context + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.subject = subject + self.remove = remove + + super.init(navigationBarPresentationData: nil) + + self.statusBar.statusBarStyle = .Ignore + + self.blocksBackgroundWhenInOverlay = true + + self.presentationDataDisposable = (context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.presentationData = presentationData + strongSelf.controllerNode.updatePresentationData(presentationData) + } + }) + + self.statusBar.statusBarStyle = .Ignore + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.presentationDataDisposable?.dispose() + } + + override public func loadDisplayNode() { + self.displayNode = RecentSessionScreenNode(context: self.context, presentationData: self.presentationData, controller: self, subject: self.subject) + self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl + self.controllerNode.present = { [weak self] c in + self?.present(c, in: .current) + } + self.controllerNode.dismiss = { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + } + } + + override public func loadView() { + super.loadView() + + self.view.disablesInteractiveTransitionGestureRecognizer = true + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !self.animatedIn { + self.animatedIn = true + self.controllerNode.animateIn() + } + } + + override public func dismiss(completion: (() -> Void)? = nil) { + self.controllerNode.animateOut(completion: completion) + + self.dismissed?() + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + } +} + +private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDelegate { + private let context: AccountContext + private var presentationData: PresentationData + private weak var controller: RecentSessionScreen? + private let subject: RecentSessionScreen.Subject + + private let dimNode: ASDisplayNode + private let wrappingScrollNode: ASScrollNode + private let contentContainerNode: ASDisplayNode + private let topContentContainerNode: SparseNode + private let backgroundNode: ASDisplayNode + private let contentBackgroundNode: ASDisplayNode + private let titleNode: ImmediateTextNode + private let textNode: ImmediateTextNode + private let fieldBackgroundNode: ASDisplayNode + private let deviceTitleNode: ImmediateTextNode + private let deviceValueNode: ImmediateTextNode + private let firstSeparatorNode: ASDisplayNode + private let locationTitleNode: ImmediateTextNode + private let locationValueNode: ImmediateTextNode + private let secondSeparatorNode: ASDisplayNode + private let ipTitleNode: ImmediateTextNode + private let ipValueNode: ImmediateTextNode + + private let cancelButton: HighlightableButtonNode + private let terminateButton: SolidRoundedButtonNode + + private var containerLayout: (ContainerViewLayout, CGFloat)? + + var present: ((ViewController) -> Void)? + var remove: (() -> Void)? + var dismiss: (() -> Void)? + + init(context: AccountContext, presentationData: PresentationData, controller: RecentSessionScreen, subject: RecentSessionScreen.Subject) { + self.context = context + self.controller = controller + self.presentationData = presentationData + self.subject = subject + + self.wrappingScrollNode = ASScrollNode() + self.wrappingScrollNode.view.alwaysBounceVertical = true + self.wrappingScrollNode.view.delaysContentTouches = false + self.wrappingScrollNode.view.canCancelContentTouches = true + + self.dimNode = ASDisplayNode() + self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + + self.contentContainerNode = ASDisplayNode() + self.contentContainerNode.isOpaque = false + + self.topContentContainerNode = SparseNode() + self.topContentContainerNode.isOpaque = false + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.clipsToBounds = true + self.backgroundNode.cornerRadius = 16.0 + + let backgroundColor = self.presentationData.theme.list.blocksBackgroundColor + let textColor = self.presentationData.theme.list.itemPrimaryTextColor + let accentColor = self.presentationData.theme.list.itemAccentColor + let secondaryTextColor = self.presentationData.theme.list.itemSecondaryTextColor + + self.contentBackgroundNode = ASDisplayNode() + self.contentBackgroundNode.backgroundColor = backgroundColor + + self.titleNode = ImmediateTextNode() + self.textNode = ImmediateTextNode() + + self.fieldBackgroundNode = ASDisplayNode() + self.fieldBackgroundNode.clipsToBounds = true + self.fieldBackgroundNode.cornerRadius = 11 + self.fieldBackgroundNode.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor + + self.deviceTitleNode = ImmediateTextNode() + self.deviceValueNode = ImmediateTextNode() + + self.locationTitleNode = ImmediateTextNode() + self.locationValueNode = ImmediateTextNode() + + self.ipTitleNode = ImmediateTextNode() + self.ipValueNode = ImmediateTextNode() + + self.cancelButton = HighlightableButtonNode() + self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) + + self.terminateButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: self.presentationData.theme.list.itemDestructiveColor), font: .regular, height: 44.0, cornerRadius: 11.0, gloss: false) + + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + let title: String + let subtitle: String + let subtitleActive: Bool + let device: String + let location: String + let ip: String + switch subject { + case let .session(session): + self.terminateButton.title = self.presentationData.strings.AuthSessions_View_TerminateSession + title = "\(session.appName) \(session.appVersion)" + if session.isCurrent { + subtitle = presentationData.strings.Presence_online + subtitleActive = true + } else { + subtitle = stringForRelativeActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, relativeTimestamp: session.activityDate, relativeTo: timestamp) + subtitleActive = false + } + var deviceString = "" + if !session.deviceModel.isEmpty { + deviceString = session.deviceModel + } + if !session.platform.isEmpty { + if !deviceString.isEmpty { + deviceString += ", " + } + deviceString += session.platform + } + if !session.systemVersion.isEmpty { + if !deviceString.isEmpty { + deviceString += ", " + } + deviceString += session.systemVersion + } + device = deviceString + location = session.country + ip = session.ip + case let .website(website): + self.terminateButton.title = self.presentationData.strings.AuthSessions_View_Logout + title = website.domain + subtitle = stringForRelativeActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, relativeTimestamp: website.dateActive, relativeTo: timestamp) + subtitleActive = false + + var deviceString = "" + if !website.domain.isEmpty { + deviceString = website.domain + } + if !website.browser.isEmpty { + if !deviceString.isEmpty { + deviceString += ", " + } + deviceString += website.browser + } + if !website.platform.isEmpty { + if !deviceString.isEmpty { + deviceString += ", " + } + deviceString += website.platform + } + device = deviceString + location = website.region + ip = website.ip + } + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(30.0), textColor: textColor) + self.textNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: subtitleActive ? accentColor : secondaryTextColor) + + self.deviceTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_Device, font: Font.regular(17.0), textColor: textColor) + self.deviceValueNode.attributedText = NSAttributedString(string: device, font: Font.regular(17.0), textColor: secondaryTextColor) + + self.firstSeparatorNode = ASDisplayNode() + + self.locationTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_Location, font: Font.regular(17.0), textColor: textColor) + self.locationValueNode.attributedText = NSAttributedString(string: location, font: Font.regular(17.0), textColor: secondaryTextColor) + + self.secondSeparatorNode = ASDisplayNode() + + self.ipTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_IP, font: Font.regular(17.0), textColor: textColor) + self.ipValueNode.attributedText = NSAttributedString(string: ip, font: Font.regular(17.0), textColor: secondaryTextColor) + + super.init() + + self.backgroundColor = nil + self.isOpaque = false + + self.addSubnode(self.dimNode) + + self.wrappingScrollNode.view.delegate = self + self.addSubnode(self.wrappingScrollNode) + + self.wrappingScrollNode.addSubnode(self.backgroundNode) + self.wrappingScrollNode.addSubnode(self.contentContainerNode) + self.wrappingScrollNode.addSubnode(self.topContentContainerNode) + + self.backgroundNode.addSubnode(self.contentBackgroundNode) + self.contentContainerNode.addSubnode(self.titleNode) + self.contentContainerNode.addSubnode(self.textNode) + + self.contentContainerNode.addSubnode(self.fieldBackgroundNode) + + self.contentContainerNode.addSubnode(self.deviceTitleNode) + self.contentContainerNode.addSubnode(self.deviceValueNode) + + self.contentContainerNode.addSubnode(self.locationTitleNode) + self.contentContainerNode.addSubnode(self.locationValueNode) + + self.contentContainerNode.addSubnode(self.ipTitleNode) + self.contentContainerNode.addSubnode(self.ipValueNode) + + self.contentContainerNode.addSubnode(self.terminateButton) + self.topContentContainerNode.addSubnode(self.cancelButton) + + self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) + self.terminateButton.pressed = { [weak self] in + if let strongSelf = self { + strongSelf.terminateButton.isUserInteractionEnabled = false + strongSelf.remove?() + } + } + } + + func updatePresentationData(_ presentationData: PresentationData) { + guard !self.animatedOut else { + return + } + let previousTheme = self.presentationData.theme + self.presentationData = presentationData + + self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.regular(30.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) + self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor) + + self.fieldBackgroundNode.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor + + self.deviceTitleNode.attributedText = NSAttributedString(string: self.deviceTitleNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) + self.locationTitleNode.attributedText = NSAttributedString(string: self.locationTitleNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) + self.ipTitleNode.attributedText = NSAttributedString(string: self.ipTitleNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) + + self.deviceValueNode.attributedText = NSAttributedString(string: self.deviceValueNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor) + self.locationValueNode.attributedText = NSAttributedString(string: self.locationValueNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor) + self.ipValueNode.attributedText = NSAttributedString(string: self.ipValueNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor) + + if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } + + self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) + self.terminateButton.updateTheme(SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: self.presentationData.theme.list.itemDestructiveColor)) + } + + override func didLoad() { + super.didLoad() + + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never + } + + self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture))) + } + + @objc func cancelButtonPressed() { + self.animateOut() + } + + @objc func dimTapGesture() { + self.cancelButtonPressed() + } + + private var animatedOut = false + func animateIn() { + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + + let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY + let dimPosition = self.dimNode.layer.position + + let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) + let targetBounds = self.bounds + self.bounds = self.bounds.offsetBy(dx: 0.0, dy: -offset) + self.dimNode.position = CGPoint(x: dimPosition.x, y: dimPosition.y - offset) + transition.animateView({ + self.bounds = targetBounds + self.dimNode.position = dimPosition + }) + } + + func animateOut(completion: (() -> Void)? = nil) { + self.animatedOut = true + + var dimCompleted = false + var offsetCompleted = false + + let internalCompletion: () -> Void = { [weak self] in + if let strongSelf = self, dimCompleted && offsetCompleted { + strongSelf.dismiss?() + } + completion?() + } + + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + dimCompleted = true + internalCompletion() + }) + + let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY + self.wrappingScrollNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in + offsetCompleted = true + internalCompletion() + }) + } + + var passthroughHitTestImpl: ((CGPoint) -> UIView?)? + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.bounds.contains(point) { + if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) { + return self.dimNode.view + } + } + return super.hitTest(point, with: event) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let contentOffset = scrollView.contentOffset + let additionalTopHeight = max(0.0, -contentOffset.y) + + if additionalTopHeight >= 30.0 { + self.cancelButtonPressed() + } + } + + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + self.containerLayout = (layout, navigationBarHeight) + + var insets = layout.insets(options: [.statusBar, .input]) + let cleanInsets = layout.insets(options: [.statusBar]) + insets.top = max(10.0, insets.top) + + let bottomInset: CGFloat = 10.0 + cleanInsets.bottom + let titleHeight: CGFloat = 54.0 + let contentHeight = titleHeight + bottomInset + 188.0 + + let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left) + + let sideInset = floor((layout.size.width - width) / 2.0) + let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight)) + let contentFrame = contentContainerFrame + + var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0)) + if backgroundFrame.minY < contentFrame.minY { + backgroundFrame.origin.y = contentFrame.minY + } + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) + transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + + let titleSize = self.titleNode.updateLayout(CGSize(width: width - 90.0, height: titleHeight)) + let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 120.0), size: titleSize) + transition.updateFrame(node: self.titleNode, frame: titleFrame) + + let textSize = self.textNode.updateLayout(CGSize(width: width - 90.0, height: titleHeight)) + let textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: titleFrame.maxY), size: textSize) + transition.updateFrame(node: self.textNode, frame: textFrame) + + let cancelSize = CGSize(width: 44.0, height: 44.0) + let cancelFrame = CGRect(origin: CGPoint(x: contentFrame.width - cancelSize.width - 3.0, y: 6.0), size: cancelSize) + transition.updateFrame(node: self.cancelButton, frame: cancelFrame) + + let buttonInset: CGFloat = 16.0 + let doneButtonHeight = self.terminateButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) + transition.updateFrame(node: self.terminateButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - insets.bottom - 6.0, width: contentFrame.width, height: doneButtonHeight)) + + transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame) + transition.updateFrame(node: self.topContentContainerNode, frame: contentContainerFrame) + + var listInsets = UIEdgeInsets() + listInsets.top += layout.safeInsets.left + 12.0 + listInsets.bottom += layout.safeInsets.right + 12.0 + + + } +} diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index f36710dc87..1a3fcecc51 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -1012,7 +1012,9 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme colorItemNode?.prepareCrossfadeTransition() } - let crossfadeController = ThemeSettingsCrossfadeController(view: view, topOffset: topOffset, bottomOffset: bottomOffset, leftOffset: leftOffset) + let sectionInset = max(16.0, floor((controller.displayNode.frame.width - 674.0) / 2.0)) + + let crossfadeController = ThemeSettingsCrossfadeController(view: view, topOffset: topOffset, bottomOffset: bottomOffset, leftOffset: leftOffset, sideInset: sectionInset) crossfadeController.didAppear = { [weak themeItemNode, weak colorItemNode] in if view != nil { themeItemNode?.animateCrossfadeTransition() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index d7f47f5138..e16ee6dc8f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -446,7 +446,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var selectThemeImpl: ((PresentationThemeReference) -> Void)? var selectAccentColorImpl: ((PresentationThemeAccentColor?) -> Void)? var openAccentColorPickerImpl: ((PresentationThemeReference, Bool) -> Void)? - var moreImpl: (() -> Void)? let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start() @@ -973,10 +972,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The themeReference = settings.theme } - let rightNavigationButton = ItemListNavigationButton(content: .icon(.add), style: .regular, enabled: true, action: { - moreImpl?() - }) - var defaultThemes: [PresentationThemeReference] = [] if presentationData.autoNightModeTriggered { defaultThemes.append(contentsOf: [.builtin(.nightAccent), .builtin(.night)]) @@ -1000,7 +995,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var chatThemes = cloudThemes.filter { $0.emoticon != nil } chatThemes.insert(.builtin(.dayClassic), at: 0) - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName, chatThemes: chatThemes, animatedEmojiStickers: animatedEmojiStickers), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) return (controllerState, (listState, arguments)) @@ -1072,7 +1067,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The themeItemNode?.prepareCrossfadeTransition() } - let crossfadeController = ThemeSettingsCrossfadeController(view: view, topOffset: topOffset, bottomOffset: bottomOffset, leftOffset: leftOffset) + let sectionInset = max(16.0, floor((controller.displayNode.frame.width - 674.0) / 2.0)) + + let crossfadeController = ThemeSettingsCrossfadeController(view: view, topOffset: topOffset, bottomOffset: bottomOffset, leftOffset: leftOffset, sideInset: sectionInset) crossfadeController.didAppear = { [weak themeItemNode] in if view != nil { themeItemNode?.animateCrossfadeTransition() @@ -1243,42 +1240,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The presentCrossfadeControllerImpl?(true) }) } - moreImpl = { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let actionSheet = ActionSheetController(presentationData: presentationData) - var items: [ActionSheetItem] = [] - items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_CreateTheme, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - let _ = (context.sharedContext.accountManager.transaction { transaction -> PresentationThemeReference in - let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings)?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings - - let themeReference: PresentationThemeReference - let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered - if autoNightModeTriggered { - themeReference = settings.automaticThemeSwitchSetting.theme - } else { - themeReference = settings.theme - } - - return themeReference - } - |> deliverOnMainQueue).start(next: { themeReference in - let controller = editThemeController(context: context, mode: .create(nil, nil), navigateToChat: { peerId in - if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) - } - }) - pushControllerImpl?(controller) - }) - })) - 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) - } return controller } @@ -1289,9 +1250,12 @@ public final class ThemeSettingsCrossfadeController: ViewController { private var bottomSnapshotView: UIView? private var sideSnapshotView: UIView? + private var leftSnapshotView: UIView? + private var rightSnapshotView: UIView? + var didAppear: (() -> Void)? - public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil) { + public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil, sideInset: CGFloat = 0.0) { if let view = view { if var leftOffset = leftOffset { leftOffset += UIScreenPixel @@ -1322,6 +1286,58 @@ public final class ThemeSettingsCrossfadeController: ViewController { } } + if sideInset > 0.0 { + if let view = view.snapshotView(afterScreenUpdates: false) { + let clipView = UIView() + clipView.clipsToBounds = true + clipView.addSubview(view) + + view.clipsToBounds = true + view.contentMode = .topLeft + + if let topOffset = topOffset, let bottomOffset = bottomOffset { + var frame = view.frame + frame.origin.y = topOffset + frame.size.width = sideInset + frame.size.height = bottomOffset - topOffset + clipView.frame = frame + + frame = view.frame + frame.origin.y = -topOffset + frame.size.width = sideInset + frame.size.height = bottomOffset + view.frame = frame + } + + self.leftSnapshotView = clipView + } + if let view = view.snapshotView(afterScreenUpdates: false) { + let clipView = UIView() + clipView.clipsToBounds = true + clipView.addSubview(view) + + view.clipsToBounds = true + view.contentMode = .topRight + + if let topOffset = topOffset, let bottomOffset = bottomOffset { + var frame = view.frame + frame.origin.x = frame.width - sideInset + frame.origin.y = topOffset + frame.size.width = sideInset + frame.size.height = bottomOffset - topOffset + clipView.frame = frame + + frame = view.frame + frame.origin.y = -topOffset + frame.size.width = sideInset + frame.size.height = bottomOffset + view.frame = frame + } + + self.rightSnapshotView = clipView + } + } + if let view = view.snapshotView(afterScreenUpdates: false) { view.clipsToBounds = true view.contentMode = .top @@ -1373,7 +1389,13 @@ public final class ThemeSettingsCrossfadeController: ViewController { } if let sideSnapshotView = self.sideSnapshotView { self.displayNode.view.addSubview(sideSnapshotView) - } + } + if let leftSnapshotView = self.leftSnapshotView { + self.displayNode.view.addSubview(leftSnapshotView) + } + if let rightSnapshotView = self.rightSnapshotView { + self.displayNode.view.addSubview(rightSnapshotView) + } } override public func viewDidAppear(_ animated: Bool) { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 9264625fc5..bcf2dba434 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) } dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } - dict[-2097579871] = { return Api.ChatFull.parse_chatFull($0) } - dict[1084166537] = { return Api.ChatFull.parse_channelFull($0) } + dict[1185349556] = { return Api.ChatFull.parse_chatFull($0) } + dict[1506802019] = { return Api.ChatFull.parse_channelFull($0) } dict[-591909213] = { return Api.PollResults.parse_pollResults($0) } dict[-1070776313] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-462696732] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } @@ -286,7 +286,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-997782967] = { return Api.Update.parse_updateBotStopped($0) } dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) } dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) } - dict[-82532135] = { return Api.Update.parse_updatePendingJoinRequests($0) } + dict[1885586395] = { return Api.Update.parse_updatePendingJoinRequests($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-592373577] = { return Api.GroupCallParticipantVideoSourceGroup.parse_groupCallParticipantVideoSourceGroup($0) } @@ -336,6 +336,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-842824308] = { return Api.account.WallPapers.parse_wallPapers($0) } dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) } dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) } + dict[2014782332] = { return Api.messages.SearchResultsRawMessages.parse_searchResultsRawMessages($0) } dict[-2032041631] = { return Api.Poll.parse_poll($0) } dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) } dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) } @@ -785,7 +786,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) } dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) } dict[750063767] = { return Api.messages.FavedStickers.parse_favedStickers($0) } - dict[-1283792928] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } + dict[179611673] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) } dict[-1392388579] = { return Api.Authorization.parse_authorization($0) } dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) } @@ -836,6 +837,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1781355374] = { return Api.MessageAction.parse_messageActionChannelCreate($0) } dict[-519864430] = { return Api.MessageAction.parse_messageActionChatMigrateTo($0) } dict[-365344535] = { return Api.MessageAction.parse_messageActionChannelMigrateFrom($0) } + dict[-339958837] = { return Api.MessageAction.parse_messageActionChatJoinedByRequest($0) } dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) } dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) } dict[-1834538890] = { return Api.MessageAction.parse_messageActionGameScore($0) } @@ -854,7 +856,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1441072131] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) } dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) } dict[-1434950843] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) } - dict[-339958837] = { return Api.MessageAction.parse_messageActionChatJoinedByRequest($0) } dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) } dict[-987599081] = { return Api.PhoneCall.parse_phoneCallWaiting($0) } dict[347139340] = { return Api.PhoneCall.parse_phoneCallRequested($0) } @@ -1150,6 +1151,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.InputTheme: _1.serialize(buffer, boxed) + case let _1 as Api.messages.SearchResultsRawMessages: + _1.serialize(buffer, boxed) case let _1 as Api.Poll: _1.serialize(buffer, boxed) case let _1 as Api.InputNotifyPeer: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 1097f60518..9659340ead 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -477,6 +477,56 @@ public struct messages { } } + } + public enum SearchResultsRawMessages: TypeConstructorDescription { + case searchResultsRawMessages(msgIds: [Int32], msgDates: [Int32]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .searchResultsRawMessages(let msgIds, let msgDates): + if boxed { + buffer.appendInt32(2014782332) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(msgIds.count)) + for item in msgIds { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(msgDates.count)) + for item in msgDates { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .searchResultsRawMessages(let msgIds, let msgDates): + return ("searchResultsRawMessages", [("msgIds", msgIds), ("msgDates", msgDates)]) + } + } + + public static func parse_searchResultsRawMessages(_ reader: BufferReader) -> SearchResultsRawMessages? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SearchResultsRawMessages.searchResultsRawMessages(msgIds: _1!, msgDates: _2!) + } + else { + return nil + } + } + } public enum ExportedChatInvites: TypeConstructorDescription { case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index d6d5e6fa46..bc73a326cf 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -160,14 +160,14 @@ public extension Api { } public enum ChatFull: TypeConstructorDescription { - case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?) - case channelFull(flags: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?) + case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?) + case channelFull(flags: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending): + case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters): if boxed { - buffer.appendInt32(-2097579871) + buffer.appendInt32(1185349556) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -188,10 +188,15 @@ public extension Api { if Int(flags) & Int(1 << 15) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)} if Int(flags) & Int(1 << 16) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 17) != 0 {serializeInt32(requestsPending!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 17) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentRequesters!.count)) + for item in recentRequesters! { + serializeInt64(item, buffer: buffer, boxed: false) + }} break - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending): + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters): if boxed { - buffer.appendInt32(1084166537) + buffer.appendInt32(1506802019) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -234,16 +239,21 @@ public extension Api { if Int(flags) & Int(1 << 26) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)} if Int(flags) & Int(1 << 27) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 28) != 0 {serializeInt32(requestsPending!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 28) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentRequesters!.count)) + for item in recentRequesters! { + serializeInt64(item, buffer: buffer, boxed: false) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending): - return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending)]) - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending): - return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending)]) + case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters): + return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending), ("recentRequesters", recentRequesters)]) + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters): + return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending), ("recentRequesters", recentRequesters)]) } } @@ -292,6 +302,10 @@ public extension Api { if Int(_1!) & Int(1 << 16) != 0 {_14 = parseString(reader) } var _15: Int32? if Int(_1!) & Int(1 << 17) != 0 {_15 = reader.readInt32() } + var _16: [Int64]? + if Int(_1!) & Int(1 << 17) != 0 {if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -307,8 +321,9 @@ public extension Api { let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 16) == 0) || _14 != nil let _c15 = (Int(_1!) & Int(1 << 17) == 0) || _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14, requestsPending: _15) + let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { + return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14, requestsPending: _15, recentRequesters: _16) } else { return nil @@ -399,6 +414,10 @@ public extension Api { if Int(_1!) & Int(1 << 27) != 0 {_32 = parseString(reader) } var _33: Int32? if Int(_1!) & Int(1 << 28) != 0 {_33 = reader.readInt32() } + var _34: [Int64]? + if Int(_1!) & Int(1 << 28) != 0 {if let _ = reader.readInt32() { + _34 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -432,8 +451,9 @@ public extension Api { let _c31 = (Int(_1!) & Int(1 << 26) == 0) || _31 != nil let _c32 = (Int(_1!) & Int(1 << 27) == 0) || _32 != nil let _c33 = (Int(_1!) & Int(1 << 28) == 0) || _33 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 { - return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30, groupcallDefaultJoinAs: _31, themeEmoticon: _32, requestsPending: _33) + let _c34 = (Int(_1!) & Int(1 << 28) == 0) || _34 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 { + return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30, groupcallDefaultJoinAs: _31, themeEmoticon: _32, requestsPending: _33, recentRequesters: _34) } else { return nil @@ -4827,7 +4847,7 @@ public extension Api { case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) case updateGroupCallConnection(flags: Int32, params: Api.DataJSON) case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand]) - case updatePendingJoinRequests(peer: Api.Peer, requestsPending: Int32) + case updatePendingJoinRequests(peer: Api.Peer, requestsPending: Int32, recentRequesters: [Int64]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -5642,12 +5662,17 @@ public extension Api { item.serialize(buffer, true) } break - case .updatePendingJoinRequests(let peer, let requestsPending): + case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): if boxed { - buffer.appendInt32(-82532135) + buffer.appendInt32(1885586395) } peer.serialize(buffer, true) serializeInt32(requestsPending, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentRequesters.count)) + for item in recentRequesters { + serializeInt64(item, buffer: buffer, boxed: false) + } break } } @@ -5840,8 +5865,8 @@ public extension Api { return ("updateGroupCallConnection", [("flags", flags), ("params", params)]) case .updateBotCommands(let peer, let botId, let commands): return ("updateBotCommands", [("peer", peer), ("botId", botId), ("commands", commands)]) - case .updatePendingJoinRequests(let peer, let requestsPending): - return ("updatePendingJoinRequests", [("peer", peer), ("requestsPending", requestsPending)]) + case .updatePendingJoinRequests(let peer, let requestsPending, let recentRequesters): + return ("updatePendingJoinRequests", [("peer", peer), ("requestsPending", requestsPending), ("recentRequesters", recentRequesters)]) } } @@ -7508,10 +7533,15 @@ public extension Api { } var _2: Int32? _2 = reader.readInt32() + var _3: [Int64]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!, recentRequesters: _3!) } else { return nil @@ -20176,13 +20206,13 @@ public extension Api { } public enum ExportedChatInvite: TypeConstructorDescription { - case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?, requested: Int32?) + case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?, requested: Int32?, title: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested): + case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let title): if boxed { - buffer.appendInt32(-1283792928) + buffer.appendInt32(179611673) } serializeInt32(flags, buffer: buffer, boxed: false) serializeString(link, buffer: buffer, boxed: false) @@ -20193,14 +20223,15 @@ public extension Api { if Int(flags) & Int(1 << 2) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(usage!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 7) != 0 {serializeInt32(requested!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(title!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested): - return ("chatInviteExported", [("flags", flags), ("link", link), ("adminId", adminId), ("date", date), ("startDate", startDate), ("expireDate", expireDate), ("usageLimit", usageLimit), ("usage", usage), ("requested", requested)]) + case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let title): + return ("chatInviteExported", [("flags", flags), ("link", link), ("adminId", adminId), ("date", date), ("startDate", startDate), ("expireDate", expireDate), ("usageLimit", usageLimit), ("usage", usage), ("requested", requested), ("title", title)]) } } @@ -20223,6 +20254,8 @@ public extension Api { if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt32() } var _9: Int32? if Int(_1!) & Int(1 << 7) != 0 {_9 = reader.readInt32() } + var _10: String? + if Int(_1!) & Int(1 << 8) != 0 {_10 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -20232,8 +20265,9 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 7) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9) + let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9, title: _10) } else { return nil @@ -21089,6 +21123,7 @@ public extension Api { case messageActionChannelCreate(title: String) case messageActionChatMigrateTo(channelId: Int64) case messageActionChannelMigrateFrom(title: String, chatId: Int64) + case messageActionChatJoinedByRequest case messageActionPinMessage case messageActionHistoryClear case messageActionGameScore(gameId: Int64, score: Int32) @@ -21107,7 +21142,6 @@ public extension Api { case messageActionSetMessagesTTL(period: Int32) case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32) case messageActionSetChatTheme(emoticon: String) - case messageActionChatJoinedByRequest public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -21186,6 +21220,12 @@ public extension Api { } serializeString(title, buffer: buffer, boxed: false) serializeInt64(chatId, buffer: buffer, boxed: false) + break + case .messageActionChatJoinedByRequest: + if boxed { + buffer.appendInt32(-339958837) + } + break case .messageActionPinMessage: if boxed { @@ -21324,12 +21364,6 @@ public extension Api { buffer.appendInt32(-1434950843) } serializeString(emoticon, buffer: buffer, boxed: false) - break - case .messageActionChatJoinedByRequest: - if boxed { - buffer.appendInt32(-339958837) - } - break } } @@ -21358,6 +21392,8 @@ public extension Api { return ("messageActionChatMigrateTo", [("channelId", channelId)]) case .messageActionChannelMigrateFrom(let title, let chatId): return ("messageActionChannelMigrateFrom", [("title", title), ("chatId", chatId)]) + case .messageActionChatJoinedByRequest: + return ("messageActionChatJoinedByRequest", []) case .messageActionPinMessage: return ("messageActionPinMessage", []) case .messageActionHistoryClear: @@ -21394,8 +21430,6 @@ public extension Api { return ("messageActionGroupCallScheduled", [("call", call), ("scheduleDate", scheduleDate)]) case .messageActionSetChatTheme(let emoticon): return ("messageActionSetChatTheme", [("emoticon", emoticon)]) - case .messageActionChatJoinedByRequest: - return ("messageActionChatJoinedByRequest", []) } } @@ -21516,6 +21550,9 @@ public extension Api { return nil } } + public static func parse_messageActionChatJoinedByRequest(_ reader: BufferReader) -> MessageAction? { + return Api.MessageAction.messageActionChatJoinedByRequest + } public static func parse_messageActionPinMessage(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionPinMessage } @@ -21760,9 +21797,6 @@ public extension Api { return nil } } - public static func parse_messageActionChatJoinedByRequest(_ reader: BufferReader) -> MessageAction? { - return Api.MessageAction.messageActionChatJoinedByRequest - } } public enum PhoneCall: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 38cddaeac5..ca8a0fabc1 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -2652,23 +2652,6 @@ public extension Api { }) } - public static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(347716823) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", flags), ("peer", peer), ("expireDate", expireDate), ("usageLimit", usageLimit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - return result - }) - } - public static func checkChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(1051570619) @@ -4254,16 +4237,17 @@ public extension Api { }) } - public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Api.Bool?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1557932235) + buffer.appendInt32(-1110823051) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) serializeString(link, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {requestNeeded!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", flags), ("peer", peer), ("link", link), ("expireDate", expireDate), ("usageLimit", usageLimit), ("requestNeeded", requestNeeded)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in + if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", flags), ("peer", peer), ("link", link), ("expireDate", expireDate), ("usageLimit", usageLimit), ("requestNeeded", requestNeeded), ("title", title)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in let reader = BufferReader(buffer) var result: Api.messages.ExportedChatInvite? if let signature = reader.readInt32() { @@ -4273,6 +4257,24 @@ public extension Api { }) } + public static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1607670315) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", flags), ("peer", peer), ("expireDate", expireDate), ("usageLimit", usageLimit), ("title", title)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in + let reader = BufferReader(buffer) + var result: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + return result + }) + } + public static func deleteRevokedExportedChatInvites(peer: Api.InputPeer, adminId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(1452833749) @@ -4413,18 +4415,18 @@ public extension Api { }) } - public static func getSearchResultsPositions(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func getSearchResultsRawMessages(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1855292323) + buffer.appendInt32(1258852762) peer.serialize(buffer, true) filter.serialize(buffer, true) serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("peer", peer), ("filter", filter), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in + serializeInt32(offsetDate, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSearchResultsRawMessages", parameters: [("peer", peer), ("filter", filter), ("offsetId", offsetId), ("offsetDate", offsetDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsRawMessages? in let reader = BufferReader(buffer) - var result: Api.messages.SearchResultsPositions? + var result: Api.messages.SearchResultsRawMessages? if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsPositions + result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsRawMessages } return result }) @@ -4445,6 +4447,23 @@ public extension Api { return result }) } + + public static func getSearchResultsPositions(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1855292323) + peer.serialize(buffer, true) + filter.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("peer", peer), ("filter", filter), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in + let reader = BufferReader(buffer) + var result: Api.messages.SearchResultsPositions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsPositions + } + return result + }) + } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -7542,12 +7561,14 @@ public extension Api { }) } - public static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func installTheme(flags: Int32, theme: Api.InputTheme?, format: String?, baseTheme: Api.BaseTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-229175188) - theme.serialize(buffer, true) - unsave.serialize(buffer, true) - return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", theme), ("unsave", unsave)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + buffer.appendInt32(-953697477) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {theme!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(format!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {baseTheme!.serialize(buffer, true)} + return (FunctionDescription(name: "account.installTheme", parameters: [("flags", flags), ("theme", theme), ("format", format), ("baseTheme", baseTheme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in let reader = BufferReader(buffer) var result: Api.Bool? if let signature = reader.readInt32() { @@ -7557,14 +7578,26 @@ public extension Api { }) } - public static func installTheme(flags: Int32, theme: Api.InputTheme?, format: String?, baseTheme: Api.BaseTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func getChatThemes(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-953697477) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {theme!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(format!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {baseTheme!.serialize(buffer, true)} - return (FunctionDescription(name: "account.installTheme", parameters: [("flags", flags), ("theme", theme), ("format", format), ("baseTheme", baseTheme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + buffer.appendInt32(-700916087) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getChatThemes", parameters: [("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in + let reader = BufferReader(buffer) + var result: Api.account.Themes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Themes + } + return result + }) + } + + public static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-229175188) + theme.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", theme), ("unsave", unsave)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in let reader = BufferReader(buffer) var result: Api.Bool? if let signature = reader.readInt32() { @@ -7723,20 +7756,6 @@ public extension Api { return result }) } - - public static func getChatThemes(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-700916087) - serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getChatThemes", parameters: [("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in - let reader = BufferReader(buffer) - var result: Api.account.Themes? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.account.Themes - } - return result - }) - } } public struct langpack { public static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index 952e316641..fdb6c021fb 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -520,7 +520,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.height / 2.0, width: targetRect.width, height: targetRect.height) avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.height / 2.0), size: CGSize()) avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0) - avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) + avatarListNode.topShadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) avatarListContainerNode.addSubnode(avatarListNode) avatarListContainerNode.addSubnode(avatarListNode.controlsClippingOffsetNode) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift index 5146e2c53f..859b8302cc 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift @@ -265,7 +265,7 @@ final class VoiceChatPeerProfileNode: ASDisplayNode { self.avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.width / 2.0, width: targetRect.width, height: targetRect.width) self.avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.width / 2.0), size: CGSize()) self.avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0) - self.avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) + self.avatarListNode.topShadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) self.avatarListNode.updateCustomItemsOnlySynchronously = true self.avatarListNode.update(size: targetSize, peer: self.peer, customNode: self.customNode, additionalEntry: self.additionalEntry, isExpanded: true, transition: .immediate) @@ -359,7 +359,7 @@ final class VoiceChatPeerProfileNode: ASDisplayNode { self.avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.width / 2.0, width: targetRect.width, height: targetRect.width) self.avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.width / 2.0), size: CGSize()) self.avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0) - self.avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) + self.avatarListNode.topShadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0) self.avatarListNode.updateCustomItemsOnlySynchronously = true self.avatarListNode.update(size: targetSize, peer: self.peer, customNode: self.customNode, additionalEntry: self.additionalEntry, isExpanded: true, transition: .immediate) @@ -432,8 +432,8 @@ final class VoiceChatPeerProfileNode: ASDisplayNode { self.avatarListNode.stripContainerNode.alpha = 0.0 self.avatarListNode.stripContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - self.avatarListNode.shadowNode.alpha = 0.0 - self.avatarListNode.shadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + self.avatarListNode.topShadowNode.alpha = 0.0 + self.avatarListNode.topShadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.infoNode.alpha = 0.0 self.infoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) @@ -492,8 +492,8 @@ final class VoiceChatPeerProfileNode: ASDisplayNode { self.avatarListNode.stripContainerNode.alpha = 0.0 self.avatarListNode.stripContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - self.avatarListNode.shadowNode.alpha = 0.0 - self.avatarListNode.shadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + self.avatarListNode.topShadowNode.alpha = 0.0 + self.avatarListNode.topShadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.infoNode.alpha = 0.0 self.infoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) diff --git a/submodules/TelegramCore/Sources/ApiUtils/ExportedInvitation.swift b/submodules/TelegramCore/Sources/ApiUtils/ExportedInvitation.swift index 448c5f9469..b458031d8a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ExportedInvitation.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ExportedInvitation.swift @@ -6,8 +6,8 @@ import TelegramApi extension ExportedInvitation { init(apiExportedInvite: Api.ExportedChatInvite) { switch apiExportedInvite { - case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage, requested): - self = ExportedInvitation(link: link, isPermanent: (flags & (1 << 5)) != 0, requestApproval: (flags & (1 << 6)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), date: date, startDate: startDate, expireDate: expireDate, usageLimit: usageLimit, count: usage, requestedCount: requested) + case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage, requested, title): + self = ExportedInvitation(link: link, title: title, isPermanent: (flags & (1 << 5)) != 0, requestApproval: (flags & (1 << 6)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), date: date, startDate: startDate, expireDate: expireDate, usageLimit: usageLimit, count: usage, requestedCount: requested) } } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 7c20a72a05..9d600828e6 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1451,7 +1451,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } return current }) - case let .updatePendingJoinRequests(peer, requestsPending): + case let .updatePendingJoinRequests(peer, requestsPending, _): updatedState.updateCachedPeerData(peer.peerId, { current in if peer.peerId.namespace == Namespaces.Peer.CloudGroup { let previous: CachedGroupData diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ExportedInvitation.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ExportedInvitation.swift index 1889aa1e77..13c68ad3d0 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ExportedInvitation.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ExportedInvitation.swift @@ -2,6 +2,7 @@ import Postbox public struct ExportedInvitation: Codable, Equatable { public let link: String + public let title: String? public let isPermanent: Bool public let requestApproval: Bool public let isRevoked: Bool @@ -13,8 +14,9 @@ public struct ExportedInvitation: Codable, Equatable { public let count: Int32? public let requestedCount: Int32? - public init(link: String, isPermanent: Bool, requestApproval: Bool, isRevoked: Bool, adminId: PeerId, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, count: Int32?, requestedCount: Int32?) { + public init(link: String, title: String?, isPermanent: Bool, requestApproval: Bool, isRevoked: Bool, adminId: PeerId, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, count: Int32?, requestedCount: Int32?) { self.link = link + self.title = title self.isPermanent = isPermanent self.requestApproval = requestApproval self.isRevoked = isRevoked @@ -31,6 +33,7 @@ public struct ExportedInvitation: Codable, Equatable { let container = try decoder.container(keyedBy: StringCodingKey.self) self.link = try container.decode(String.self, forKey: "l") + self.title = try container.decodeIfPresent(String.self, forKey: "title") self.isPermanent = try container.decode(Bool.self, forKey: "permanent") self.requestApproval = try container.decodeIfPresent(Bool.self, forKey: "requestApproval") ?? false self.isRevoked = try container.decode(Bool.self, forKey: "revoked") @@ -47,6 +50,7 @@ public struct ExportedInvitation: Codable, Equatable { var container = encoder.container(keyedBy: StringCodingKey.self) try container.encode(self.link, forKey: "l") + try container.encodeIfPresent(self.title, forKey: "title") try container.encode(self.isPermanent, forKey: "permanent") try container.encode(self.requestApproval, forKey: "requestApproval") try container.encode(self.isRevoked, forKey: "revoked") @@ -60,10 +64,10 @@ public struct ExportedInvitation: Codable, Equatable { } public static func ==(lhs: ExportedInvitation, rhs: ExportedInvitation) -> Bool { - return lhs.link == rhs.link && lhs.isPermanent == rhs.isPermanent && lhs.requestApproval == rhs.requestApproval && lhs.isRevoked == rhs.isRevoked && lhs.adminId == rhs.adminId && lhs.date == rhs.date && lhs.startDate == rhs.startDate && lhs.expireDate == rhs.expireDate && lhs.usageLimit == rhs.usageLimit && lhs.count == rhs.count && lhs.requestedCount == rhs.requestedCount + return lhs.link == rhs.link && lhs.title == rhs.title && lhs.isPermanent == rhs.isPermanent && lhs.requestApproval == rhs.requestApproval && lhs.isRevoked == rhs.isRevoked && lhs.adminId == rhs.adminId && lhs.date == rhs.date && lhs.startDate == rhs.startDate && lhs.expireDate == rhs.expireDate && lhs.usageLimit == rhs.usageLimit && lhs.count == rhs.count && lhs.requestedCount == rhs.requestedCount } public func withUpdated(isRevoked: Bool) -> ExportedInvitation { - return ExportedInvitation(link: self.link, isPermanent: self.isPermanent, requestApproval: self.requestApproval, isRevoked: isRevoked, adminId: self.adminId, date: self.date, startDate: self.startDate, expireDate: self.expireDate, usageLimit: self.usageLimit, count: self.count, requestedCount: self.requestedCount) + return ExportedInvitation(link: self.link, title: self.title, isPermanent: self.isPermanent, requestApproval: self.requestApproval, isRevoked: isRevoked, adminId: self.adminId, date: self.date, startDate: self.startDate, expireDate: self.expireDate, usageLimit: self.usageLimit, count: self.count, requestedCount: self.requestedCount) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift index 3c527902b7..904825b561 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift @@ -25,7 +25,7 @@ func _internal_revokePersistentPeerExportedInvitation(account: Account, peerId: if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { let flags: Int32 = (1 << 2) if let _ = peer as? TelegramChannel { - return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil)) + return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil, title: nil)) |> retryRequest |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> ExportedInvitation? in @@ -42,7 +42,7 @@ func _internal_revokePersistentPeerExportedInvitation(account: Account, peerId: } } } else if let _ = peer as? TelegramGroup { - return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil)) + return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil, title: nil)) |> retryRequest |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> ExportedInvitation? in @@ -71,7 +71,7 @@ public enum CreatePeerExportedInvitationError { case generic } -func _internal_createPeerExportedInvitation(account: Account, peerId: PeerId, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { +func _internal_createPeerExportedInvitation(account: Account, peerId: PeerId, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 @@ -84,7 +84,10 @@ func _internal_createPeerExportedInvitation(account: Account, peerId: PeerId, ex if let requestNeeded = requestNeeded, requestNeeded { flags |= (1 << 3) } - return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: expireDate, usageLimit: usageLimit)) + if let _ = title { + flags |= (1 << 4) + } + return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: expireDate, usageLimit: usageLimit, title: title)) |> mapError { _ in return CreatePeerExportedInvitationError.generic } |> map { result -> ExportedInvitation? in return ExportedInvitation(apiExportedInvite: result) @@ -101,7 +104,7 @@ public enum EditPeerExportedInvitationError { case generic } -func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { +func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link: String, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 @@ -114,7 +117,10 @@ func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link if let _ = requestNeeded { flags |= (1 << 3) } - return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded.flatMap { $0 ? .boolTrue : .boolFalse })) + if let _ = title { + flags |= (1 << 4) + } + return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded.flatMap { $0 ? .boolTrue : .boolFalse }, title: title)) |> mapError { _ in return EditPeerExportedInvitationError.generic } |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction in @@ -154,7 +160,7 @@ func _internal_revokePeerExportedInvitation(account: Account, peerId: PeerId, li return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { let flags: Int32 = (1 << 2) - return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil, requestNeeded: nil)) + return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil, requestNeeded: nil, title: nil)) |> mapError { _ in return RevokePeerExportedInvitationError.generic } |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 0e32199fee..b528147794 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -419,12 +419,12 @@ public extension TelegramEngine { return _internal_checkPeerChatServiceActions(postbox: self.account.postbox, peerId: peerId) } - public func createPeerExportedInvitation(peerId: PeerId, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { - return _internal_createPeerExportedInvitation(account: self.account, peerId: peerId, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded) + public func createPeerExportedInvitation(peerId: PeerId, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { + return _internal_createPeerExportedInvitation(account: self.account, peerId: peerId, title: title, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded) } - public func editPeerExportedInvitation(peerId: PeerId, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { - return _internal_editPeerExportedInvitation(account: self.account, peerId: peerId, link: link, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded) + public func editPeerExportedInvitation(peerId: PeerId, link: String, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { + return _internal_editPeerExportedInvitation(account: self.account, peerId: peerId, link: link, title: title, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded) } public func revokePeerExportedInvitation(peerId: PeerId, link: String) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 4dd91d7c29..3d40f1d1b7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -222,14 +222,14 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch result { case let .chatFull(fullChat, chats, users): switch fullChat { - case let .chatFull(_, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _): + case let .chatFull(_, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _): transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .channelFull: break } switch fullChat { - case let .chatFull(chatFullFlags, _, chatFullAbout, chatFullParticipants, chatFullChatPhoto, _, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, _, chatFullCall, _, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon, chatFullRequestsPending): + case let .chatFull(chatFullFlags, _, chatFullAbout, chatFullParticipants, chatFullChatPhoto, _, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, _, chatFullCall, _, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon, chatFullRequestsPending, _): var botInfos: [CachedPeerBotInfo] = [] for botInfo in chatFullBotInfo ?? [] { switch botInfo { @@ -351,14 +351,14 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch result { case let .chatFull(fullChat, chats, users): switch fullChat { - case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .chatFull: break } switch fullChat { - case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, requestsPending): + case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, requestsPending, _): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { channelFlags.insert(.canDisplayParticipants) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 9b43eb3083..9c938dc31b 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -63,6 +63,7 @@ public enum PresentationResourceKey: Int32 { case itemListBlockAccentIcon case itemListBlockDestructiveIcon case itemListAddDeviceIcon + case itemListResetIcon case itemListVoiceCallIcon case itemListVideoCallIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 324403df56..21c3391e22 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -216,6 +216,12 @@ public struct PresentationResourcesItemList { }) } + public static func resetIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.itemListEditThemeIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Settings/Reset"), color: theme.list.itemAccentColor) + }) + } + public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? { if !top && !bottom { return nil diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Contents.json new file mode 100644 index 0000000000..3181ca1086 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon-36.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Icon-36.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Icon-36.pdf new file mode 100644 index 0000000000..b4a403a5d1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Icon-36.pdf @@ -0,0 +1,88 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 5.334961 5.270020 cm +0.000000 0.478431 1.000000 scn +6.194835 19.200245 m +5.935136 18.940548 5.935136 18.519493 6.194835 18.259792 c +9.694835 14.759793 l +9.954534 14.500095 10.375588 14.500095 10.635287 14.759793 c +10.894985 15.019492 10.894985 15.440547 10.635287 15.700245 c +8.370360 17.965172 l +8.791913 18.030869 9.224258 18.065020 9.665000 18.065020 c +14.268293 18.065020 18.000000 14.333313 18.000000 9.730020 c +18.000000 5.126726 14.268293 1.395020 9.665000 1.395020 c +5.061707 1.395020 1.330000 5.126726 1.330000 9.730020 c +1.330000 11.844328 2.116372 13.773348 3.413577 15.243072 c +3.656611 15.518428 3.630409 15.938666 3.355053 16.181702 c +3.079696 16.424736 2.659458 16.398533 2.416424 16.123177 c +0.912993 14.419802 0.000000 12.180721 0.000000 9.730020 c +0.000000 4.392187 4.327168 0.065018 9.665000 0.065018 c +15.002832 0.065018 19.330002 4.392187 19.330002 9.730020 c +19.330002 15.067852 15.002832 19.395020 9.665000 19.395020 c +9.150949 19.395020 8.645859 19.354828 8.152806 19.277313 c +10.635287 21.759794 l +10.894985 22.019493 10.894985 22.440548 10.635287 22.700245 c +10.375588 22.959944 9.954534 22.959944 9.694835 22.700245 c +6.194835 19.200245 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1242 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001332 00000 n +0000001355 00000 n +0000001528 00000 n +0000001602 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1661 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift index 1ff9d4634c..d97fd8fb65 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenInfoItem.swift @@ -35,6 +35,7 @@ final class PeerInfoScreenInfoItem: PeerInfoScreenItem { private final class PeerInfoScreenInfoItemNode: PeerInfoScreenItemNode { private let bottomSeparatorNode: ASDisplayNode + private let maskNode: ASImageNode private var item: PeerInfoScreenInfoItem? private var itemNode: InfoItemNode? @@ -43,6 +44,9 @@ private final class PeerInfoScreenInfoItemNode: PeerInfoScreenItemNode { self.bottomSeparatorNode = ASDisplayNode() self.bottomSeparatorNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + super.init() self.addSubnode(self.bottomSeparatorNode) @@ -99,6 +103,18 @@ private final class PeerInfoScreenInfoItemNode: PeerInfoScreenItemNode { separatorInset += 49.0 } + let hasCorners = safeInsets.left > 0.0 && (topItem == nil || bottomItem == nil) + let hasTopCorners = hasCorners && topItem == nil + let hasBottomCorners = hasCorners && bottomItem == nil + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height)) + self.bottomSeparatorNode.isHidden = hasBottomCorners + + if self.maskNode.supernode == nil { + self.addSubnode(self.maskNode) + } + transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel))) transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0) diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift index 8b6e4af91c..2af5ba8af4 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift @@ -51,6 +51,7 @@ final class PeerInfoScreenMemberItem: PeerInfoScreenItem { private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { private let selectionNode: PeerInfoScreenSelectableBackgroundNode + private let maskNode: ASImageNode private let bottomSeparatorNode: ASDisplayNode private var item: PeerInfoScreenMemberItem? @@ -61,6 +62,9 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() }) self.selectionNode.isUserInteractionEnabled = false + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + self.bottomSeparatorNode = ASDisplayNode() self.bottomSeparatorNode.isLayerBacked = true @@ -221,6 +225,19 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: itemNode.bounds.size)) + + let hasCorners = safeInsets.left > 0.0 && (topItem == nil || bottomItem == nil) + let hasTopCorners = hasCorners && topItem == nil + let hasBottomCorners = hasCorners && bottomItem == nil + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height)) + self.bottomSeparatorNode.isHidden = hasBottomCorners + + if self.maskNode.supernode == nil { + self.addSubnode(self.maskNode) + } + let highlightNodeOffset: CGFloat = topItem == nil ? 0.0 : UIScreenPixel self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition) transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset))) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 309eb36879..9e135997f6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1044,6 +1044,7 @@ struct PeerInfoHeaderNavigationButtonSpec: Equatable { } final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { + private var presentationData: PresentationData? private(set) var buttonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] private var currentButtons: [PeerInfoHeaderNavigationButtonSpec] = [] @@ -1060,14 +1061,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?) -> Void)? - override init() { - super.init() - } - func update(size: CGSize, presentationData: PresentationData, buttons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, transition: ContainedViewLayoutTransition) { let maximumExpandOffset: CGFloat = 14.0 let expandOffset: CGFloat = -expandFraction * maximumExpandOffset - if self.currentButtons != buttons { + if self.currentButtons != buttons || presentationData.strings !== self.presentationData?.strings { self.currentButtons = buttons var nextRegularButtonOrigin = size.width - 16.0 @@ -1145,6 +1142,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { } } } + self.presentationData = presentationData } } @@ -1152,7 +1150,7 @@ final class PeerInfoHeaderRegularContentNode: ASDisplayNode { } -enum PeerInfoHeaderTextFieldNodeKey { +enum PeerInfoHeaderTextFieldNodeKey: Equatable { case firstName case lastName case title @@ -1162,15 +1160,17 @@ enum PeerInfoHeaderTextFieldNodeKey { protocol PeerInfoHeaderTextFieldNode: ASDisplayNode { var text: String { get } - func update(width: CGFloat, safeInset: CGFloat, hasPrevious: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat } final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeaderTextFieldNode, UITextFieldDelegate { + private let backgroundNode: ASDisplayNode private let textNode: TextFieldNode private let measureTextNode: ImmediateTextNode private let clearIconNode: ASImageNode private let clearButtonNode: HighlightableButtonNode private let topSeparator: ASDisplayNode + private let maskNode: ASImageNode private var theme: PresentationTheme? @@ -1179,6 +1179,8 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader } override init() { + self.backgroundNode = ASDisplayNode() + self.textNode = TextFieldNode() self.measureTextNode = ImmediateTextNode() self.measureTextNode.maximumNumberOfLines = 0 @@ -1194,12 +1196,17 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader self.topSeparator = ASDisplayNode() + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + super.init() + self.addSubnode(self.backgroundNode) self.addSubnode(self.textNode) self.addSubnode(self.clearIconNode) self.addSubnode(self.clearButtonNode) self.addSubnode(self.topSeparator) + self.addSubnode(self.maskNode) self.textNode.textField.delegate = self @@ -1237,12 +1244,15 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader self.clearButtonNode.isAccessibilityElement = isHidden } - func update(width: CGFloat, safeInset: CGFloat, hasPrevious: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) self.textNode.textField.font = titleFont if self.theme !== presentationData.theme { self.theme = presentationData.theme + if isSettings { + self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + } self.textNode.textField.textColor = presentationData.theme.list.itemPrimaryTextColor self.textNode.textField.keyboardAppearance = presentationData.theme.rootController.keyboardColor.keyboardAppearance self.textNode.textField.tintColor = presentationData.theme.list.itemAccentColor @@ -1260,8 +1270,12 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader self.textNode.textField.text = updateText } + if !hasPrevious && isSettings { + self.topSeparator.isHidden = true + } self.topSeparator.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - self.topSeparator.frame = CGRect(origin: CGPoint(x: safeInset + (hasPrevious ? 16.0 : 0.0), y: 0.0), size: CGSize(width: width, height: UIScreenPixel)) + let separatorX = safeInset + (hasPrevious ? 16.0 : 0.0) + self.topSeparator.frame = CGRect(origin: CGPoint(x: separatorX, y: 0.0), size: CGSize(width: width - separatorX - safeInset, height: UIScreenPixel)) let measureText = "|" let attributedMeasureText = NSAttributedString(string: measureText, font: titleFont, textColor: .black) @@ -1276,8 +1290,16 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader self.clearIconNode.frame = CGRect(origin: CGPoint(x: width - safeInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size) } + self.backgroundNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: max(1.0, width - safeInset * 2.0), height: height)) self.textNode.frame = CGRect(origin: CGPoint(x: safeInset + 16.0, y: floor((height - 40.0) / 2.0)), size: CGSize(width: max(1.0, width - 16.0 * 2.0 - 32.0), height: 40.0)) + let hasCorners = safeInset > 0.0 && (!hasPrevious || !hasNext) + let hasTopCorners = hasCorners && !hasPrevious + let hasBottomCorners = hasCorners && !hasNext + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInset, y: 0.0), size: CGSize(width: width - safeInset - safeInset, height: height)) + self.textNode.isUserInteractionEnabled = isEnabled self.textNode.alpha = isEnabled ? 1.0 : 0.6 @@ -1364,7 +1386,7 @@ final class PeerInfoHeaderMultiLineTextFieldNode: ASDisplayNode, PeerInfoHeaderT self.updateClearButtonVisibility() } - func update(width: CGFloat, safeInset: CGFloat, hasPrevious: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { + func update(width: CGFloat, safeInset: CGFloat, isSettings: Bool, hasPrevious: Bool, hasNext: Bool, placeholder: String, isEnabled: Bool, presentationData: PresentationData, updateText: String?) -> CGFloat { self.currentParams = (width, safeInset) self.fontSize = presentationData.listsFontSize @@ -1613,7 +1635,7 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode { placeholder = presentationData.strings.Channel_Edit_AboutItem isEnabled = canEditPeerInfo(context: self.context, peer: peer) } - let itemHeight = itemNode.update(width: width, safeInset: safeInset, hasPrevious: hasPrevious, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText) + let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText) transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight))) contentHeight += itemHeight hasPrevious = true @@ -1671,8 +1693,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { let usernameNodeRawContainer: ASDisplayNode let usernameNode: MultiScaleTextNode var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:] - private let backgroundNode: NavigationBackgroundNode - private let expandedBackgroundNode: NavigationBackgroundNode + let backgroundNode: NavigationBackgroundNode + let expandedBackgroundNode: NavigationBackgroundNode let separatorNode: ASDisplayNode let navigationBackgroundNode: ASDisplayNode let navigationBackgroundBackgroundNode: ASDisplayNode @@ -1693,6 +1715,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { var navigationTransition: PeerInfoHeaderNavigationTransition? + var updateHeaderAlpha: ((CGFloat, ContainedViewLayoutTransition) -> Void)? + init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isSettings: Bool) { self.context = context self.isAvatarExpanded = avatarInitiallyExpanded @@ -1772,16 +1796,25 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.addSubnode(self.backgroundNode) self.addSubnode(self.expandedBackgroundNode) self.titleNodeContainer.addSubnode(self.titleNode) - self.regularContentNode.addSubnode(self.titleNodeContainer) self.subtitleNodeContainer.addSubnode(self.subtitleNode) self.subtitleNodeContainer.addSubnode(self.panelSubtitleNode) - self.regularContentNode.addSubnode(self.subtitleNodeContainer) - self.regularContentNode.addSubnode(self.subtitleNodeRawContainer) self.usernameNodeContainer.addSubnode(self.usernameNode) - self.regularContentNode.addSubnode(self.usernameNodeContainer) - self.regularContentNode.addSubnode(self.usernameNodeRawContainer) + if !self.isSettings { + self.regularContentNode.addSubnode(self.titleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeRawContainer) + self.regularContentNode.addSubnode(self.usernameNodeContainer) + self.regularContentNode.addSubnode(self.usernameNodeRawContainer) + } self.regularContentNode.addSubnode(self.avatarListNode) self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode) + if self.isSettings { + self.regularContentNode.addSubnode(self.titleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeContainer) + self.regularContentNode.addSubnode(self.subtitleNodeRawContainer) + self.regularContentNode.addSubnode(self.usernameNodeContainer) + self.regularContentNode.addSubnode(self.usernameNodeRawContainer) + } self.addSubnode(self.regularContentNode) self.addSubnode(self.editingContentNode) self.addSubnode(self.avatarOverlayNode) @@ -1818,7 +1851,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { return } strongSelf.navigationButtonContainer.layer.animateAlpha(from: 0.0, to: strongSelf.navigationButtonContainer.alpha, duration: 0.25) - strongSelf.avatarListNode.listContainerNode.shadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.shadowNode.alpha, duration: 0.25) + strongSelf.avatarListNode.listContainerNode.topShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.topShadowNode.alpha, duration: 0.25) + strongSelf.avatarListNode.listContainerNode.bottomShadowNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.bottomShadowNode.alpha, duration: 0.25) strongSelf.avatarListNode.listContainerNode.controlsContainerNode.layer.animateAlpha(from: 0.0, to: strongSelf.avatarListNode.listContainerNode.controlsContainerNode.alpha, duration: 0.25) strongSelf.animateOverlaysFadeIn?() @@ -2022,7 +2056,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: false, videoCallsEnabled: self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact) var isVerified = false + let smallTitleString: NSAttributedString let titleString: NSAttributedString + let smallSubtitleString: NSAttributedString let subtitleString: NSAttributedString var panelSubtitleString: NSAttributedString? let usernameString: NSAttributedString @@ -2049,17 +2085,22 @@ final class PeerInfoHeaderNode: ASDisplayNode { title = " " } } - titleString = NSAttributedString(string: title, font: Font.semibold(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor) - + if self.isSettings { + titleString = NSAttributedString(string: title, font: Font.semibold(26.0), textColor: presentationData.theme.list.itemPrimaryTextColor) + smallTitleString = NSAttributedString(string: title, font: Font.semibold(28.0), textColor: .white) + } else { + titleString = NSAttributedString(string: title, font: Font.semibold(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor) + smallTitleString = titleString + } if self.isSettings, let user = peer as? TelegramUser { - let formattedPhone = formatPhoneNumber(user.phone ?? "") - subtitleString = NSAttributedString(string: formattedPhone, font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + var subtitle = formatPhoneNumber(user.phone ?? "") - var username = "" if let addressName = user.addressName, !addressName.isEmpty { - username = "@\(addressName)" + subtitle = "\(subtitle) • @\(addressName)" } - usernameString = NSAttributedString(string: username, font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + smallSubtitleString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) + subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) } else if let statusData = statusData { let subtitleColor: UIColor if statusData.isActivity { @@ -2068,6 +2109,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { subtitleColor = presentationData.theme.list.itemSecondaryTextColor } subtitleString = NSAttributedString(string: statusData.text, font: Font.regular(15.0), textColor: subtitleColor) + smallSubtitleString = subtitleString usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) if let panelStatusData = panelStatusData { @@ -2081,28 +2123,34 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } else { subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + smallSubtitleString = subtitleString usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) } } else { titleString = NSAttributedString(string: " ", font: Font.semibold(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor) + smallTitleString = titleString subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + smallSubtitleString = subtitleString usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) } let textSideInset: CGFloat = 44.0 - let expandedAvatarHeight: CGFloat = expandedAvatarListSize.height + expandedAvatarControlsHeight + var expandedAvatarHeight: CGFloat = expandedAvatarListSize.height + if !self.isSettings { + expandedAvatarHeight += expandedAvatarControlsHeight + } let titleConstrainedSize = CGSize(width: width - textSideInset * 2.0 - (isVerified ? 16.0 : 0.0), height: .greatestFiniteMagnitude) let titleNodeLayout = self.titleNode.updateLayout(states: [ TitleNodeStateRegular: MultiScaleTextState(attributedText: titleString, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributedText: titleString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)) + TitleNodeStateExpanded: MultiScaleTextState(attributedText: smallTitleString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)) ], mainState: TitleNodeStateRegular) self.titleNode.accessibilityLabel = titleString.string let subtitleNodeLayout = self.subtitleNode.updateLayout(states: [ TitleNodeStateRegular: MultiScaleTextState(attributedText: subtitleString, constrainedSize: titleConstrainedSize), - TitleNodeStateExpanded: MultiScaleTextState(attributedText: subtitleString, constrainedSize: CGSize(width: titleConstrainedSize.width - 82.0, height: titleConstrainedSize.height)) + TitleNodeStateExpanded: MultiScaleTextState(attributedText: smallSubtitleString, constrainedSize: self.isSettings ? titleConstrainedSize : CGSize(width: titleConstrainedSize.width - 82.0, height: titleConstrainedSize.height)) ], mainState: TitleNodeStateRegular) self.subtitleNode.accessibilityLabel = subtitleString.string @@ -2133,26 +2181,40 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size)) } - let titleFrame: CGRect + var titleFrame: CGRect let subtitleFrame: CGRect let usernameFrame: CGRect let usernameSpacing: CGFloat = 4.0 var twoLineInfo = false + if self.isSettings { + transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: expandedAvatarHeight - 70.0), size: CGSize(width: width, height: 70.0))) + } + if self.isAvatarExpanded { let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7) - let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - expandedAvatarControlsHeight + 9.0 + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) + let minTitleFrame: CGRect + if self.isSettings { + minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) + } else { + minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - expandedAvatarControlsHeight + 9.0 + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) + } titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize) - subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 4.0), size: subtitleSize) + if self.isSettings { + subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize) + } else { + subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 4.0), size: subtitleSize) + } usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize) } else { titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 10.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) - + if self.isSettings { + titleFrame = titleFrame.offsetBy(dx: 0.0, dy: 13.0) + } let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width - twoLineInfo = true + twoLineInfo = false if usernameSize.width == 0.0 || twoLineInfo { subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize) - } else { subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize) @@ -2200,6 +2262,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isSettings { subtitleAlpha = 1.0 - titleCollapseFraction panelSubtitleAlpha = 0.0 + + var headerBackgroundAlpha: CGFloat = 0.0 + if !state.isEditing && titleCollapseFraction >= 0.8 { + headerBackgroundAlpha = (titleCollapseFraction - 0.8) / 0.2 + } + self.updateHeaderAlpha?(headerBackgroundAlpha, transition) } else { if (panelSubtitleString ?? subtitleString).string != subtitleString.string { subtitleAlpha = 1.0 - effectiveAreaExpansionFraction @@ -2315,7 +2383,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateFrame(node: self.avatarListNode.listContainerNode.controlsClippingNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.width / 2.0, y: -controlsClippingFrame.height / 2.0), size: controlsClippingFrame.size)) transition.updateFrameAdditive(node: self.avatarListNode.listContainerNode.controlsContainerNode, frame: CGRect(origin: CGPoint(x: -controlsClippingFrame.minX, y: -controlsClippingFrame.minY), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) - transition.updateFrame(node: self.avatarListNode.listContainerNode.shadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: expandedAvatarListSize.width, height: navigationHeight + 20.0))) + transition.updateFrame(node: self.avatarListNode.listContainerNode.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: expandedAvatarListSize.width, height: navigationHeight + 20.0))) transition.updateFrame(node: self.avatarListNode.listContainerNode.stripContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusBarHeight < 25.0 ? (statusBarHeight + 2.0) : (statusBarHeight - 3.0)), size: CGSize(width: expandedAvatarListSize.width, height: 2.0))) transition.updateFrame(node: self.avatarListNode.listContainerNode.highlightContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: expandedAvatarListSize.width, height: expandedAvatarListSize.height))) transition.updateAlpha(node: self.avatarListNode.listContainerNode.controlsContainerNode, alpha: self.isAvatarExpanded ? (1.0 - transitionFraction) : 0.0) @@ -2599,7 +2667,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { let resolvedRegularHeight: CGFloat if self.isAvatarExpanded { - resolvedRegularHeight = expandedAvatarListSize.height + expandedAvatarControlsHeight + if self.isSettings { + resolvedRegularHeight = expandedAvatarListSize.height + } else { + resolvedRegularHeight = expandedAvatarListSize.height + expandedAvatarControlsHeight + } } else { resolvedRegularHeight = panelWithAvatarHeight + navigationHeight } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 885b84e0ba..d2861bf2ee 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -172,7 +172,8 @@ private final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode { itemTransition.updateFrame(node: itemNode, frame: itemFrame) if wasAdded { itemNode.alpha = 0.0 - transition.updateAlpha(node: itemNode, alpha: 1.0) + let alphaTransition: ContainedViewLayoutTransition = transition.isAnimated ? .animated(duration: 0.35, curve: .linear) : .immediate + alphaTransition.updateAlpha(node: itemNode, alpha: 1.0) } if item is PeerInfoScreenCommentItem { @@ -1639,6 +1640,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.paneContainerNode.parentController = controller + self.headerNode.updateHeaderAlpha = { [weak self] alpha, transition in + self?.updateHeaderBackgroundAlpha(alpha, transition: transition) + } + self._interaction = PeerInfoInteraction( openUsername: { [weak self] value in self?.openUsername(value: value) @@ -2971,6 +2976,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate override func didLoad() { super.didLoad() + if self.isSettings { + self.updateHeaderBackgroundAlpha(0.0, transition: .immediate) + } + self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in if let strongSelf = self { return strongSelf.state.isEditing @@ -2980,6 +2989,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } } + public func updateHeaderBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + self.controller?.navigationBar?.updateBackgroundAlpha(alpha, transition: transition) + + transition.updateAlpha(node: self.headerNode.backgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.headerNode.expandedBackgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.headerNode.separatorNode, alpha: alpha, delay: 0.15) + } + var canAttachVideo: Bool? private func updateData(_ data: PeerInfoScreenData) { @@ -6326,7 +6343,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate var contentHeight: CGFloat = 0.0 - let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) + let sectionInset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) + let headerInset: CGFloat + if self.isSettings { + headerInset = sectionInset + } else { + headerInset = layout.safeInsets.left + } + + let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight)) if additive { transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame) @@ -6341,10 +6366,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate var validRegularSections: [AnyHashable] = [] if !self.isMediaOnly { var insets = UIEdgeInsets() - let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) if self.isSettings { - insets.left += inset - insets.right += inset + insets.left += sectionInset + insets.right += sectionInset } else { insets = layout.safeInsets } @@ -6363,6 +6387,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate for (sectionId, sectionItems) in items { validRegularSections.append(sectionId) + var wasAdded = false let sectionNode: PeerInfoScreenItemSectionContainerNode if let current = self.regularSections[sectionId] { sectionNode = current @@ -6370,6 +6395,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate sectionNode = PeerInfoScreenItemSectionContainerNode() self.regularSections[sectionId] = sectionNode self.scrollNode.addSubnode(sectionNode) + wasAdded = true + } + + if wasAdded && transition.isAnimated && self.isSettings && !self.state.isEditing { + sectionNode.alpha = 0.0 + transition.updateAlpha(node: sectionNode, alpha: 1.0) } let sectionHeight = sectionNode.update(width: layout.size.width, safeInsets: insets, presentationData: self.presentationData, items: sectionItems, transition: transition) @@ -6405,9 +6436,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate for (sectionId, sectionItems) in editItems { var insets = UIEdgeInsets() - let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) - insets.left += inset - insets.right += inset + insets.left += sectionInset + insets.right += sectionInset if self.state.isEditing { currentInsets = insets @@ -6582,10 +6612,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate |> take(1)) } } - - private func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) { + + fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) { let offsetY = self.scrollNode.view.contentOffset.y + if self.isSettings, !(self.controller?.movingInHierarchy == true) { + let bottomOffsetY = self.scrollNode.view.contentSize.height + self.scrollNode.view.contentInset.bottom - offsetY - self.scrollNode.frame.height + let backgroundAlpha: CGFloat = min(30.0, bottomOffsetY) / 30.0 + + if let tabBarController = self.controller?.parent as? TabBarController { + tabBarController.updateBackgroundAlpha(backgroundAlpha, transition: transition) + } + } + if self.state.isEditing || offsetY <= 50.0 || self.paneContainerNode.alpha.isZero { if !self.scrollNode.view.bounces { self.scrollNode.view.bounces = true @@ -6600,7 +6639,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate if let (layout, navigationHeight) = self.validLayout { if !additive { - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) + let sectionInset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) + let headerInset: CGFloat + if self.isSettings { + headerInset = sectionInset + } else { + headerInset = layout.safeInsets.left + } + + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -7215,12 +7262,27 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { super.displayNodeDidLoad() } + fileprivate var movingInHierarchy = false + public override func willMove(toParent viewController: UIViewController?) { + super.willMove(toParent: parent) + + if self.isSettings, viewController == nil, let tabBarController = self.parent as? TabBarController { + self.movingInHierarchy = true + tabBarController.updateBackgroundAlpha(1.0, transition: .immediate) + } + } + public override func didMove(toParent viewController: UIViewController?) { super.didMove(toParent: viewController) - if self.isSettings && viewController == nil { - Queue.mainQueue().after(0.1) { - self.controllerNode.resetHeaderExpansion() + if self.isSettings { + if viewController == nil { + self.movingInHierarchy = false + Queue.mainQueue().after(0.1) { + self.controllerNode.resetHeaderExpansion() + } + } else { + self.controllerNode.updateNavigation(transition: .immediate, additive: false) } } } @@ -7475,8 +7537,17 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig self.headerNode.navigationTransition = PeerInfoHeaderNavigationTransition(sourceNavigationBar: bottomNavigationBar, sourceTitleView: previousTitleView, sourceTitleFrame: previousTitleFrame, sourceSubtitleFrame: previousStatusFrame, fraction: fraction) var topHeight = topNavigationBar.backgroundNode.bounds.height + if let (layout, _) = self.screenNode.validLayout { - topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: nil, isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, transition: transition, additive: false) + let sectionInset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) + let headerInset: CGFloat + if screenNode.isSettings { + headerInset = sectionInset + } else { + headerInset = layout.safeInsets.left + } + + topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: nil, isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, transition: transition, additive: false) } let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m index 6dcba5e73e..22ea4a793e 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m @@ -138,7 +138,7 @@ static const void *userInfoKey = &userInfoKey; - (void)_ac91f40f_setBackBarButtonItem:(UIBarButtonItem *)backBarButtonItem { - UIBarButtonItem *previousItem = self.rightBarButtonItem; + UIBarButtonItem *previousItem = self.backBarButtonItem; [self _ac91f40f_setBackBarButtonItem:backBarButtonItem];