Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "InstantPageCache",
module_name = "InstantPageCache",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/TelegramCore:TelegramCore",
"//submodules/Postbox:Postbox",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/PersistentStringHash:PersistentStringHash",
"//submodules/AccountContext:AccountContext",
"//submodules/UrlHandling:UrlHandling",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,54 @@
import Foundation
import UIKit
import SwiftSignalKit
import TelegramCore
import Postbox
import TelegramUIPreferences
import PersistentStringHash
public final class CachedInstantPage: Codable {
public let webPage: TelegramMediaWebpage
public let timestamp: Int32
public init(webPage: TelegramMediaWebpage, timestamp: Int32) {
self.webPage = webPage
self.timestamp = timestamp
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
let webPageData = try container.decode(AdaptedPostboxDecoder.RawObjectData.self, forKey: "webpage")
self.webPage = TelegramMediaWebpage(decoder: PostboxDecoder(buffer: MemoryBuffer(data: webPageData.data)))
self.timestamp = try container.decode(Int32.self, forKey: "timestamp")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(PostboxEncoder().encodeObjectToRawData(self.webPage), forKey: "webpage")
try container.encode(self.timestamp, forKey: "timestamp")
}
}
public func cachedInstantPage(engine: TelegramEngine, url: String) -> Signal<CachedInstantPage?, NoError> {
let key = ValueBoxKey(length: 8)
key.setInt64(0, value: Int64(bitPattern: url.persistentHashValue))
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, id: key))
|> map { entry -> CachedInstantPage? in
return entry?.get(CachedInstantPage.self)
}
}
public func updateCachedInstantPage(engine: TelegramEngine, url: String, webPage: TelegramMediaWebpage?) -> Signal<Never, NoError> {
let key = ValueBoxKey(length: 8)
key.setInt64(0, value: Int64(bitPattern: url.persistentHashValue))
if let webPage = webPage {
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, id: key, item: CachedInstantPage(webPage: webPage, timestamp: Int32(CFAbsoluteTimeGetCurrent())))
} else {
return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, id: key)
}
}
@@ -0,0 +1,100 @@
import Foundation
import SwiftSignalKit
import TelegramCore
import AccountContext
import UrlHandling
public func extractAnchor(string: String) -> (String, String?) {
var anchorValue: String?
if let anchorRange = string.range(of: "#") {
let anchor = string[anchorRange.upperBound...]
if !anchor.isEmpty {
anchorValue = String(anchor)
}
}
var trimmedUrl = string
if let anchor = anchorValue, let anchorRange = string.range(of: "#\(anchor)") {
let url = string[..<anchorRange.lowerBound]
if !url.isEmpty {
trimmedUrl = String(url)
}
}
return (trimmedUrl, anchorValue)
}
private let refreshTimeout: Int32 = 60 * 60 * 12
public func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var faqUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
faqUrl = "https://telegram.org/faq#general-questions"
}
return cachedInternalInstantPage(context: context, url: faqUrl)
}
public func cachedTermsPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var termsUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_Terms_URL
if termsUrl == "Settings.Terms_URL" || termsUrl.isEmpty {
termsUrl = "https://telegram.org/tos"
}
return cachedInternalInstantPage(context: context, url: termsUrl)
}
public func cachedPrivacyPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var privacyUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_PrivacyPolicy_URL
if privacyUrl == "Settings.PrivacyPolicy_URL" || privacyUrl.isEmpty {
privacyUrl = "https://telegram.org/privacy"
}
return cachedInternalInstantPage(context: context, url: privacyUrl)
}
public func cachedWebAppTermsPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var privacyUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.WebApp_TermsOfUse_URL
if privacyUrl == "WebApp.TermsOfUse_URL" || privacyUrl.isEmpty {
privacyUrl = "https://telegram.org/tos/mini-apps"
}
return cachedInternalInstantPage(context: context, url: privacyUrl)
}
private func cachedInternalInstantPage(context: AccountContext, url: String) -> Signal<ResolvedUrl, NoError> {
let (cachedUrl, anchor) = extractAnchor(string: url)
return cachedInstantPage(engine: context.engine, url: cachedUrl)
|> mapToSignal { cachedInstantPage -> Signal<ResolvedUrl, NoError> in
let updated = resolveInstantViewUrl(account: context.account, url: url)
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
guard case let .result(result) = result else {
return .complete()
}
return .single(result)
}
|> afterNext { result in
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
if instantPage.isComplete {
let _ = updateCachedInstantPage(engine: context.engine, url: cachedUrl, webPage: webPage).start()
} else {
let _ = (actualizedWebpage(account: context.account, webpage: webPage)
|> mapToSignal { webPage -> Signal<Never, NoError> in
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
return updateCachedInstantPage(engine: context.engine, url: cachedUrl, webPage: webPage)
} else {
return .complete()
}
}).start()
}
}
}
let now = Int32(CFAbsoluteTimeGetCurrent())
if let cachedInstantPage = cachedInstantPage, case let .Loaded(content) = cachedInstantPage.webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
let current: Signal<ResolvedUrl, NoError> = .single(.instantView(cachedInstantPage.webPage, anchor))
if now > cachedInstantPage.timestamp + refreshTimeout {
return current
|> then(updated)
} else {
return current
}
} else {
return updated
}
}
}