Merge commit 'f362ce9fc6a91bd1184d300e5dd7fd41c15436ae'

This commit is contained in:
Ali 2021-04-09 15:21:40 +04:00
commit 80ec63bf84
5 changed files with 4309 additions and 4254 deletions

View File

@ -6295,6 +6295,7 @@ Sorry for the inconvenience.";
"VoiceChat.LeaveConfirmation" = "Are you sure you want to leave this voice chat?"; "VoiceChat.LeaveConfirmation" = "Are you sure you want to leave this voice chat?";
"VoiceChat.LeaveVoiceChat" = "Leave Voice Chat"; "VoiceChat.LeaveVoiceChat" = "Leave Voice Chat";
"VoiceChat.LeaveAndEndVoiceChat" = "End Voice Chat"; "VoiceChat.LeaveAndEndVoiceChat" = "End Voice Chat";
"VoiceChat.LeaveAndCancelVoiceChat" = "Cancel Voice Chat";
"VoiceChat.ForwardTooltip.Chat" = "Invite link forwarded to **%@**"; "VoiceChat.ForwardTooltip.Chat" = "Invite link forwarded to **%@**";
"VoiceChat.ForwardTooltip.TwoChats" = "Invite link forwarded to **%@** and **%@**"; "VoiceChat.ForwardTooltip.TwoChats" = "Invite link forwarded to **%@** and **%@**";
@ -6341,7 +6342,7 @@ Sorry for the inconvenience.";
"VoiceChat.UnpinVideo" = "Unpin Video"; "VoiceChat.UnpinVideo" = "Unpin Video";
"Notification.VoiceChatScheduledChannel" = "Voice chat scheduled for %@"; "Notification.VoiceChatScheduledChannel" = "Voice chat scheduled for %@";
"Notification.VoiceChatScheduled" = "%1$@ Voice chat scheduled for %2$@"; "Notification.VoiceChatScheduled" = "%1$@ scheduled a voice chat for %2$@";
"VoiceChat.StartsIn" = "Starts in"; "VoiceChat.StartsIn" = "Starts in";
"VoiceChat.LateBy" = "Late by"; "VoiceChat.LateBy" = "Late by";
@ -6349,12 +6350,13 @@ Sorry for the inconvenience.";
"VoiceChat.StatusStartsIn" = "starts in %@"; "VoiceChat.StatusStartsIn" = "starts in %@";
"VoiceChat.StatusLateBy" = "late by %@"; "VoiceChat.StatusLateBy" = "late by %@";
"VoiceChat.Scheduled" = "Scheduled";
"VoiceChat.StartNow" = "Start Now"; "VoiceChat.StartNow" = "Start Now";
"VoiceChat.SetReminder" = "Set Reminder"; "VoiceChat.SetReminder" = "Set Reminder";
"VoiceChat.CancelReminder" = "Cancel Reminder"; "VoiceChat.CancelReminder" = "Cancel Reminder";
"VoiceChat.ShareShort" = "share"; "VoiceChat.ShareShort" = "share";
"VoiceChat.TapToEditTitle" = "Tap to edit title"; "VoiceChat.TapToEditTitle" = "Tap to edit title";
"ChannelInfo.ScheduleVoiceChat" = "Schedule Voice Chat"; "ChannelInfo.ScheduleVoiceChat" = "Schedule Voice Chat";
@ -6367,8 +6369,11 @@ Sorry for the inconvenience.";
"ScheduleVoiceChat.ScheduleTomorrow" = "Start tomorrow at %@"; "ScheduleVoiceChat.ScheduleTomorrow" = "Start tomorrow at %@";
"ScheduleVoiceChat.ScheduleOn" = "Start on %@ at %@"; "ScheduleVoiceChat.ScheduleOn" = "Start on %@ at %@";
"VoiceChat.ScheduledTitle" = "Scheduled Voice Chat";
"Conversation.ScheduledVoiceChat" = "Scheduled Voice Chat"; "Conversation.ScheduledVoiceChat" = "Scheduled Voice Chat";
"Conversation.ScheduledVoiceChatStartsOn" = "Voice chat starts %@"; "Conversation.ScheduledVoiceChatStartsOn" = "Voice chat starts %@";
"Conversation.ScheduledVoiceChatStartsOnShort" = "Starts %@"; "Conversation.ScheduledVoiceChatStartsOnShort" = "Starts %@";
"VoiceChat.CancelVoiceChat" = "Cancel Voice Chat";
"VoiceChat.CancelConfirmationTitle" = "Cancel Voice Chat";
"VoiceChat.CancelConfirmationText" = "Are you sure you want to cancel this voice chat?";
"VoiceChat.CancelConfirmationEnd" = "Cancel";

