Merge commit '8e219193d0cd6b681433e153503b6249b09fd35d'

This commit is contained in:
Isaac 2024-07-27 00:04:15 +08:00
commit 48dae263f6
9 changed files with 367 additions and 124 deletions

View File

@ -12593,9 +12593,7 @@ Sorry for the inconvenience.";
"AccessDenied.LocationWeather" = "Telegram needs access to your location so that you can add the weather widget to your stories.\n\nPlease go to Settings > Privacy > Location Services and set Telegram to ON."; "AccessDenied.LocationWeather" = "Telegram needs access to your location so that you can add the weather widget to your stories.\n\nPlease go to Settings > Privacy > Location Services and set Telegram to ON.";
"Story.Editor.TooltipWeatherLimitValue_1" = "**%@** weather stickers"; "Story.Editor.TooltipWeatherLimitText" = "You can't add more than one weather sticker to a story.";
"Story.Editor.TooltipWeatherLimitValue_any" = "**%@** weather stickers";
"Story.Editor.TooltipWeatherLimitText" = "You can't add more than %@ to a story.";
"WebBrowser.AddressPlaceholder" = "Enter URL"; "WebBrowser.AddressPlaceholder" = "Enter URL";

View File

@ -47,6 +47,7 @@ swift_library(
"//submodules/SearchUI", "//submodules/SearchUI",
"//submodules/SearchBarNode", "//submodules/SearchBarNode",
"//submodules/TelegramUI/Components/SaveProgressScreen", "//submodules/TelegramUI/Components/SaveProgressScreen",
"//submodules/TelegramUI/Components/ListActionItemComponent",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -10,6 +10,7 @@ import AccountContext
import TelegramPresentationData import TelegramPresentationData
import ContextUI import ContextUI
import UndoUI import UndoUI
import ListActionItemComponent
final class BrowserAddressListComponent: Component { final class BrowserAddressListComponent: Component {
let context: AccountContext let context: AccountContext
@ -69,6 +70,7 @@ final class BrowserAddressListComponent: Component {
var insets: UIEdgeInsets var insets: UIEdgeInsets
var itemHeight: CGFloat var itemHeight: CGFloat
var itemCount: Int var itemCount: Int
var hasMore: Bool
var totalHeight: CGFloat var totalHeight: CGFloat
@ -76,14 +78,21 @@ final class BrowserAddressListComponent: Component {
id: Int, id: Int,
insets: UIEdgeInsets, insets: UIEdgeInsets,
itemHeight: CGFloat, itemHeight: CGFloat,
itemCount: Int itemCount: Int,
hasMore: Bool
) { ) {
self.id = id self.id = id
self.insets = insets self.insets = insets
self.itemHeight = itemHeight self.itemHeight = itemHeight
self.itemCount = itemCount self.itemCount = itemCount
self.hasMore = hasMore
self.totalHeight = insets.top + itemHeight * CGFloat(itemCount) + insets.bottom var totalHeight = insets.top + itemHeight * CGFloat(itemCount) + insets.bottom
if hasMore {
totalHeight -= itemHeight
totalHeight += 44.0
}
self.totalHeight = totalHeight
} }
} }
@ -123,6 +132,7 @@ final class BrowserAddressListComponent: Component {
final class View: UIView, UIScrollViewDelegate { final class View: UIView, UIScrollViewDelegate {
struct State { struct State {
let recent: [TelegramMediaWebpage] let recent: [TelegramMediaWebpage]
let isRecentExpanded: Bool
let bookmarks: [Message] let bookmarks: [Message]
} }
@ -145,6 +155,7 @@ final class BrowserAddressListComponent: Component {
private var stateDisposable: Disposable? private var stateDisposable: Disposable?
private var stateValue: State? private var stateValue: State?
private let isRecentExpanded = ValuePromise<Bool>(false)
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -274,16 +285,28 @@ final class BrowserAddressListComponent: Component {
} }
for i in 0 ..< section.itemCount { for i in 0 ..< section.itemCount {
let itemFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight)) var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
if !visibleBounds.intersects(itemFrame) { if !visibleBounds.intersects(itemFrame) {
continue continue
} }
var isMore = false
if section.hasMore && i == 3 {
isMore = true
itemFrame.size.height = 44.0
}
var id: String = "" var id: String = ""
if section.id == 0 { if section.id == 0 {
id = "recent_\(state.recent[i].content.url ?? "")" id = "recent_\(state.recent[i].content.url ?? "")"
if isMore {
id = "recent_more"
}
} else if section.id == 1 { } else if section.id == 1 {
id = "bookmark_\(state.bookmarks[i].id.id)" id = "bookmark_\(state.bookmarks[i].id.id)"
if isMore {
id = "bookmark_more"
}
} }
let itemId = AnyHashable(id) let itemId = AnyHashable(id)
@ -301,99 +324,137 @@ final class BrowserAddressListComponent: Component {
self.visibleItems[itemId] = visibleItem self.visibleItems[itemId] = visibleItem
} }
var webPage: TelegramMediaWebpage? if isMore {
var itemMessage: Message? let _ = visibleItem.update(
transition: itemTransition,
if section.id == 0 { component: AnyComponent(
webPage = state.recent[i] ListActionItemComponent(
} else if section.id == 1 { theme: component.theme,
let message = state.bookmarks[i] title: AnyComponent(Text(
if let primaryUrl = getPrimaryUrl(message: message) { text: component.strings.WebBrowser_AddressBar_ShowMore,
if let media = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage { font: Font.regular(17.0),
webPage = media color: component.theme.list.itemAccentColor
)),
leftIcon: .custom(
AnyComponentWithIdentity(
id: "icon",
component: AnyComponent(Image(
image: PresentationResourcesItemList.downArrowImage(component.theme),
size: CGSize(width: 30.0, height: 30.0)
))
),
false
),
accessory: nil,
action: { [weak self] _ in
self?.isRecentExpanded.set(true)
},
highlighting: .default,
updateIsHighlighted: { view, _ in
})
),
environment: {},
containerSize: itemFrame.size
)
} else {
var webPage: TelegramMediaWebpage?
var itemMessage: Message?
if section.id == 0 {
webPage = state.recent[i]
} else if section.id == 1 {
let message = state.bookmarks[i]
if let primaryUrl = getPrimaryUrl(message: message) {
if let media = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage {
webPage = media
} else {
webPage = TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: primaryUrl, displayUrl: "", hash: 0, type: nil, websiteName: "", title: message.text, text: "", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil)))
}
itemMessage = message
} else { } else {
webPage = TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: primaryUrl, displayUrl: "", hash: 0, type: nil, websiteName: "", title: message.text, text: "", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil))) continue
} }
itemMessage = message
} else {
continue
} }
}
let performAction = component.performAction let performAction = component.performAction
let _ = visibleItem.update( let _ = visibleItem.update(
transition: itemTransition, transition: itemTransition,
component: AnyComponent( component: AnyComponent(
BrowserAddressListItemComponent( BrowserAddressListItemComponent(
context: component.context, context: component.context,
theme: component.theme, theme: component.theme,
webPage: webPage!, webPage: webPage!,
message: itemMessage, message: itemMessage,
hasNext: true, hasNext: true,
insets: component.insets, insets: component.insets,
action: { action: {
if let url = webPage?.content.url { if let url = webPage?.content.url {
performAction.invoke(.navigateTo(url)) performAction.invoke(.navigateTo(url))
}
},
contextAction: { [weak self] webPage, message, sourceView, gesture in
guard let self, let component = self.component, let url = webPage.content.url else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
var itemList: [ContextMenuItem] = []
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.default)
UIPasteboard.general.string = url
if let self, let component = self.component {
component.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
} }
}))) },
contextAction: { [weak self] webPage, message, sourceView, gesture in
if let message { guard let self, let component = self.component, let url = webPage.content.url else {
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in return
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
var itemList: [ContextMenuItem] = []
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.default)
UIPasteboard.general.string = url
if let self, let component = self.component { if let self, let component = self.component {
let _ = component.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone() component.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
} }
}))) })))
} else {
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_RemoveRecent, textColor: .destructive, icon: { theme in if let message {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in
}, action: { [weak self] _, f in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
f(.dismissWithoutContent) }, action: { [weak self] _, f in
f(.dismissWithoutContent)
if let self, let component = self.component, let url = webPage.content.url {
let _ = removeRecentlyVisitedLink(engine: component.context.engine, url: url).startStandalone() if let self, let component = self.component {
} let _ = component.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone()
}))) }
} })))
} else {
let items = ContextController.Items(content: .list(itemList)) itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_RemoveRecent, textColor: .destructive, icon: { theme in
let controller = ContextController( return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
presentationData: presentationData, }, action: { [weak self] _, f in
source: .extracted(BrowserAddressListContextExtractedContentSource(contentView: sourceView)), f(.dismissWithoutContent)
items: .single(items),
recognizer: nil, if let self, let component = self.component, let url = webPage.content.url {
gesture: gesture let _ = removeRecentlyVisitedLink(engine: component.context.engine, url: url).startStandalone()
) }
component.presentInGlobalOverlay(controller) })))
}) }
),
environment: {}, let items = ContextController.Items(content: .list(itemList))
containerSize: itemFrame.size let controller = ContextController(
) presentationData: presentationData,
source: .extracted(BrowserAddressListContextExtractedContentSource(contentView: sourceView)),
items: .single(items),
recognizer: nil,
gesture: gesture
)
component.presentInGlobalOverlay(controller)
})
),
environment: {},
containerSize: itemFrame.size
)
}
if let itemView = visibleItem.view { if let itemView = visibleItem.view {
if itemView.superview == nil { if itemView.superview == nil {
self.itemContainerView.addSubview(itemView) self.itemContainerView.addSubview(itemView)
if !transition.animation.isImmediate {
transition.animateAlpha(view: itemView, from: 0.0, to: 1.0)
}
} }
itemTransition.setFrame(view: itemView, frame: itemFrame) itemTransition.setFrame(view: itemView, frame: itemFrame)
} }
@ -447,8 +508,9 @@ final class BrowserAddressListComponent: Component {
if self.component == nil { if self.component == nil {
self.stateDisposable = combineLatest(queue: Queue.mainQueue(), self.stateDisposable = combineLatest(queue: Queue.mainQueue(),
recentlyVisitedLinks(engine: component.context.engine), recentlyVisitedLinks(engine: component.context.engine),
self.isRecentExpanded.get(),
component.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: component.context.account.peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil, tag: .tag(.webPage)) component.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: component.context.account.peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil, tag: .tag(.webPage))
).start(next: { [weak self] recent, view in ).start(next: { [weak self] recent, isRecentExpanded, view in
guard let self else { guard let self else {
return return
} }
@ -461,6 +523,7 @@ final class BrowserAddressListComponent: Component {
let isFirstTime = self.stateValue == nil let isFirstTime = self.stateValue == nil
self.stateValue = State( self.stateValue = State(
recent: recent, recent: recent,
isRecentExpanded: isRecentExpanded,
bookmarks: bookmarks bookmarks: bookmarks
) )
self.state?.updated(transition: isFirstTime ? .immediate : .easeInOut(duration: 0.25)) self.state?.updated(transition: isFirstTime ? .immediate : .easeInOut(duration: 0.25))
@ -510,11 +573,18 @@ final class BrowserAddressListComponent: Component {
var sections: [ItemLayout.Section] = [] var sections: [ItemLayout.Section] = []
if let state = self.stateValue { if let state = self.stateValue {
if !state.recent.isEmpty { if !state.recent.isEmpty {
var recentCount = state.recent.count
var hasMore = false
if recentCount > 4 && !state.isRecentExpanded {
recentCount = 4
hasMore = true
}
sections.append(ItemLayout.Section( sections.append(ItemLayout.Section(
id: 0, id: 0,
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0), insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
itemHeight: addressItemSize.height, itemHeight: addressItemSize.height,
itemCount: state.recent.count itemCount: recentCount,
hasMore: hasMore
)) ))
} }
if !state.bookmarks.isEmpty { if !state.bookmarks.isEmpty {
@ -522,7 +592,8 @@ final class BrowserAddressListComponent: Component {
id: 1, id: 1,
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0), insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
itemHeight: addressItemSize.height, itemHeight: addressItemSize.height,
itemCount: state.bookmarks.count itemCount: state.bookmarks.count,
hasMore: false
)) ))
} }
} }

View File

@ -775,12 +775,18 @@ public class BrowserScreen: ViewController, MinimizableController {
self.presentationState = f(self.presentationState) self.presentationState = f(self.presentationState)
self.requestLayout(transition: transition) self.requestLayout(transition: transition)
} }
func pushContent(_ content: BrowserScreen.Subject, transition: ComponentTransition) { func pushContent(_ content: BrowserScreen.Subject, transition: ComponentTransition) {
let browserContent: BrowserContent let browserContent: BrowserContent
switch content { switch content {
case let .webPage(url): case let .webPage(url):
browserContent = BrowserWebContent(context: self.context, presentationData: self.presentationData, url: url) let webContent = BrowserWebContent(context: self.context, presentationData: self.presentationData, url: url)
webContent.cancelInteractiveTransitionGestures = { [weak self] in
if let self, let view = self.controller?.view {
cancelInteractiveTransitionGestures(view: view)
}
}
browserContent = webContent
case let .instantPage(webPage, anchor, sourceLocation): case let .instantPage(webPage, anchor, sourceLocation):
let instantPageContent = BrowserInstantPageContent(context: self.context, presentationData: self.presentationData, webPage: webPage, anchor: anchor, url: webPage.content.url ?? "", sourceLocation: sourceLocation) let instantPageContent = BrowserInstantPageContent(context: self.context, presentationData: self.presentationData, webPage: webPage, anchor: anchor, url: webPage.content.url ?? "", sourceLocation: sourceLocation)
instantPageContent.openPeer = { [weak self] peer in instantPageContent.openPeer = { [weak self] peer in
@ -1582,3 +1588,19 @@ private final class BrowserContentComponent: Component {
return view.update(component: self, availableSize: availableSize, transition: transition) return view.update(component: self, availableSize: availableSize, transition: transition)
} }
} }
private func cancelInteractiveTransitionGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
for gesture in gestureRecognizers {
if let gesture = gesture as? InteractiveTransitionGestureRecognizer {
gesture.cancel()
} else if let scrollView = gesture.view as? UIScrollView, gesture.isEnabled, scrollView.tag == 0x5C4011 {
gesture.isEnabled = false
gesture.isEnabled = true
}
}
}
if let superview = view.superview {
cancelInteractiveTransitionGestures(view: superview)
}
}

View File

@ -127,6 +127,28 @@ final class WebView: WKWebView {
override var safeAreaInsets: UIEdgeInsets { override var safeAreaInsets: UIEdgeInsets {
return UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.customBottomInset, right: 0.0) return UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.customBottomInset, right: 0.0)
} }
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
var result = super.point(inside: point, with: event)
if !result && point.x > 0.0 && point.y < self.frame.width && point.y > 0.0 && point.y < self.frame.height + 83.0 {
result = true
}
return result
}
}
private class WeakScriptMessageHandler: NSObject, WKScriptMessageHandler {
private let f: (WKScriptMessage) -> ()
init(_ f: @escaping (WKScriptMessage) -> ()) {
self.f = f
super.init()
}
func userContentController(_ controller: WKUserContentController, didReceive scriptMessage: WKScriptMessage) {
self.f(scriptMessage)
}
} }
final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate { final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
@ -160,6 +182,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
var present: (ViewController, Any?) -> Void = { _, _ in } var present: (ViewController, Any?) -> Void = { _, _ in }
var presentInGlobalOverlay: (ViewController) -> Void = { _ in } var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
var getNavigationController: () -> NavigationController? = { return nil } var getNavigationController: () -> NavigationController? = { return nil }
var cancelInteractiveTransitionGestures: () -> Void = {}
private var tempFile: TempBoxFile? private var tempFile: TempBoxFile?
@ -185,8 +208,17 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
let contentController = WKUserContentController() let contentController = WKUserContentController()
let videoScript = WKUserScript(source: videoSource, injectionTime: .atDocumentStart, forMainFrameOnly: false) let videoScript = WKUserScript(source: videoSource, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(videoScript) contentController.addUserScript(videoScript)
let touchScript = WKUserScript(source: setupTouchObservers, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(touchScript)
configuration.userContentController = contentController configuration.userContentController = contentController
var handleScriptMessageImpl: ((WKScriptMessage) -> Void)?
let eventProxyScript = WKUserScript(source: eventProxySource, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(eventProxyScript)
contentController.add(WeakScriptMessageHandler { message in
handleScriptMessageImpl?(message)
}, name: "performAction")
self.webView = WebView(frame: CGRect(), configuration: configuration) self.webView = WebView(frame: CGRect(), configuration: configuration)
self.webView.allowsLinkPreview = true self.webView.allowsLinkPreview = true
@ -239,6 +271,29 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
self.webView.isInspectable = true self.webView.isInspectable = true
} }
self.addSubview(self.webView) self.addSubview(self.webView)
self.webView.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
if let self, self.webView.canGoBack {
return true
} else {
return false
}
}
self.webView.interactiveTransitionGestureRecognizerTest = { [weak self] point in
if let self {
if let result = self.webView.hitTest(point, with: nil), let scrollView = findScrollView(view: result), scrollView.isDescendant(of: self.webView) {
if scrollView.contentSize.width > scrollView.frame.width, scrollView.contentOffset.x > -scrollView.contentInset.left {
return true
}
}
}
return false
}
handleScriptMessageImpl = { [weak self] message in
self?.handleScriptMessage(message)
}
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -256,6 +311,22 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
self.faviconDisposable.dispose() self.faviconDisposable.dispose()
} }
private func handleScriptMessage(_ message: WKScriptMessage) {
guard let body = message.body as? [String: Any] else {
return
}
guard let eventName = body["eventName"] as? String else {
return
}
switch eventName {
case "cancellingTouch":
self.cancelInteractiveTransitionGestures()
default:
break
}
}
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData self.presentationData = presentationData
if #available(iOS 15.0, *) { if #available(iOS 15.0, *) {
@ -516,7 +587,6 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
self.updateState { $0.withUpdatedEstimatedProgress(self.webView.estimatedProgress) } self.updateState { $0.withUpdatedEstimatedProgress(self.webView.estimatedProgress) }
} else if keyPath == "canGoBack" { } else if keyPath == "canGoBack" {
self.updateState { $0.withUpdatedCanGoBack(self.webView.canGoBack) } self.updateState { $0.withUpdatedCanGoBack(self.webView.canGoBack) }
self.webView.disablesInteractiveTransitionGestureRecognizer = self.webView.canGoBack
} else if keyPath == "canGoForward" { } else if keyPath == "canGoForward" {
self.updateState { $0.withUpdatedCanGoForward(self.webView.canGoForward) } self.updateState { $0.withUpdatedCanGoForward(self.webView.canGoForward) }
} else if keyPath == "hasOnlySecureContent" { } else if keyPath == "hasOnlySecureContent" {
@ -1194,6 +1264,76 @@ function disconnectObserver() {
} }
""" """
let setupTouchObservers =
"""
(function() {
function saveOriginalCssProperties(element) {
while (element) {
const computedStyle = window.getComputedStyle(element);
const propertiesToSave = ['transform', 'top', 'left'];
element._originalProperties = {};
for (const property of propertiesToSave) {
element._originalProperties[property] = computedStyle.getPropertyValue(property);
}
element = element.parentElement;
}
}
function checkForCssChanges(element) {
while (element) {
if (!element._originalProperties) return false;
const computedStyle = window.getComputedStyle(element);
const modifiedProperties = ['transform', 'top', 'left'];
for (const property of modifiedProperties) {
if (computedStyle.getPropertyValue(property) !== element._originalProperties[property]) {
return true;
}
}
element = element.parentElement;
}
return false;
}
function clearOriginalCssProperties(element) {
while (element) {
delete element._originalProperties;
element = element.parentElement;
}
}
let touchedElement = null;
document.addEventListener('touchstart', function(event) {
touchedElement = event.target;
saveOriginalCssProperties(touchedElement);
}, { passive: true });
document.addEventListener('touchmove', function(event) {
if (checkForCssChanges(touchedElement)) {
TelegramWebviewProxy.postEvent("cancellingTouch", {})
console.log('CSS properties changed during touchmove');
}
}, { passive: true });
document.addEventListener('touchend', function() {
clearOriginalCssProperties(touchedElement);
touchedElement = null;
}, { passive: true });
})();
"""
private let eventProxySource = "var TelegramWebviewProxyProto = function() {}; " +
"TelegramWebviewProxyProto.prototype.postEvent = function(eventName, eventData) { " +
"window.webkit.messageHandlers.performAction.postMessage({'eventName': eventName, 'eventData': eventData}); " +
"}; " +
"var TelegramWebviewProxy = new TelegramWebviewProxyProto();"
@available(iOS 16.0, *) @available(iOS 16.0, *)
final class BrowserSearchOptions: UITextSearchOptions { final class BrowserSearchOptions: UITextSearchOptions {
override var wordMatchMethod: UITextSearchOptions.WordMatchMethod { override var wordMatchMethod: UITextSearchOptions.WordMatchMethod {
@ -1204,3 +1344,14 @@ final class BrowserSearchOptions: UITextSearchOptions {
return .caseInsensitive return .caseInsensitive
} }
} }
private func findScrollView(view: UIView?) -> UIScrollView? {
if let view = view {
if let view = view as? UIScrollView {
return view
}
return findScrollView(view: view.superview)
} else {
return nil
}
}

View File

@ -90,6 +90,7 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes
self.scrollNode.view.delaysContentTouches = false self.scrollNode.view.delaysContentTouches = false
self.scrollNode.view.clipsToBounds = false self.scrollNode.view.clipsToBounds = false
self.scrollNode.view.delegate = self.wrappedScrollViewDelegate self.scrollNode.view.delegate = self.wrappedScrollViewDelegate
self.scrollNode.view.tag = 0x5C4011
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in
guard let strongSelf = self, !strongSelf.isDismissed else { guard let strongSelf = self, !strongSelf.isDismissed else {

View File

@ -4654,7 +4654,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
return return
} }
let maxWeatherCount = 3 let maxWeatherCount = 1
var currentWeatherCount = 0 var currentWeatherCount = 0
self.entitiesView.eachView { entityView in self.entitiesView.eachView { entityView in
if entityView.entity is DrawingWeatherEntity { if entityView.entity is DrawingWeatherEntity {
@ -6290,12 +6290,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let context = self.context let context = self.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let limit: Int32 = 3
let value = presentationData.strings.Story_Editor_TooltipWeatherLimitValue(limit)
let content: UndoOverlayContent = .info( let content: UndoOverlayContent = .info(
title: nil, title: nil,
text: presentationData.strings.Story_Editor_TooltipWeatherLimitText(value).string, text: presentationData.strings.Story_Editor_TooltipWeatherLimitText.string,
timeout: nil, timeout: nil,
customUndoText: nil customUndoText: nil
) )

View File

@ -645,28 +645,30 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
) )
} }
let flashButton = flashButton.update( if !environment.metrics.isTablet {
component: CameraButton( let flashButton = flashButton.update(
content: flashContentComponent, component: CameraButton(
minSize: CGSize(width: 44.0, height: 44.0), content: flashContentComponent,
isExclusive: false, minSize: CGSize(width: 44.0, height: 44.0),
action: { [weak state] in isExclusive: false,
if let state { action: { [weak state] in
state.toggleFlashMode() if let state {
Queue.mainQueue().justDispatch { state.toggleFlashMode()
flashAction.invoke(Void()) Queue.mainQueue().justDispatch {
flashAction.invoke(Void())
}
} }
} }
} ),
), availableSize: availableSize,
availableSize: availableSize, transition: context.transition
transition: context.transition )
) context.add(flashButton
context.add(flashButton .position(CGPoint(x: flipButton.size.width + 8.0 + flashButton.size.width / 2.0 + 11.0, y: availableSize.height - flashButton.size.height / 2.0 - 8.0))
.position(CGPoint(x: flipButton.size.width + 8.0 + flashButton.size.width / 2.0 + 11.0, y: availableSize.height - flashButton.size.height / 2.0 - 8.0)) .appear(.default(scale: true, alpha: true))
.appear(.default(scale: true, alpha: true)) .disappear(.default(scale: true, alpha: true))
.disappear(.default(scale: true, alpha: true)) )
) }
} }
if showViewOnce { if showViewOnce {

View File

@ -413,12 +413,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
} }
func checkBotIdAndUrl(_ url: String) { func checkBotIdAndUrl(_ url: String) {
//1985737506 // if url.hasPrefix("https://walletbot.me"), let botId = self.controller?.botId.id._internalGetInt64Value(), botId != 1985737506 {
if url.hasPrefix("https://walletbot.me"), let botId = self.controller?.botId.id._internalGetInt64Value(), botId != 1985737506 { // let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: "Bot id mismatch, please report steps to app developer", actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: "Bot id mismatch, please report steps to app developer", actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { // })])
})]) // self.controller?.present(alertController, in: .window(.root))
self.controller?.present(alertController, in: .window(.root)) // }
}
} }
@objc fileprivate func mainButtonPressed() { @objc fileprivate func mainButtonPressed() {