Various Fixes

This commit is contained in:
Ilya Laktyushin 2021-02-15 21:05:22 +04:00
parent 1fe96da125
commit 19901b4177
15 changed files with 3401 additions and 3342 deletions

View File

@ -6084,3 +6084,6 @@ Sorry for the inconvenience.";
"ChannelInfo.InviteLink.RevokeAlert.Text" = "Are you sure you want to revoke this link? Once you do, no one will be able to join the channel using it."; "ChannelInfo.InviteLink.RevokeAlert.Text" = "Are you sure you want to revoke this link? Once you do, no one will be able to join the channel using it.";
"Group.Info.Members" = "Members"; "Group.Info.Members" = "Members";
"Group.Members.Title" = "Members";
"Group.Members.AddMembers" = "Add Members";
"Group.Members.AddMembersHelp" = "Only group admins can see this list.";

View File

@ -263,7 +263,7 @@ private struct ChannelMembersControllerState: Equatable {
} }
} }
private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?) -> [ChannelMembersEntry] { private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?, isGroup: Bool) -> [ChannelMembersEntry] {
if participants == nil || participants?.count == nil { if participants == nil || participants?.count == nil {
return [] return []
} }
@ -277,11 +277,11 @@ private func channelMembersControllerEntries(context: AccountContext, presentati
} }
if canAddMember { if canAddMember {
entries.append(.addMember(presentationData.theme, presentationData.strings.Channel_Members_AddMembers)) entries.append(.addMember(presentationData.theme, isGroup ? presentationData.strings.Group_Members_AddMembers : presentationData.strings.Channel_Members_AddMembers))
if let peer = view.peers[view.peerId] as? TelegramChannel, peer.addressName == nil { if let peer = view.peers[view.peerId] as? TelegramChannel, peer.addressName == nil {
entries.append(.inviteLink(presentationData.theme, presentationData.strings.Channel_Members_InviteLink)) entries.append(.inviteLink(presentationData.theme, presentationData.strings.Channel_Members_InviteLink))
} }
entries.append(.addMemberInfo(presentationData.theme, presentationData.strings.Channel_Members_AddMembersHelp)) entries.append(.addMemberInfo(presentationData.theme, isGroup ? presentationData.strings.Group_Members_AddMembersHelp : presentationData.strings.Channel_Members_AddMembersHelp))
} }
@ -463,6 +463,11 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), peerView, peersPromise.get()) let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), peerView, peersPromise.get())
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { presentationData, state, view, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state, view, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in
var isGroup = true
if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
}
var rightNavigationButton: ItemListNavigationButton? var rightNavigationButton: ItemListNavigationButton?
var secondaryRightNavigationButton: ItemListNavigationButton? var secondaryRightNavigationButton: ItemListNavigationButton?
if let peers = peers, !peers.isEmpty { if let peers = peers, !peers.isEmpty {
@ -514,8 +519,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
let previous = previousPeers let previous = previousPeers
previousPeers = peers previousPeers = peers
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: peers), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -526,6 +526,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
var pushControllerImpl: ((ViewController) -> Void)? var pushControllerImpl: ((ViewController) -> Void)?
var navigateToChatControllerImpl: ((PeerId) -> Void)? var navigateToChatControllerImpl: ((PeerId) -> Void)?
var dismissInputImpl: (() -> Void)? var dismissInputImpl: (() -> Void)?
var dismissToChatController: (() -> Void)?
var resetSlowmodeVisualValueImpl: (() -> Void)? var resetSlowmodeVisualValueImpl: (() -> Void)?
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
@ -772,7 +773,9 @@ public func channelPermissionsController(context: AccountContext, peerId origina
let _ = (convertGroupToGigagroup(account: context.account, peerId: originalPeerId) let _ = (convertGroupToGigagroup(account: context.account, peerId: originalPeerId)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).0), elevatedLayout: false, action: { _ in return false }), nil) presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).0), elevatedLayout: true, action: { _ in return false }), nil)
dismissToChatController?()
}) })
})]) })])
presentControllerImpl?(alertController, nil) presentControllerImpl?(alertController, nil)
@ -958,6 +961,21 @@ public func channelPermissionsController(context: AccountContext, peerId origina
dismissInputImpl = { [weak controller] in dismissInputImpl = { [weak controller] in
controller?.view.endEditing(true) controller?.view.endEditing(true)
} }
dismissToChatController = { [weak controller] in
if let controller = controller, let navigationController = controller.navigationController as? NavigationController {
var viewControllers = navigationController.viewControllers
viewControllers = viewControllers.filter { controller in
if controller is ItemListController {
return false
}
if controller is PeerInfoScreen {
return false
}
return true
}
navigationController.setViewControllers(viewControllers, animated: true)
}
}
resetSlowmodeVisualValueImpl = { [weak controller] in resetSlowmodeVisualValueImpl = { [weak controller] in
guard let controller = controller else { guard let controller = controller else {
return return

View File

@ -51,7 +51,7 @@ private final class ReportPeerDetailsActionSheetItemNode: ActionSheetItemNode {
self.addSubnode(self.inputFieldNode) self.addSubnode(self.inputFieldNode)
self.inputFieldNode.updateText = { text in self.inputFieldNode.updateText = { text in
textUpdated(text) textUpdated(String(text.prefix(512)))
} }
self.inputFieldNode.updateHeight = { [weak self] in self.inputFieldNode.updateHeight = { [weak self] in
self?.requestLayout?() self?.requestLayout?()

View File

@ -30,6 +30,7 @@ public final class PermissionController: ViewController {
private var skip: (() -> Void)? private var skip: (() -> Void)?
public var proceed: ((Bool) -> Void)? public var proceed: ((Bool) -> Void)?
public init(context: AccountContext, splashScreen: Bool = true, splitTest: PermissionUISplitTest? = nil) { public init(context: AccountContext, splashScreen: Bool = true, splitTest: PermissionUISplitTest? = nil) {
self.context = context self.context = context
self.splitTest = splitTest self.splitTest = splitTest
@ -241,9 +242,17 @@ public final class PermissionController: ViewController {
} }
} }
@objc private func cancelPressed() {
self.dismiss()
}
public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)
if let state = self.state, case .custom(.animation, _, _, _, _, _, _) = state, layout.size.width <= 320.0 {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
}
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.splashScreen ? 0.0 : self.navigationHeight, transition: transition) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.splashScreen ? 0.0 : self.navigationHeight, transition: transition)
} }

File diff suppressed because one or more lines are too long

View File

@ -7105,7 +7105,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (convertGroupToGigagroup(account: context.account, peerId: peerId) let _ = (convertGroupToGigagroup(account: context.account, peerId: peerId)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).0), elevatedLayout: false, action: { _ in return false }), in: .current) strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).0), elevatedLayout: false, action: { _ in return false }), in: .current)
}) })
})]) })])
strongSelf.present(alertController, in: .window(.root)) strongSelf.present(alertController, in: .window(.root))

