From 6b330351e186f3329904c29d2feed3c4f3d8660c Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 6 Nov 2025 12:53:18 +0400 Subject: [PATCH] Stories --- .../ChatListControllerStoryStealthMode.swift | 4 +- ...tworkFrameworkTcpConnectionInterface.swift | 16 +++--- .../Messages/TelegramEngineMessages.swift | 31 ++++++------ .../StoryLiveChatMessageComponent.swift | 6 ++- .../Sources/PinnedBarComponent.swift | 7 ++- .../StoryItemSetContainerComponent.swift | 3 ++ ...StoryItemSetContainerViewSendMessage.swift | 4 +- .../WebUI/Sources/WebAppController.swift | 50 +++++++++++++++++-- 8 files changed, 86 insertions(+), 35 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift b/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift index e50b7668d6..c1eb0cf9d5 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift @@ -37,9 +37,9 @@ extension ChatListControllerImpl { } ) tooltipScreen.tag = "no_auto_dismiss" - weak var tooltipScreenValue: UndoOverlayController? = tooltipScreen + let tooltipScreenValue: UndoOverlayController? = tooltipScreen self.currentTooltipUpdateTimer?.invalidate() - self.currentTooltipUpdateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in + self.currentTooltipUpdateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self, weak tooltipScreenValue] _ in guard let self else { return } diff --git a/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift b/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift index d17d399bb9..0a10e1c2a7 100644 --- a/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift +++ b/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift @@ -163,8 +163,8 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt connectTimeoutTimer.invalidate() } - weak var delegate = self.delegate - self.delegateQueue.async { + let delegate = self.delegate + self.delegateQueue.async { [weak delegate] in if let delegate = delegate { delegate.connectionInterfaceDidConnect() } @@ -221,9 +221,9 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt if requestChunkLength == 0 { self.currentReadRequest = nil - weak var delegate = self.delegate + let delegate = self.delegate let currentInterfaceIsWifi = self.currentInterfaceIsWifi - self.delegateQueue.async { + self.delegateQueue.async { [weak delegate] in if let delegate = delegate { delegate.connectionInterfaceDidRead(currentReadRequest.data, withTag: currentReadRequest.request.tag, networkType: currentInterfaceIsWifi ? 0 : 1) } @@ -249,8 +249,8 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt let tag = currentReadRequest.request.tag let readCount = data.count - weak var delegate = self.delegate - self.delegateQueue.async { + let delegate = self.delegate + self.delegateQueue.async { [weak delegate] in if let delegate = delegate { delegate.connectionInterfaceDidReadPartialData(ofLength: UInt(readCount), tag: tag) } @@ -279,8 +279,8 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt if !self.reportedDisconnection { self.reportedDisconnection = true - weak var delegate = self.delegate - self.delegateQueue.async { + let delegate = self.delegate + self.delegateQueue.async { [weak delegate] in if let delegate = delegate { delegate.connectionInterfaceDidDisconnectWithError(error) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index c5660f34e3..2bad40a011 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -1091,16 +1091,15 @@ public extension TelegramEngine { hasUnseen = peerState.maxReadId < lastEntry.id for item in itemsView.items { - if item.id > peerState.maxReadId { - unseenCount += 1 - - if case let .item(item) = item.value.get(Stories.StoredItem.self) { + if case let .item(item) = item.value.get(Stories.StoredItem.self) { + if item.id > peerState.maxReadId { + unseenCount += 1 if item.isCloseFriends { hasUnseenCloseFriends = true } - if item.media is TelegramMediaLiveStream { - hasLiveItems = true - } + } + if item.media is TelegramMediaLiveStream { + hasLiveItems = true } } } @@ -1176,8 +1175,8 @@ public extension TelegramEngine { } items.sort(by: { lhs, rhs in - let lhsUnseenOrPending = lhs.hasUnseen || lhs.hasPending - let rhsUnseenOrPending = rhs.hasUnseen || rhs.hasPending + let lhsUnseenOrPending = lhs.hasUnseen || lhs.hasLiveItems || lhs.hasPending + let rhsUnseenOrPending = rhs.hasUnseen || rhs.hasLiveItems || rhs.hasPending if lhsUnseenOrPending != rhsUnseenOrPending { if lhsUnseenOrPending { @@ -1255,7 +1254,7 @@ public extension TelegramEngine { let _ = accountPeer let _ = storiesStateView - var sortedItems: [(peer: Peer, item: Stories.Item, hasUnseen: Bool, lastTimestamp: Int32)] = [] + var sortedItems: [(peer: Peer, item: Stories.Item, hasUnseenOrLive: Bool, lastTimestamp: Int32)] = [] for peerId in storySubscriptionsView.peerIds { guard let peerView = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView else { @@ -1275,22 +1274,22 @@ public extension TelegramEngine { let lastTimestamp = itemsView.items.last?.value.get(Stories.StoredItem.self)?.timestamp let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self) - var hasUnseen = false + var hasUnseenOrLive = false if let peerState = peerState { - if let item = itemsView.items.first(where: { $0.id > peerState.maxReadId }) { - hasUnseen = true + if let item = itemsView.items.first(where: { $0.id > peerState.maxReadId || $0.isLiveStream }) { + hasUnseenOrLive = true nextItem = item.value.get(Stories.StoredItem.self) } } if let nextItem = nextItem, case let .item(item) = nextItem, let lastTimestamp = lastTimestamp { - sortedItems.append((peer, item, hasUnseen, lastTimestamp)) + sortedItems.append((peer, item, hasUnseenOrLive, lastTimestamp)) } } sortedItems.sort(by: { lhs, rhs in - if lhs.hasUnseen != rhs.hasUnseen { - if lhs.hasUnseen { + if lhs.hasUnseenOrLive != rhs.hasUnseenOrLive { + if lhs.hasUnseenOrLive { return true } else { return false diff --git a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift index e84c86f5d7..d9a7025fc8 100644 --- a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift +++ b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift @@ -419,6 +419,10 @@ public final class StoryLiveChatMessageComponent: Component { if textLayout.numberOfLines > 1 || (component.message.isFromAdmin && !displayStarsAmountBackground) { backgroundFrame.size.height = max(backgroundFrame.size.height, textFrame.maxY + 8.0 - backgroundOrigin.y) } + + if let starsAmountTextSize, !displayStarsAmountBackground, let lastLineRect = textLayout.linesRects().last, textFrame.minX + lastLineRect.maxX > backgroundFrame.width - 8.0 - starsAmountTextSize.width { + backgroundFrame.size.height += starsAmountTextSize.height + 2.0 + } } if let starsAmountTextSize, let starsAmountTextView = self.starsAmountText?.view, let starsAmountIcon = self.starsAmountIcon { @@ -431,7 +435,7 @@ public final class StoryLiveChatMessageComponent: Component { starsAmountTextFrame = CGRect(origin: CGPoint(x: starsAmountBackgroundFrame.maxX - starsAmountTextSize.width - 5.0, y: starsAmountBackgroundFrame.minY + UIScreenPixel + floor((starsAmountBackgroundFrame.height - starsAmountTextSize.height) * 0.5)), size: starsAmountTextSize) } else { - starsAmountTextFrame = CGRect(origin: CGPoint(x: textFrame.maxX - starsAmountTextSize.width - 1.0, y: textFrame.maxY - starsAmountTextSize.height + 1.0), size: starsAmountTextSize) + starsAmountTextFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - 8.0 - starsAmountTextSize.width, y: backgroundFrame.maxY - starsAmountTextSize.height - 8.0), size: starsAmountTextSize) } if starsAmountTextView.superview == nil { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift index 7855ec85b4..a06d2dda92 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift @@ -212,9 +212,12 @@ private final class PinnedBarMessageComponent: Component { self.backgroundView.tintColor = baseColor.withMultipliedBrightnessBy(0.7) self.foregroundView.tintColor = baseColor - let timestamp = CFAbsoluteTimeGetCurrent() + let timestamp = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 let currentDuration = max(0.0, timestamp - Double(component.message.date)) - let timeFraction: CGFloat = 1.0 - min(1.0, currentDuration / Double(component.message.lifetime)) + var timeFraction: CGFloat = 1.0 - min(1.0, currentDuration / Double(component.message.lifetime)) + if case .local = component.message.id.space { + timeFraction = 1.0 + } let backgroundFrame = CGRect(origin: CGPoint(), size: size) transition.setFrame(view: self.backgroundView, frame: backgroundFrame) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index da0110bc74..eff241602f 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1756,6 +1756,9 @@ public final class StoryItemSetContainerComponent: Component { displayFooter = true displayFooterViews = false } + if case .liveStream = component.slice.item.storyItem.media { + displayFooter = false + } if component.slice.item.storyItem.isForwardingDisabled { canShare = false } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index efae8cdad7..6bf6183e45 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -3529,9 +3529,9 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { } ) tooltipScreen.tag = "no_auto_dismiss" - weak var tooltipScreenValue: UndoOverlayController? = tooltipScreen + let tooltipScreenValue: UndoOverlayController? = tooltipScreen self.currentTooltipUpdateTimer?.invalidate() - self.currentTooltipUpdateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self, weak view] _ in + self.currentTooltipUpdateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self, weak view, weak tooltipScreenValue] _ in guard let self, let view, let component = view.component else { return } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 2219c4e942..b92d8db2bc 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -42,6 +42,7 @@ import Photos import GlassBarButtonComponent import BundleIconComponent import LottieComponent +import CryptoKit private let durgerKingBotIds: [Int64] = [5104055776, 2200339955] @@ -426,6 +427,47 @@ public final class WebAppController: ViewController, AttachmentContainable { webView.scrollView.insertSubview(self.topOverscrollNode.view, at: 0) } + private func load(url: URL) { + #if DEBUG + if "".isEmpty { + if #available(iOS 16.0, *) { + let documentsPath = URL.documentsDirectory.path(percentEncoded: false) + + var hasher = SHA256() + var urlString = url.absoluteString + if let range = urlString.firstRange(of: "#") { + urlString.removeSubrange(range.lowerBound...) + } + hasher.update(data: urlString.data(using: .utf8)!) + let digest = Data(hasher.finalize()) + let urlHash = hexString(digest) + + let cachedFilePath = documentsPath.appending("\(urlHash).bin") + + Task { + do { + let data: Data + if let cachedData = try? Data(contentsOf: URL(fileURLWithPath: cachedFilePath)) { + data = cachedData + print("Loaded from cache at \(cachedFilePath)") + } else { + let (loadedData, _) = try await URLSession.shared.data(from: url) + data = loadedData + try loadedData.write(to: URL(fileURLWithPath: cachedFilePath), options: .atomic) + } + self.webView?.load(data, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: url) + } catch let e { + print("\(e)") + } + } + } + + return + } + #endif + self.webView?.load(URLRequest(url: url)) + } + func setupWebView() { guard let controller = self.controller else { return @@ -434,7 +476,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if let url = controller.url, controller.source != .menu { self.queryId = controller.queryId if let parsedUrl = URL(string: url) { - self.webView?.load(URLRequest(url: parsedUrl)) + self.load(url: parsedUrl) } if let keepAliveSignal = controller.keepAliveSignal { self.keepAliveDisposable = (keepAliveSignal @@ -457,7 +499,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } if let parsedUrl = URL(string: result.url) { strongSelf.queryId = result.queryId - strongSelf.webView?.load(URLRequest(url: parsedUrl)) + strongSelf.load(url: parsedUrl) } }) } else { @@ -477,7 +519,7 @@ public final class WebAppController: ViewController, AttachmentContainable { return } self.controller?.titleView?.title = WebAppTitle(title: botApp.title, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: controller.botVerified) - self.webView?.load(URLRequest(url: parsedUrl)) + self.load(url: parsedUrl) }) }) } else { @@ -487,7 +529,7 @@ public final class WebAppController: ViewController, AttachmentContainable { return } strongSelf.queryId = result.queryId - strongSelf.webView?.load(URLRequest(url: parsedUrl)) + strongSelf.load(url: parsedUrl) if let keepAliveSignal = result.keepAliveSignal { strongSelf.keepAliveDisposable = (keepAliveSignal