diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 328609b702..e8de89cdf3 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -283,6 +283,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return (data.isLockable, false) } + let previousEditingAndNetworkStateValue = Atomic<(Bool, AccountNetworkState)?>(value: nil) if !self.hideNetworkActivityStatus { self.titleDisposable = combineLatest(queue: .mainQueue(), context.account.networkState, @@ -298,13 +299,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } else { defaultTitle = strongSelf.presentationData.strings.ChatList_ArchivedChatsTitle } + let previousEditingAndNetworkState = previousEditingAndNetworkStateValue.swap((stateAndFilterId.state.editing, networkState)) if stateAndFilterId.state.editing { if strongSelf.groupId == .root { strongSelf.navigationItem.rightBarButtonItem = nil } - let title = !stateAndFilterId.state.selectedPeerIds.isEmpty ? strongSelf.presentationData.strings.ChatList_SelectedChats(Int32(stateAndFilterId.state.selectedPeerIds.count)) : defaultTitle - strongSelf.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) + + var animated = false + if let (previousEditing, previousNetworkState) = previousEditingAndNetworkState { + if previousEditing != stateAndFilterId.state.editing, previousNetworkState == networkState, case .online = networkState { + animated = true + } + } + strongSelf.titleView.setTitle(NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false), animated: animated) } else if isReorderingTabs { if strongSelf.groupId == .root { strongSelf.navigationItem.rightBarButtonItem = nil @@ -374,7 +382,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController case .updating: strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked) case .online: - strongSelf.titleView.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked) + strongSelf.titleView.setTitle(NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked), animated: (previousEditingAndNetworkState?.0 ?? false) != stateAndFilterId.state.editing) } if groupId == .root && filter == nil && checkProxy { if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil && strongSelf.navigationController?.topViewController === self { @@ -1413,10 +1421,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) editItem.accessibilityLabel = self.presentationData.strings.Common_Done if case .root = self.groupId, self.filter == nil { - self.navigationItem.leftBarButtonItem = editItem + self.navigationItem.setLeftBarButton(editItem, animated: true) (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring)) } else { - self.navigationItem.rightBarButtonItem = editItem + self.navigationItem.setRightBarButton(editItem, animated: true) (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.master, transition: .animated(duration: 0.5, curve: .spring)) } self.searchContentNode?.setIsEnabled(false, animated: true) @@ -1440,9 +1448,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) editItem.accessibilityLabel = self.presentationData.strings.Common_Edit if case .root = self.groupId, self.filter == nil { - self.navigationItem.leftBarButtonItem = editItem + self.navigationItem.setLeftBarButton(editItem, animated: true) } else { - self.navigationItem.rightBarButtonItem = editItem + self.navigationItem.setRightBarButton(editItem, animated: true) } (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring)) self.searchContentNode?.setIsEnabled(true, animated: true) diff --git a/submodules/ChatListUI/Sources/ChatListTitleView.swift b/submodules/ChatListUI/Sources/ChatListTitleView.swift index b2fac18119..88fb547588 100644 --- a/submodules/ChatListUI/Sources/ChatListTitleView.swift +++ b/submodules/ChatListUI/Sources/ChatListTitleView.swift @@ -24,30 +24,70 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl private var validLayout: (CGSize, CGRect)? - var title: NetworkStatusTitle = NetworkStatusTitle(text: "", activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) { - didSet { - if self.title != oldValue { - self.titleNode.attributedText = NSAttributedString(string: self.title.text, font: Font.bold(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) - self.buttonView.accessibilityLabel = self.title.text - self.activityIndicator.isHidden = !self.title.activity - if self.title.connectsViaProxy { - self.proxyNode.status = self.title.activity ? .connecting : .connected - } else { - self.proxyNode.status = .available - } - self.proxyNode.isHidden = !self.title.hasProxy - self.proxyButton.isHidden = !self.title.hasProxy - - self.buttonView.isHidden = !self.title.isPasscodeSet - if self.title.isPasscodeSet && !self.title.activity { - self.lockView.isHidden = false - } else { - self.lockView.isHidden = true - } - self.lockView.updateTheme(self.theme) - - self.setNeedsLayout() + private var _title: NetworkStatusTitle = NetworkStatusTitle(text: "", activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) + var title: NetworkStatusTitle { + get { + return self._title + } + set { + self.setTitle(newValue, animated: false) + } + } + + func setTitle(_ title: NetworkStatusTitle, animated: Bool) { + let oldValue = self._title + self._title = title + + if self._title != oldValue { + self.titleNode.attributedText = NSAttributedString(string: self.title.text, font: Font.bold(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) + self.buttonView.accessibilityLabel = self.title.text + self.activityIndicator.isHidden = !self.title.activity + + self.proxyButton.isHidden = !self.title.hasProxy + if self.title.connectsViaProxy { + self.proxyNode.status = self.title.activity ? .connecting : .connected + } else { + self.proxyNode.status = .available } + + let proxyIsHidden = !self.title.hasProxy + let previousProxyIsHidden = self.proxyNode.isHidden + if proxyIsHidden != previousProxyIsHidden { + if proxyIsHidden { + if let snapshotView = self.proxyNode.view.snapshotContentTree() { + snapshotView.frame = self.proxyNode.frame + self.proxyNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.proxyNode.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } else { + self.proxyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + self.proxyNode.isHidden = !self.title.hasProxy + + self.buttonView.isHidden = !self.title.isPasscodeSet + if self.title.isPasscodeSet && !self.title.activity { + if self.lockView.isHidden && animated { + self.lockView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + self.lockView.isHidden = false + } else { + if !self.lockView.isHidden && animated { + if let snapshotView = self.lockView.snapshotContentTree() { + snapshotView.frame = self.lockView.frame + self.lockView.superview?.insertSubview(snapshotView, aboveSubview: self.lockView) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.lockView.isHidden = true + } + self.lockView.updateTheme(self.theme) + + self.setNeedsLayout() } } @@ -178,11 +218,9 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl indicatorPadding = indicatorSize.width + 6.0 } var maxTitleWidth = clearBounds.size.width - indicatorPadding - var alignedTitleWidth = size.width - indicatorPadding var proxyPadding: CGFloat = 0.0 if !self.proxyNode.isHidden { maxTitleWidth -= 25.0 - alignedTitleWidth -= 20.0 proxyPadding += 39.0 } if !self.lockView.isHidden { @@ -197,18 +235,20 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl titleContentRect.origin.x = min(titleContentRect.origin.x, clearBounds.maxX - proxyPadding - titleContentRect.width) let titleFrame = titleContentRect - self.titleNode.frame = titleFrame + transition.updateFrame(node: self.titleNode, frame: titleFrame) - let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - proxyNode.bounds.height) / 2.0)), size: proxyNode.bounds.size) + let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - self.proxyNode.bounds.height) / 2.0)), size: self.proxyNode.bounds.size) self.proxyNode.frame = proxyFrame self.proxyButton.frame = proxyFrame.insetBy(dx: -2.0, dy: -2.0) let buttonX = max(0.0, titleFrame.minX - 10.0) self.buttonView.frame = CGRect(origin: CGPoint(x: buttonX, y: 0.0), size: CGSize(width: min(titleFrame.maxX + 28.0, size.width) - buttonX, height: size.height)) - self.lockView.frame = CGRect(x: titleFrame.maxX + 6.0, y: titleFrame.minY + 2.0, width: 2.0, height: 2.0) + let lockFrame = CGRect(x: titleFrame.maxX + 6.0, y: titleFrame.minY + 2.0, width: 2.0, height: 2.0) + transition.updateFrame(view: self.lockView, frame: lockFrame) - self.activityIndicator.frame = CGRect(origin: CGPoint(x: titleFrame.minX - indicatorSize.width - 4.0, y: titleFrame.minY - 1.0), size: indicatorSize) + let activityIndicatorFrame = CGRect(origin: CGPoint(x: titleFrame.minX - indicatorSize.width - 4.0, y: titleFrame.minY - 1.0), size: indicatorSize) + transition.updateFrame(node: self.activityIndicator, frame: activityIndicatorFrame) } @objc private func buttonPressed() { diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index bfdcc179a1..2642564f85 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -433,7 +433,32 @@ public extension ContainedViewLayoutTransition { } } - func updateFrame(view: UIView, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) { + func updateFrame(view: UIView, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + if frame.origin.x.isNaN { + return + } + if frame.origin.y.isNaN { + return + } + if frame.size.width.isNaN { + return + } + if frame.size.width < 0.0 { + return + } + if frame.size.height.isNaN { + return + } + if frame.size.height < 0.0 { + return + } + if !ASIsCGRectValidForLayout(CGRect(origin: CGPoint(), size: frame.size)) { + return + } + if !ASIsCGPositionValidForLayout(frame.origin) { + return + } + if view.frame.equalTo(frame) && !force { completion?(true) } else { @@ -444,9 +469,14 @@ public extension ContainedViewLayoutTransition { completion(true) } case let .animated(duration, curve): - let previousFrame = view.frame + let previousFrame: CGRect + if beginWithCurrentState, let presentation = view.layer.presentation() { + previousFrame = presentation.frame + } else { + previousFrame = view.frame + } view.frame = frame - view.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in + view.layer.animateFrame(from: previousFrame, to: frame, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in if let completion = completion { completion(result) } @@ -454,7 +484,7 @@ public extension ContainedViewLayoutTransition { } } } - + func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)? = nil) { if layer.frame.equalTo(frame) { completion?(true) diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 7308bd7c54..123e8c9534 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -606,9 +606,27 @@ open class NavigationBar: ASDisplayNode { self.rightButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } } else { + if self.rightButtonNode.view.superview != nil { + if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNode.frame + self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } self.rightButtonNode.removeFromSupernode() } } else { + if self.rightButtonNode.view.superview != nil { + if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNode.frame + self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } self.rightButtonNode.removeFromSupernode() } diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 75cd192064..46a894e8a8 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -1491,7 +1491,8 @@ private final class PlaybackButtonNode: HighlightTrackingButtonNode { strongSelf.textNode.alpha = 0.4 let transition: ContainedViewLayoutTransition = .animated(duration: 0.18, curve: .linear) - transition.updateTransformRotation(node: strongSelf.backgroundIconNode, angle: strongSelf.forward ? CGFloat.pi / 4.0 : -CGFloat.pi / 4.0) + let angle = CGFloat.pi / 4.0 + 0.226 + transition.updateTransformRotation(node: strongSelf.backgroundIconNode, angle: strongSelf.forward ? angle : -angle) } else if !strongSelf.isPressing { strongSelf.backgroundIconNode.alpha = 1.0 strongSelf.backgroundIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index d7d23de670..dba9aa2e02 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1278,7 +1278,24 @@ public final class VoiceChatController: ViewController { let context = strongSelf.context strongSelf.controller?.dismiss(completion: { Queue.mainQueue().justDispatch { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) + if peer.id.namespace == Namespaces.Peer.CloudUser { + let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peer.id) + |> take(1) + |> deliverOnMainQueue).start(next: { peer in + var expandAvatar = true + if peer.smallProfileImage == nil { + expandAvatar = false + } + if let (validLayout, _) = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet { + expandAvatar = false + } + if let strongSelf = self, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar, fromChat: false) { + navigationController.pushViewController(controller) + } + }) + } else { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) + } } }) @@ -2105,22 +2122,29 @@ public final class VoiceChatController: ViewController { return } - let _ = (strongSelf.call.leave(terminateIfPossible: true) - |> filter { $0 } - |> take(1) + strongSelf.leaveDisposable.set((strongSelf.call.leave(terminateIfPossible: true) |> deliverOnMainQueue).start(completed: { self?.controller?.dismiss() - }) + })) } let actionSheet = ActionSheetController(presentationData: self.presentationData.withUpdated(theme: self.darkTheme)) var items: [ActionSheetItem] = [] items.append(ActionSheetTextItem(title: self.presentationData.strings.VoiceChat_LeaveConfirmation)) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat, color: .destructive, action: { [weak actionSheet] in + items.append(ActionSheetButtonItem(title: self.presentationData.strings.VoiceChat_LeaveAndEndVoiceChat, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() - action() + if let strongSelf = self { + 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: { + action() + })]) + strongSelf.controller?.present(alertController, in: .window(.root)) + } else { + action() + } + } })) items.append(ActionSheetButtonItem(title: self.presentationData.strings.VoiceChat_LeaveVoiceChat, color: .accent, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 2974e2eee0..9f9cf814fd 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -1516,6 +1516,11 @@ public final class GroupCallParticipantsContext { strongSelf.isLoadingMore = false strongSelf.shouldResetStateFromServer = false var state = state + state.adminIds = strongSelf.stateValue.state.adminIds + state.isCreator = strongSelf.stateValue.state.isCreator + state.defaultParticipantsAreMuted = strongSelf.stateValue.state.defaultParticipantsAreMuted + state.title = strongSelf.stateValue.state.title + state.recordingStartTimestamp = strongSelf.stateValue.state.recordingStartTimestamp state.mergeActivity(from: strongSelf.stateValue.state, myPeerId: nil, previousMyPeerId: nil, mergeActivityTimestamps: false) strongSelf.stateValue.state = state strongSelf.endedProcessingUpdate()