View File

@ -15,16 +15,20 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
return nil return nil
} }
var isScheduledOrPinnedMessages = false var inhibitTitlePanelDisplay = false
switch chatPresentationInterfaceState.subject { switch chatPresentationInterfaceState.subject {
case .scheduledMessages, .pinnedMessages: case .scheduledMessages, .pinnedMessages:
isScheduledOrPinnedMessages = true inhibitTitlePanelDisplay = true
default: default:
break break
} }
if case .peer = chatPresentationInterfaceState.chatLocation {
} else {
inhibitTitlePanelDisplay = true
}
var selectedContext: ChatTitlePanelContext? var selectedContext: ChatTitlePanelContext?
if !chatPresentationInterfaceState.titlePanelContexts.isEmpty && !isScheduledOrPinnedMessages { if !chatPresentationInterfaceState.titlePanelContexts.isEmpty && !inhibitTitlePanelDisplay {
loop: for context in chatPresentationInterfaceState.titlePanelContexts.reversed() { loop: for context in chatPresentationInterfaceState.titlePanelContexts.reversed() {
switch context { switch context {
case .pinnedMessage: case .pinnedMessage:
@ -40,7 +44,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
} }
var displayActionsPanel = false var displayActionsPanel = false
if !chatPresentationInterfaceState.peerIsBlocked && !isScheduledOrPinnedMessages, let contactStatus = chatPresentationInterfaceState.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings { if !chatPresentationInterfaceState.peerIsBlocked && !inhibitTitlePanelDisplay, let contactStatus = chatPresentationInterfaceState.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings {
if !peerStatusSettings.flags.isEmpty { if !peerStatusSettings.flags.isEmpty {
if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) {
displayActionsPanel = true displayActionsPanel = true

View File

@ -1329,7 +1329,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
} else { } else {
peerTitle = linkedDiscussionPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) peerTitle = linkedDiscussionPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
} }
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, action: { items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: UIImage(bundleImageName: "Chat/Info/GroupChannelIcon"), action: {
interaction.editingOpenDiscussionGroupSetup() interaction.editingOpenDiscussionGroupSetup()
})) }))
} }
@ -1342,7 +1342,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
} }
} }
if (isCreator && (channel.username?.isEmpty ?? true)) || (channel.adminRights?.flags.contains(.canInviteUsers) == true) { if (isCreator && (channel.username?.isEmpty ?? true) && cachedData.peerGeoLocation == nil) || (channel.adminRights?.flags.contains(.canInviteUsers) == true) {
let invitesText: String let invitesText: String
if let count = data.invitations?.count, count > 0 { if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)" invitesText = "\(count)"
@ -1499,7 +1499,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
} }
private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate { private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
private weak var controller: PeerInfoScreen? private weak var controller: PeerInfoScreenImpl?
private let context: AccountContext private let context: AccountContext
let peerId: PeerId let peerId: PeerId
@ -1580,7 +1580,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
private var didSetReady = false private var didSetReady = false
init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool, ignoreGroupInCommon: PeerId?) { init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool, ignoreGroupInCommon: PeerId?) {
self.controller = controller self.controller = controller
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
@ -5550,7 +5550,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
} }
public final class PeerInfoScreen: ViewController { public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private let context: AccountContext private let context: AccountContext
private let peerId: PeerId private let peerId: PeerId
private let avatarInitiallyExpanded: Bool private let avatarInitiallyExpanded: Bool

View File

@ -1395,9 +1395,9 @@ private let defaultChatControllerInteraction = ChatControllerInteraction.default
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) -> ViewController? { private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) -> ViewController? {
if let _ = peer as? TelegramGroup { if let _ = peer as? TelegramGroup {
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} else if let channel = peer as? TelegramChannel { } else if let _ = peer as? TelegramChannel {
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} else if peer is TelegramUser { } else if peer is TelegramUser {
var nearbyPeerDistance: Int32? var nearbyPeerDistance: Int32?
var callMessages: [Message] = [] var callMessages: [Message] = []
@ -1412,9 +1412,9 @@ private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: P
case let .group(id): case let .group(id):
ignoreGroupInCommon = id ignoreGroupInCommon = id
} }
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, ignoreGroupInCommon: ignoreGroupInCommon) return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, ignoreGroupInCommon: ignoreGroupInCommon)
} else if peer is TelegramSecretChat { } else if peer is TelegramSecretChat {
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} }
return nil return nil
} }

