From 4e4d02aefcdd6f8d69c745af8cd02a620f278ee0 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 23 Sep 2023 20:16:34 +0400 Subject: [PATCH] Various fixes --- .../Sources/ChatListController.swift | 3 + ...SendMessageActionSheetControllerNode.swift | 28 ++-- .../Sources/PremiumLimitScreen.swift | 5 + .../Sources/StatsOverviewItem.swift | 2 +- .../StoryItemSetContainerComponent.swift | 3 + ...StoryItemSetContainerViewSendMessage.swift | 158 +++++++++++------- .../ChatTextInputAudioRecordingTimeNode.swift | 1 + .../TelegramUI/Sources/OpenResolvedUrl.swift | 19 ++- .../TelegramUI/Sources/TextLinkHandling.swift | 4 + 9 files changed, 144 insertions(+), 79 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 36cf241d44..a0b066fc5b 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -781,6 +781,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.storyProgressDisposable?.dispose() self.storiesPostingAvailabilityDisposable?.dispose() self.sharedOpenStoryProgressDisposable.dispose() + for (_, disposable) in self.preloadStoryResourceDisposables { + disposable.dispose() + } } private func updateNavigationMetadata() { diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift index 893d4ce88c..e5d88cd154 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift @@ -351,6 +351,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.scrollNode.view.contentInsetAdjustmentBehavior = .never } + self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) + if let snapshotView = self.sourceSendButton.view.snapshotView(afterScreenUpdates: false) { self.sendButtonNode.view.addSubview(snapshotView) } @@ -446,10 +448,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } self.textInputNode.textView.setContentOffset(self.textInputNode.textView.contentOffset, animated: false) - - UIView.animate(withDuration: 0.2, animations: { - self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) - }, completion: { _ in }) + + self.effectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.messageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) @@ -533,24 +533,32 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, var completedBubble = false var completedAlpha = false + var completed = false let intermediateCompletion: () -> Void = { [weak self] in - if completedEffect && completedButton && completedBubble && completedAlpha { + if completedEffect && completedButton && completedBubble && completedAlpha && !completed { + completed = true self?.textInputNode.isHidden = false self?.sourceSendButton.isHidden = false completion() } } - UIView.animate(withDuration: 0.2, animations: { - self.effectView.effect = nil - }, completion: { _ in + self.effectView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in completedEffect = true intermediateCompletion() }) - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in }) + Queue.mainQueue().after(0.45) { + if !completed { + completed = true + self.textInputNode.isHidden = false + self.sourceSendButton.isHidden = false + completion() + } + } + if self.animateInputField { if cancel { self.fromMessageTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, delay: 0.15, removeOnCompletion: false) @@ -561,7 +569,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, }) } else { self.textInputNode.isHidden = false - self.messageClipNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + self.messageClipNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in completedAlpha = true intermediateCompletion() }) diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 4d4d642a3c..f77b582081 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -1676,6 +1676,7 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { private let context: AccountContext private var action: (() -> Bool)? private let openPeer: (EnginePeer) -> Void + public var disposed: () -> Void = {} private let hapticFeedback = HapticFeedback() @@ -1707,6 +1708,10 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { fatalError("init(coder:) has not been implemented") } + deinit { + self.disposed() + } + public override func viewDidLoad() { super.viewDidLoad() diff --git a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift index 97272528be..5021d83779 100644 --- a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift +++ b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift @@ -230,7 +230,7 @@ class StatsOverviewItemNode: ListViewItemNode { if let stats = item.stats as? ChannelBoostStatus { topLeftValueLabelLayoutAndApply = makeTopLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(stats.level)", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - topRightValueLabelLayoutAndApply = makeTopRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(Int(stats.premiumAudience?.value ?? 0))", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + topRightValueLabelLayoutAndApply = makeTopRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "≈\(Int(stats.premiumAudience?.value ?? 0))", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) bottomLeftValueLabelLayoutAndApply = makeBottomLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(stats.boosts)", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index e89b4beb6c..0cb022cddf 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1382,6 +1382,9 @@ public final class StoryItemSetContainerComponent: Component { if self.sendMessageContext.menuController != nil { return .pause } + if self.sendMessageContext.progressPauseContext.hasExternalController { + return .pause + } if let navigationController = component.controller()?.navigationController as? NavigationController { let topViewController = navigationController.topViewController if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) && !(topViewController is ShareWithPeersScreen) && !(topViewController is AttachmentController) { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index a076c9457d..4e741492ec 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -69,6 +69,7 @@ final class StoryItemSetContainerSendMessage { weak var statusController: ViewController? weak var lookupController: UIViewController? weak var menuController: ViewController? + var progressPauseContext = StoryProgressPauseContext() var isViewingAttachedStickers = false var currentTooltipUpdateTimer: Foundation.Timer? @@ -2617,78 +2618,96 @@ final class StoryItemSetContainerSendMessage { guard let component = view.component, let navigationController = component.controller()?.navigationController as? NavigationController else { return } - let peerId = component.slice.peer.id - component.context.sharedContext.openResolvedUrl(result, context: component.context, urlContext: .chat(peerId: peerId, updatedPresentationData: nil), navigationController: navigationController, forceExternal: forceExternal, openPeer: { [weak self, weak view] peerId, navigation in - guard let self, let view, let component = view.component, let controller = component.controller() as? StoryContainerScreen else { + + self.progressPauseContext.update = { [weak self, weak view] controller in + guard let self, let view else { return } - - switch navigation { - case let .chat(_, subject, peekData): - if let navigationController = controller.navigationController as? NavigationController { - if case let .channel(channel) = peerId, channel.flags.contains(.isForum) { - controller.dismissWithoutTransitionOut() - component.context.sharedContext.navigateToForumChannel(context: component.context, peerId: peerId.id, navigationController: navigationController) - } else { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData, pushController: { [weak controller, weak navigationController] chatController, animated, completion in - guard let controller, let navigationController else { - return - } - if "".isEmpty { - navigationController.pushViewController(chatController) - } else { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(where: { $0 === controller }) { - viewControllers.insert(chatController, at: index) - } else { - viewControllers.append(chatController) - } - navigationController.setViewControllers(viewControllers, animated: animated) - } - })) - } + self.progressPauseContext.externalController = controller + view.updateIsProgressPaused() + } + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) + let peerId = component.slice.peer.id + component.context.sharedContext.openResolvedUrl( + result, + context: component.context, + urlContext: .chat(peerId: peerId, updatedPresentationData: updatedPresentationData), + navigationController: navigationController, + forceExternal: forceExternal, + openPeer: { [weak self, weak view] peerId, navigation in + guard let self, let view, let component = view.component, let controller = component.controller() as? StoryContainerScreen else { + return } - case .info: - self.navigationActionDisposable.set((component.context.account.postbox.loadedPeerWithId(peerId.id) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak view] peer in - guard let view, let component = view.component else { - return - } - if peer.restrictionText(platform: "ios", contentSettings: component.context.currentContentSettings.with { $0 }) == nil { - if let infoController = component.context.sharedContext.makePeerInfoController(context: component.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - component.controller()?.push(infoController) + + switch navigation { + case let .chat(_, subject, peekData): + if let navigationController = controller.navigationController as? NavigationController { + if case let .channel(channel) = peerId, channel.flags.contains(.isForum) { + controller.dismissWithoutTransitionOut() + component.context.sharedContext.navigateToForumChannel(context: component.context, peerId: peerId.id, navigationController: navigationController) + } else { + component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData, pushController: { [weak controller, weak navigationController] chatController, animated, completion in + guard let controller, let navigationController else { + return + } + if "".isEmpty { + navigationController.pushViewController(chatController) + } else { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === controller }) { + viewControllers.insert(chatController, at: index) + } else { + viewControllers.append(chatController) + } + navigationController.setViewControllers(viewControllers, animated: animated) + } + })) } } - })) - case let .withBotStartPayload(startPayload): - if let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), botStart: startPayload, keepStack: .always)) + case .info: + self.navigationActionDisposable.set((component.context.account.postbox.loadedPeerWithId(peerId.id) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak view] peer in + guard let view, let component = view.component else { + return + } + if peer.restrictionText(platform: "ios", contentSettings: component.context.currentContentSettings.with { $0 }) == nil { + if let infoController = component.context.sharedContext.makePeerInfoController(context: component.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + component.controller()?.push(infoController) + } + } + })) + case let .withBotStartPayload(startPayload): + if let navigationController = controller.navigationController as? NavigationController { + component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), botStart: startPayload, keepStack: .always)) + } + case let .withAttachBot(attachBotStart): + if let navigationController = controller.navigationController as? NavigationController { + component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), attachBotStart: attachBotStart)) + } + default: + break } - case let .withAttachBot(attachBotStart): - if let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), attachBotStart: attachBotStart)) + }, + sendFile: nil, + sendSticker: nil, + requestMessageActionUrlAuth: nil, + joinVoiceChat: nil, + present: { [weak view] c, a in + guard let view, let component = view.component, let controller = component.controller() else { + return } - default: - break - } - }, - sendFile: nil, - sendSticker: nil, - requestMessageActionUrlAuth: nil, - joinVoiceChat: nil, - present: { [weak view] c, a in - guard let view, let component = view.component, let controller = component.controller() else { - return - } - controller.present(c, in: .window(.root), with: a) - }, dismissInput: { [weak view] in - guard let view else { - return - } - view.endEditing(true) - }, - contentContext: nil + controller.present(c, in: .window(.root), with: a) + }, + dismissInput: { [weak view] in + guard let view else { + return + } + view.endEditing(true) + }, + contentContext: self.progressPauseContext ) } @@ -3397,3 +3416,12 @@ final class StoryItemSetContainerSendMessage { } } } + +public class StoryProgressPauseContext { + fileprivate weak var externalController: ViewController? + public fileprivate(set) var update: (ViewController?) -> Void = { _ in } + + var hasExternalController: Bool { + return self.externalController != nil + } +} diff --git a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift index 7fe344d6f4..415eba149f 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift @@ -103,6 +103,7 @@ final class ChatTextInputAudioRecordingTimeNode: ASDisplayNode { deinit { self.stateDisposable.dispose() + self.durationDisposable?.dispose() } func updateTheme(theme: PresentationTheme) { diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index bb3af9aeae..6401288d2b 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -836,6 +836,10 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur } }) case let .boost(peerId, status, canApplyStatus): + var forceDark = false + if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance { + forceDark = true + } let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in guard let peer, let status else { @@ -868,7 +872,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur var updateImpl: (() -> Void)? var dismissImpl: (() -> Void)? - let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), action: { + let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: { if isBoosted { return true } @@ -918,7 +922,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur return true } - let controller = textAlertController(sharedContext: context.sharedContext, title: title, text: text, actions: actions, parseMarkdown: true) + let controller = textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, title: title, text: text, actions: actions, parseMarkdown: true) present(controller, nil) } return dismiss @@ -928,12 +932,21 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur }) navigationController?.pushViewController(controller) + if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext { + storyProgressPauseContext.update(controller) + + let updateExternalController = storyProgressPauseContext.update + controller.disposed = { + updateExternalController(nil) + } + } + updateImpl = { [weak controller] in if let _ = status.nextLevelBoosts { let _ = context.engine.peers.applyChannelBoost(peerId: peerId).startStandalone() controller?.updateSubject(nextSubject, count: nextCount) } else { - controller?.dismiss() + dismissImpl?() } } dismissImpl = { [weak controller] in diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index 15fee27ab2..557ea1b578 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -90,6 +90,10 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peer, peekData in openResolvedPeerImpl(peer, .chat(textInputState: nil, subject: nil, peekData: peekData)) }, parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root)) + case .boost: + if let navigationController = controller.navigationController as? NavigationController { + openResolvedUrlImpl(result, context: context, urlContext: peerId.flatMap { .chat(peerId: $0, updatedPresentationData: nil) } ?? .generic, navigationController: navigationController, forceExternal: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, joinVoiceChat: nil, present: { c, a in }, dismissInput: {}, contentContext: nil) + } default: break }