From 7860375458e4d80ccaf14f7195f46694c2b273d8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 27 Oct 2021 15:53:59 +0400 Subject: [PATCH] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 + .../CallListUI/Sources/CallListCallItem.swift | 118 ++-- .../ChatListFilterPresetCategoryItem.swift | 4 +- .../ChatListFilterPresetListItem.swift | 3 +- .../Sources/InviteLinkEditController.swift | 2 +- .../Sources/ItemListAddressItem.swift | 25 +- .../Sources/ItemListPeerActionItem.swift | 5 +- .../Sources/ItemListPeerItem.swift | 4 +- .../Sources/ItemListStickerPackItem.swift | 3 +- .../Sources/ItemListVenueItem.swift | 2 + .../Sources/LocationLiveListItem.swift | 3 + .../DataAndStorageSettingsController.swift | 11 +- .../ProxySettingsServerItem.swift | 3 +- .../ItemListRecentSessionItem.swift | 110 +++- .../Recent Sessions/ItemListWebsiteItem.swift | 74 ++- .../RecentSessionsController.swift | 28 +- .../RecentSessionScreen.swift | 506 ++++++++++++++++++ .../Sources/ThemePickerController.swift | 4 +- .../Themes/ThemeSettingsController.swift | 69 ++- .../Resources/PresentationResourceKey.swift | 1 + .../PresentationResourcesItemList.swift | 6 + .../Settings/Reset.imageset/Contents.json | 12 + .../Settings/Reset.imageset/Icon-36.pdf | 88 +++ .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 10 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 5 +- .../UINavigationItem+Proxy.m | 2 +- 26 files changed, 991 insertions(+), 113 deletions(-) create mode 100644 submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/Reset.imageset/Icon-36.pdf 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/InviteLinksUI/Sources/InviteLinkEditController.swift b/submodules/InviteLinksUI/Sources/InviteLinkEditController.swift index 1bece46115..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 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/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/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/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift index 11a0386f1c..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,12 +99,21 @@ 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") { + if device.contains("chrome") && !device.contains("chromebook") { return UIImage(bundleImageName: "Settings/Devices/Chrome") } if device.contains("brave") { @@ -120,9 +131,18 @@ 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 } @@ -145,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 @@ -162,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 @@ -189,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) } @@ -236,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? @@ -260,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 { @@ -418,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) @@ -447,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))) @@ -456,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) @@ -467,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 19439abf00..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,6 +97,15 @@ 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) @@ -118,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 @@ -135,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 @@ -155,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 @@ -169,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) { @@ -348,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) @@ -388,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) @@ -399,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) } @@ -427,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 8e16697e6d..e16ee6dc8f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -1067,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() @@ -1248,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 @@ -1281,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 @@ -1332,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/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/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index bb5d84a3d7..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 } } @@ -2210,7 +2208,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } 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: 6.0) + titleFrame = titleFrame.offsetBy(dx: 0.0, dy: 13.0) } let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width twoLineInfo = false diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 5eff91140e..d2861bf2ee 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -6616,7 +6616,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) { let offsetY = self.scrollNode.view.contentOffset.y - if self.isSettings { + 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 @@ -7262,10 +7262,12 @@ 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) } } @@ -7275,6 +7277,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { if self.isSettings { if viewController == nil { + self.movingInHierarchy = false Queue.mainQueue().after(0.1) { self.controllerNode.resetHeaderExpansion() } 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];