View File

@ -111,7 +111,7 @@ public final class TelegramRootController: NavigationController {
sharedContext.switchingData = (nil, nil, nil) sharedContext.switchingData = (nil, nil, nil)
} }
let accountSettingsController = PeerInfoScreen(context: self.context, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true) let accountSettingsController = PeerInfoScreenImpl(context: self.context, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true)
accountSettingsController.tabBarItemDebugTapAction = { [weak self, weak accountSettingsController] in accountSettingsController.tabBarItemDebugTapAction = { [weak self, weak accountSettingsController] in
guard let strongSelf = self, let accountSettingsController = accountSettingsController else { guard let strongSelf = self, let accountSettingsController = accountSettingsController else {
return return

View File

@ -30,6 +30,7 @@ public enum UndoOverlayContent {
case audioRate(slowdown: Bool, text: String) case audioRate(slowdown: Bool, text: String)
case forward(savedMessages: Bool, text: String) case forward(savedMessages: Bool, text: String)
case autoDelete(isOn: Bool, title: String?, text: String) case autoDelete(isOn: Bool, title: String?, text: String)
case gigagroupConversion(text: String)
} }
public enum UndoOverlayAction { public enum UndoOverlayAction {

View File

@ -519,6 +519,21 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.attributedText = attributedText self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2 self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
case let .gigagroupConversion(text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_gigagroup", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false displayUndo = false
self.originalRemainingSeconds = 3 self.originalRemainingSeconds = 3
} }
@ -547,14 +562,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
super.init() super.init()
switch content { switch content {
case .removedChat: case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode) self.panelWrapperNode.addSubnode(self.timerTextNode)
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward: case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion:
break break
case .dice: case .dice:
self.panelWrapperNode.clipsToBounds = true self.panelWrapperNode.clipsToBounds = true
case .info: case .info:
self.isUserInteractionEnabled = false self.isUserInteractionEnabled = false
} }
self.statusNode.flatMap(self.panelWrapperNode.addSubnode) self.statusNode.flatMap(self.panelWrapperNode.addSubnode)
self.iconNode.flatMap(self.panelWrapperNode.addSubnode) self.iconNode.flatMap(self.panelWrapperNode.addSubnode)