View File

@ -448,7 +448,7 @@ public final class VoiceChatController: ViewController {
} }
private enum ListEntry: Comparable, Identifiable { private enum ListEntry: Comparable, Identifiable {
case invite(PresentationTheme, PresentationStrings, String) case invite(PresentationTheme, PresentationStrings, String, Bool)
case peer(PeerEntry) case peer(PeerEntry)
var stableId: EntryId { var stableId: EntryId {
@ -462,8 +462,8 @@ public final class VoiceChatController: ViewController {
static func ==(lhs: ListEntry, rhs: ListEntry) -> Bool { static func ==(lhs: ListEntry, rhs: ListEntry) -> Bool {
switch lhs { switch lhs {
case let .invite(lhsTheme, lhsStrings, lhsText): case let .invite(lhsTheme, lhsStrings, lhsText, lhsIsLink):
if case let .invite(rhsTheme, rhsStrings, rhsText) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsText == rhsText { if case let .invite(rhsTheme, rhsStrings, rhsText, rhsIsLink) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsText == rhsText, lhsIsLink == rhsIsLink {
return true return true
} else { } else {
return false return false
@ -494,8 +494,8 @@ public final class VoiceChatController: ViewController {
func item(context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListViewItem { func item(context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListViewItem {
switch self { switch self {
case let .invite(_, _, text): case let .invite(_, _, text, isLink):
return VoiceChatActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .generic(UIImage(bundleImageName: "Chat/Context Menu/AddUser")!), action: { return VoiceChatActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .generic(UIImage(bundleImageName: isLink ? "Chat/Context Menu/Link" : "Chat/Context Menu/AddUser")!), action: {
interaction.openInvite() interaction.openInvite()
}) })
case let .peer(peerEntry): case let .peer(peerEntry):
@ -1186,22 +1186,33 @@ public final class VoiceChatController: ViewController {
let itemsForEntry: (PeerEntry, GroupCallParticipantsContext.Participant.MuteState?) -> [ContextMenuItem] = { entry, muteState in let itemsForEntry: (PeerEntry, GroupCallParticipantsContext.Participant.MuteState?) -> [ContextMenuItem] = { entry, muteState in
var items: [ContextMenuItem] = [] var items: [ContextMenuItem] = []
var hasVolumeSlider = false
let peer = entry.peer let peer = entry.peer
if let muteState = muteState, !muteState.canUnmute || muteState.mutedByYou { if let muteState = muteState, !muteState.canUnmute || muteState.mutedByYou {
} else { } else {
let minValue: CGFloat if entry.canManageCall || !entry.isMyPeer {
if let callState = strongSelf.callState, callState.canManageCall && callState.adminIds.contains(peer.id) && muteState != nil { hasVolumeSlider = true
minValue = 0.01
} else { let minValue: CGFloat
minValue = 0.0 if let callState = strongSelf.callState, callState.canManageCall && callState.adminIds.contains(peer.id) && muteState != nil {
} minValue = 0.01
items.append(.custom(VoiceChatVolumeContextItem(minValue: minValue, value: entry.volume.flatMap { CGFloat($0) / 10000.0 } ?? 1.0, valueChanged: { newValue, finished in
if finished && newValue.isZero {
let updatedMuteState = strongSelf.call.updateMuteState(peerId: peer.id, isMuted: true)
muteStatePromise.set(.single(updatedMuteState))
} else { } else {
strongSelf.call.setVolume(peerId: peer.id, volume: Int32(newValue * 10000), sync: finished) minValue = 0.0
} }
items.append(.custom(VoiceChatVolumeContextItem(minValue: minValue, value: entry.volume.flatMap { CGFloat($0) / 10000.0 } ?? 1.0, valueChanged: { newValue, finished in
if finished && newValue.isZero {
let updatedMuteState = strongSelf.call.updateMuteState(peerId: peer.id, isMuted: true)
muteStatePromise.set(.single(updatedMuteState))
} else {
strongSelf.call.setVolume(peerId: peer.id, volume: Int32(newValue * 10000), sync: finished)
}
}), true))
}
}
if entry.isMyPeer && !hasVolumeSlider && ((entry.about?.isEmpty ?? true) || entry.peer.smallProfileImage == nil) {
items.append(.custom(VoiceChatInfoContextItem(text: strongSelf.presentationData.strings.VoiceChat_ImproveYourProfileText, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tip"), color: theme.actionSheet.primaryTextColor)
}), true)) }), true))
} }
@ -1268,7 +1279,7 @@ public final class VoiceChatController: ViewController {
return .complete() return .complete()
}).start() }).start()
} else { } else {
let _ = (updatePeerTitle(account: strongSelf.context.account, peerId: peer.id, title: bio) let _ = (updatePeerDescription(account: strongSelf.context.account, peerId: peer.id, description: bio)
|> `catch` { _ -> Signal<Void, NoError> in |> `catch` { _ -> Signal<Void, NoError> in
return .complete() return .complete()
}).start() }).start()
@ -2019,7 +2030,8 @@ public final class VoiceChatController: ViewController {
})))*/ })))*/
if let callState = strongSelf.callState, callState.canManageCall { if let callState = strongSelf.callState, callState.canManageCall {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EndVoiceChat, textColor: .destructive, icon: { theme in let isScheduled = strongSelf.callState?.scheduleTimestamp != nil
items.append(.action(ContextMenuActionItem(text: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelVoiceChat : strongSelf.presentationData.strings.VoiceChat_EndVoiceChat, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { _, f in }, action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
@ -2041,7 +2053,7 @@ public final class VoiceChatController: ViewController {
}) })
} }
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: { let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
action() action()
})]) })])
strongSelf.controller?.present(alertController, in: .window(.root)) strongSelf.controller?.present(alertController, in: .window(.root))
@ -2223,7 +2235,7 @@ public final class VoiceChatController: ViewController {
} }
} }
private func updateMinimumDate() { private func updateSchedulePickerDates() {
let timeZone = TimeZone(secondsFromGMT: 0)! let timeZone = TimeZone(secondsFromGMT: 0)!
var calendar = Calendar(identifier: .gregorian) var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = timeZone calendar.timeZone = timeZone
@ -2234,9 +2246,10 @@ public final class VoiceChatController: ViewController {
let roundedDate = calendar.date(from: components)! let roundedDate = calendar.date(from: components)!
let next1MinDate = calendar.date(byAdding: .minute, value: 1, to: roundedDate) let next1MinDate = calendar.date(byAdding: .minute, value: 1, to: roundedDate)
let minute = components.minute ?? 0
components.minute = 0 components.minute = 0
let roundedToHourDate = calendar.date(from: components)! let roundedToHourDate = calendar.date(from: components)!
let nextTwoHourDate = calendar.date(byAdding: .hour, value: 2, to: roundedToHourDate) let nextTwoHourDate = calendar.date(byAdding: .hour, value: minute > 30 ? 4 : 3, to: roundedToHourDate)
let maxDate = calendar.date(byAdding: .day, value: 7, to: currentDate) let maxDate = calendar.date(byAdding: .day, value: 7, to: currentDate)
if let date = calendar.date(byAdding: .day, value: 365, to: currentDate) { if let date = calendar.date(byAdding: .day, value: 365, to: currentDate) {
@ -2275,7 +2288,7 @@ public final class VoiceChatController: ViewController {
pickerView.setValue(textColor, forKey: "textColor") pickerView.setValue(textColor, forKey: "textColor")
self.pickerView = pickerView self.pickerView = pickerView
self.updateMinimumDate() self.updateSchedulePickerDates()
if let currentDate = currentDate { if let currentDate = currentDate {
pickerView.date = currentDate pickerView.date = currentDate
} }
@ -2400,6 +2413,12 @@ public final class VoiceChatController: ViewController {
self?.timerNode.isHidden = true self?.timerNode.isHidden = true
}) })
if self.audioButton.isHidden {
self.audioButton.isHidden = false
self.audioButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.audioButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6, damping: 100.0)
}
self.updateTitle(transition: .animated(duration: 0.2, curve: .easeInOut)) self.updateTitle(transition: .animated(duration: 0.2, curve: .easeInOut))
} }
@ -2419,6 +2438,7 @@ public final class VoiceChatController: ViewController {
self.controller?.dismissAllTooltips() self.controller?.dismissAllTooltips()
if let callState = self.callState, callState.canManageCall { if let callState = self.callState, callState.canManageCall {
let isScheduled = callState.scheduleTimestamp != nil
let action: () -> Void = { [weak self] in let action: () -> Void = { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -2434,12 +2454,12 @@ public final class VoiceChatController: ViewController {
var items: [ActionSheetItem] = [] var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: self.presentationData.strings.VoiceChat_LeaveConfirmation)) items.append(ActionSheetTextItem(title: self.presentationData.strings.VoiceChat_LeaveConfirmation))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat, color: .destructive, action: { [weak self, weak actionSheet] in items.append(ActionSheetButtonItem(title: isScheduled ? self.presentationData.strings.VoiceChat_LeaveAndCancelVoiceChat : self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
if let strongSelf = self { if let strongSelf = self {
if let (members, _) = strongSelf.currentCallMembers, members.count >= 10 || true { if let (members, _) = strongSelf.currentCallMembers, members.count >= 10 || true {
let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: { let alertController = textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationTitle : strongSelf.presentationData.strings.VoiceChat_EndConfirmationTitle, text: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationText : strongSelf.presentationData.strings.VoiceChat_EndConfirmationText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: isScheduled ? strongSelf.presentationData.strings.VoiceChat_CancelConfirmationEnd : strongSelf.presentationData.strings.VoiceChat_EndConfirmationEnd, action: {
action() action()
})]) })])
strongSelf.controller?.present(alertController, in: .window(.root)) strongSelf.controller?.present(alertController, in: .window(.root))
@ -2716,11 +2736,31 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let inviteLinks = inviteLinks {
strongSelf.presentShare(inviteLinks) let callPeerId = strongSelf.call.peerId
} else if let addressName = strongSelf.peer?.addressName, !addressName.isEmpty { let _ = (strongSelf.context.account.postbox.transaction { transaction -> GroupCallInviteLinks? in
strongSelf.presentShare(GroupCallInviteLinks(listenerLink: "https://t.me/\(addressName)?voicechat", speakerLink: nil)) if let inviteLinks = inviteLinks {
return inviteLinks
} else if let peer = transaction.getPeer(callPeerId), let addressName = peer.addressName, !addressName.isEmpty {
return GroupCallInviteLinks(listenerLink: "https://t.me/\(addressName)?voicechat", speakerLink: nil)
} else if let cachedData = transaction.getPeerCachedData(peerId: callPeerId) {
if let cachedData = cachedData as? CachedChannelData, let link = cachedData.exportedInvitation?.link {
return GroupCallInviteLinks(listenerLink: link, speakerLink: nil)
} else if let cachedData = cachedData as? CachedGroupData, let link = cachedData.exportedInvitation?.link {
return GroupCallInviteLinks(listenerLink: link, speakerLink: nil)
}
}
return nil
} }
|> deliverOnMainQueue).start(next: { links in
guard let strongSelf = self else {
return
}
if let links = links {
strongSelf.presentShare(links)
}
})
}) })
return return
} }
@ -2987,11 +3027,7 @@ public final class VoiceChatController: ViewController {
if let navigationController = self.controller?.navigationController as? NavigationController { if let navigationController = self.controller?.navigationController as? NavigationController {
for controller in navigationController.viewControllers.reversed() { for controller in navigationController.viewControllers.reversed() {
if let controller = controller as? ChatController, case let .peer(peerId) = controller.chatLocation, peerId == self.call.peerId { if let controller = controller as? ChatController, case let .peer(peerId) = controller.chatLocation, peerId == self.call.peerId {
if self.callState?.scheduleTimestamp != nil { title = self.presentationData.strings.VoiceChat_Title
title = self.presentationData.strings.VoiceChat_ScheduledTitle
} else {
title = self.presentationData.strings.VoiceChat_Title
}
} }
} }
} }
@ -3004,7 +3040,7 @@ public final class VoiceChatController: ViewController {
if self.callState?.canManageCall ?? false { if self.callState?.canManageCall ?? false {
subtitle = self.presentationData.strings.VoiceChat_TapToEditTitle subtitle = self.presentationData.strings.VoiceChat_TapToEditTitle
} else { } else {
subtitle = "" subtitle = self.presentationData.strings.VoiceChat_Scheduled
} }
} }
@ -3429,14 +3465,19 @@ public final class VoiceChatController: ViewController {
} }
self.enqueuedTransitions.remove(at: 0) self.enqueuedTransitions.remove(at: 0)
if self.callState?.scheduleTimestamp != nil && self.listNode.alpha > 0.0 { if let callState = self.callState {
self.timerNode.isHidden = false if callState.scheduleTimestamp != nil && self.listNode.alpha > 0.0 {
self.listNode.alpha = 0.0 if !callState.canManageCall && (self.peer?.addressName?.isEmpty ?? true) {
self.listNode.isUserInteractionEnabled = false self.audioButton.isHidden = true
self.backgroundNode.backgroundColor = panelBackgroundColor }
self.updateIsFullscreen(false) self.timerNode.isHidden = false
} else if self.callState?.scheduleTimestamp == nil && !self.isScheduling && self.listNode.alpha == 0.0 { self.listNode.alpha = 0.0
self.transitionToCall() self.listNode.isUserInteractionEnabled = false
self.backgroundNode.backgroundColor = panelBackgroundColor
self.updateIsFullscreen(false)
} else if callState.scheduleTimestamp == nil && !self.isScheduling && self.listNode.alpha == 0.0 {
self.transitionToCall()
}
} }
var options = ListViewDeleteAndInsertOptions() var options = ListViewDeleteAndInsertOptions()
@ -3525,6 +3566,7 @@ public final class VoiceChatController: ViewController {
var processedPeerIds = Set<PeerId>() var processedPeerIds = Set<PeerId>()
var canInvite = true var canInvite = true
var inviteIsLink = false
if let peer = self.peer as? TelegramChannel { if let peer = self.peer as? TelegramChannel {
if peer.flags.contains(.isGigagroup) || (peer.addressName?.isEmpty ?? true) { if peer.flags.contains(.isGigagroup) || (peer.addressName?.isEmpty ?? true) {
if peer.flags.contains(.isCreator) || peer.adminRights != nil { if peer.flags.contains(.isCreator) || peer.adminRights != nil {
@ -3532,9 +3574,12 @@ public final class VoiceChatController: ViewController {
canInvite = false canInvite = false
} }
} }
if case .broadcast = peer.info, !(peer.addressName?.isEmpty ?? true) {
inviteIsLink = true
}
} }
if canInvite { if canInvite {
entries.append(.invite(self.presentationData.theme, self.presentationData.strings, self.presentationData.strings.VoiceChat_InviteMember)) entries.append(.invite(self.presentationData.theme, self.presentationData.strings, self.presentationData.strings.VoiceChat_InviteMember, inviteIsLink))
} }
for member in callMembers.0 { for member in callMembers.0 {
@ -4251,7 +4296,7 @@ public final class VoiceChatController: ViewController {
self.blocksBackgroundWhenInOverlay = true self.blocksBackgroundWhenInOverlay = true
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.statusBar.statusBarStyle = .Ignore self.statusBar.statusBarStyle = .Ignore

View File

@ -73,7 +73,7 @@ private final class VoiceChatInfoContextItemNode: ASDisplayNode, ContextMenuCust
let standardIconWidth: CGFloat = 32.0 let standardIconWidth: CGFloat = 32.0
var rightTextInset: CGFloat = sideInset var rightTextInset: CGFloat = sideInset
if !iconSize.width.isZero { if !iconSize.width.isZero {
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 8.0 rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0
} }
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude)) let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))