Various fixes

This commit is contained in:
Ilya Laktyushin 2024-07-26 00:06:19 +02:00
parent aa5ad8e194
commit 332408acb7
5 changed files with 226 additions and 73 deletions

View file

@ -10,6 +10,9 @@ import TelegramPresentationData
import PhotoResources
import AccountContext
private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold)
private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radius: 6.0, color: UIColor(rgb: 0xFF9500))
final class BrowserAddressListItemComponent: Component {
let context: AccountContext
let theme: PresentationTheme
@ -57,6 +60,7 @@ final class BrowserAddressListItemComponent: Component {
private let containerButton: HighlightTrackingButton
private var emptyIcon: UIImageView?
private var emptyLabel: ComponentView<Empty>?
private var icon = TransformImageNode()
private let title = ComponentView<Empty>()
private let subtitle = ComponentView<Empty>()
@ -104,12 +108,14 @@ final class BrowserAddressListItemComponent: Component {
let title: String
let subtitle: String
var parsedUrl: URL?
var iconImageReferenceAndRepresentation: (AnyMediaReference, TelegramMediaImageRepresentation)?
var updateIconImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
if case let .Loaded(content) = component.webPage.content {
title = content.title ?? content.url
subtitle = content.url
parsedUrl = URL(string: content.url)
if let image = content.image {
if let representation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 80, height: 80)) {
@ -211,17 +217,58 @@ final class BrowserAddressListItemComponent: Component {
iconImageApply()
// if strongSelf.iconTextBackgroundNode.supernode != nil {
// strongSelf.iconTextBackgroundNode.removeFromSupernode()
// }
// if strongSelf.iconTextNode.supernode != nil {
// strongSelf.iconTextNode.removeFromSupernode()
// }
if let emptyIcon = self.emptyIcon {
self.emptyIcon = nil
emptyIcon.removeFromSuperview()
}
if let emptyLabel = self.emptyLabel {
self.emptyLabel = nil
emptyLabel.view?.removeFromSuperview()
}
} else {
if self.icon.supernode != nil {
self.icon.view.removeFromSuperview()
}
let icon: UIImageView
let label: ComponentView<Empty>
if let currentEmptyIcon = self.emptyIcon, let currentEmptyLabel = self.emptyLabel {
icon = currentEmptyIcon
label = currentEmptyLabel
} else {
icon = UIImageView()
icon.image = iconTextBackgroundImage
self.addSubview(icon)
label = ComponentView()
}
icon.frame = iconFrame
var iconText = ""
if let parsedUrl, let host = parsedUrl.host {
if parsedUrl.path.hasPrefix("/addstickers/") {
iconText = "S"
} else if parsedUrl.path.hasPrefix("/addemoji/") {
iconText = "E"
} else {
iconText = host[..<host.index(after: host.startIndex)].uppercased()
}
}
let labelSize = label.update(
transition: .immediate,
component: AnyComponent(Text(text: iconText, font: iconFont, color: .white)),
environment: {},
containerSize: iconSize
)
let labelFrame = CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - labelSize.width) / 2.0), y: iconFrame.minY + floorToScreenPixels((iconFrame.height - labelSize.height) / 2.0)), size: labelSize)
if let labelView = label.view {
if labelView.superview == nil {
self.addSubview(labelView)
}
labelView.frame = labelFrame
}
// if strongSelf.iconTextBackgroundNode.supernode == nil {
// strongSelf.iconTextBackgroundNode.image = applyIconTextBackgroundImage
// strongSelf.offsetContainerNode.addSubnode(strongSelf.iconTextBackgroundNode)

View file

@ -81,6 +81,7 @@ private final class BrowserScreenComponent: CombinedComponent {
let performHoldAction = context.component.performHoldAction
let isTablet = environment.metrics.isTablet
let canOpenIn = !(context.component.contentState?.url.hasPrefix("tonsite") ?? false)
let navigationContent: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?
var navigationLeftItems: [AnyComponentWithIdentity<Empty>]
@ -148,7 +149,7 @@ private final class BrowserScreenComponent: CombinedComponent {
)
)
]
if isTablet {
navigationLeftItems.append(
AnyComponentWithIdentity(
@ -273,24 +274,26 @@ private final class BrowserScreenComponent: CombinedComponent {
),
at: 0
)
navigationRightItems.append(
AnyComponentWithIdentity(
id: "openIn",
component: AnyComponent(
Button(
content: AnyComponent(
BundleIconComponent(
name: "Instant View/Browser",
tintColor: environment.theme.rootController.navigationBar.accentTextColor
)
),
action: {
performAction.invoke(.openIn)
}
if canOpenIn {
navigationRightItems.append(
AnyComponentWithIdentity(
id: "openIn",
component: AnyComponent(
Button(
content: AnyComponent(
BundleIconComponent(
name: "Instant View/Browser",
tintColor: environment.theme.rootController.navigationBar.accentTextColor
)
),
action: {
performAction.invoke(.openIn)
}
)
)
)
)
)
}
}
}
}
@ -349,6 +352,7 @@ private final class BrowserScreenComponent: CombinedComponent {
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
canGoBack: context.component.contentState?.canGoBack ?? false,
canGoForward: context.component.contentState?.canGoForward ?? false,
canOpenIn: canOpenIn,
performAction: performAction,
performHoldAction: performHoldAction
)
@ -395,6 +399,12 @@ private final class BrowserScreenComponent: CombinedComponent {
}
if context.component.presentationState.addressFocused {
let addressListSize: CGSize
if isTablet {
addressListSize = CGSize(width: 660.0, height: 420.0)
} else {
addressListSize = CGSize(width: context.availableSize.width, height: context.availableSize.height - navigationBar.size.height - toolbarSize)
}
let addressList = addressList.update(
component: BrowserAddressListComponent(
context: context.component.context,
@ -405,15 +415,26 @@ private final class BrowserScreenComponent: CombinedComponent {
performAction.invoke(.navigateTo(url))
}
),
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height - navigationBar.size.height - toolbarSize),
availableSize: addressListSize,
transition: context.transition
)
context.add(addressList
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0))
.clipsToBounds(true)
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
if isTablet {
context.add(addressList
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0 - 3.0))
.cornerRadius(10.0)
.clipsToBounds(true)
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
} else {
context.add(addressList
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0))
.clipsToBounds(true)
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
}
}
return context.availableSize
@ -992,7 +1013,7 @@ public class BrowserScreen: ViewController, MinimizableController {
let _ = (settings
|> deliverOnMainQueue).start(next: { [weak self] settings in
guard let self, let controller = self.controller, let contentState = self.contentState else {
guard let self, let controller = self.controller, let contentState = self.contentState, let layout = self.validLayout?.0 else {
return
}
@ -1072,22 +1093,26 @@ public class BrowserScreen: ViewController, MinimizableController {
})))
}
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Share, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
performAction.invoke(.share)
action(.default)
})))
if !layout.metrics.isTablet {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Share, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
performAction.invoke(.share)
action(.default)
})))
}
if [.webPage, .instantPage].contains(contentState.contentType) {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_AddBookmark, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
performAction.invoke(.addBookmark)
action(.default)
})))
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_OpenInBrowser(openInTitle).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] (controller, action) in
if let self {
self.context.sharedContext.applicationBindings.openUrl(openInUrl)
}
action(.default)
})))
if !layout.metrics.isTablet {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_OpenInBrowser(openInTitle).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] (controller, action) in
if let self {
self.context.sharedContext.applicationBindings.openUrl(openInUrl)
}
action(.default)
})))
}
}
let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))))
@ -1278,7 +1303,7 @@ public class BrowserScreen: ViewController, MinimizableController {
BrowserContentComponent(
content: content,
insets: UIEdgeInsets(
top: environment.statusBarHeight,
top: layout.statusBarHeight ?? 0.0,
left: layout.safeInsets.left,
bottom: layout.intrinsicInsets.bottom,
right: layout.safeInsets.right
@ -1401,6 +1426,16 @@ public class BrowserScreen: ViewController, MinimizableController {
}
}
public override func dismiss(completion: (() -> Void)? = nil) {
if let layout = self.validLayout, layout.metrics.isTablet {
self.node.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: layout.size.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in
super.dismiss(completion: completion)
})
} else {
super.dismiss(completion: completion)
}
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
@ -1519,7 +1554,7 @@ private final class BrowserContentComponent: Component {
}
let collapsedHeight: CGFloat = 24.0
let topInset: CGFloat = component.insets.top + component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + collapsedHeight * component.scrollingPanelOffsetFraction
let topInset: CGFloat = component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + (component.insets.top + collapsedHeight) * component.scrollingPanelOffsetFraction
let bottomInset = (49.0 + component.insets.bottom) * (1.0 - component.scrollingPanelOffsetFraction)
let insets = UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right)
let fullInsets = UIEdgeInsets(top: component.insets.top + component.navigationBarHeight, left: component.insets.left, bottom: 49.0 + component.insets.bottom, right: component.insets.right)

View file

@ -124,6 +124,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
let textColor: UIColor
let canGoBack: Bool
let canGoForward: Bool
let canOpenIn: Bool
let performAction: ActionSlot<BrowserScreen.Action>
let performHoldAction: (UIView, ContextGesture?, BrowserScreen.Action) -> Void
@ -132,6 +133,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
textColor: UIColor,
canGoBack: Bool,
canGoForward: Bool,
canOpenIn: Bool,
performAction: ActionSlot<BrowserScreen.Action>,
performHoldAction: @escaping (UIView, ContextGesture?, BrowserScreen.Action) -> Void
) {
@ -139,6 +141,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
self.textColor = textColor
self.canGoBack = canGoBack
self.canGoForward = canGoForward
self.canOpenIn = canOpenIn
self.performAction = performAction
self.performHoldAction = performHoldAction
}
@ -156,6 +159,9 @@ final class NavigationToolbarContentComponent: CombinedComponent {
if lhs.canGoForward != rhs.canGoForward {
return false
}
if lhs.canOpenIn != rhs.canOpenIn {
return false
}
return true
}
@ -170,10 +176,16 @@ final class NavigationToolbarContentComponent: CombinedComponent {
let availableSize = context.availableSize
let performAction = context.component.performAction
let performHoldAction = context.component.performHoldAction
let sideInset: CGFloat = 5.0
let buttonSize = CGSize(width: 50.0, height: availableSize.height)
let spacing = (availableSize.width - buttonSize.width * 5.0 - sideInset * 2.0) / 4.0
var buttonCount = 4
if context.component.canOpenIn {
buttonCount += 1
}
let spacing = (availableSize.width - buttonSize.width * CGFloat(buttonCount) - sideInset * 2.0) / CGFloat(buttonCount - 1)
let canGoBack = context.component.canGoBack
let back = back.update(
@ -269,24 +281,26 @@ final class NavigationToolbarContentComponent: CombinedComponent {
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width / 2.0, y: availableSize.height / 2.0))
)
let openIn = openIn.update(
component: Button(
content: AnyComponent(
BundleIconComponent(
name: "Instant View/Browser",
tintColor: context.component.accentColor
)
),
action: {
performAction.invoke(.openIn)
}
).minSize(buttonSize),
availableSize: buttonSize,
transition: .easeInOut(duration: 0.2)
)
context.add(openIn
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0))
)
if context.component.canOpenIn {
let openIn = openIn.update(
component: Button(
content: AnyComponent(
BundleIconComponent(
name: "Instant View/Browser",
tintColor: context.component.accentColor
)
),
action: {
performAction.invoke(.openIn)
}
).minSize(buttonSize),
availableSize: buttonSize,
transition: .easeInOut(duration: 0.2)
)
context.add(openIn
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0))
)
}
return availableSize
}

View file

@ -182,6 +182,11 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
configuration.mediaPlaybackRequiresUserAction = false
}
let contentController = WKUserContentController()
let videoScript = WKUserScript(source: videoSource, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(videoScript)
configuration.userContentController = contentController
self.webView = WebView(frame: CGRect(), configuration: configuration)
self.webView.allowsLinkPreview = true
@ -601,7 +606,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
if download {
decisionHandler(.download, preferences)
} else {
decisionHandler(.cancel, preferences)
// decisionHandler(.cancel, preferences)
}
})
} else {
@ -1147,6 +1152,48 @@ let setupFontFunctions = """
})();
"""
private let videoSource = """
function disableWebkitEnterFullscreen(videoElement) {
if (videoElement && videoElement.webkitEnterFullscreen) {
Object.defineProperty(videoElement, 'webkitEnterFullscreen', {
value: undefined
});
}
}
function disableFullscreenOnExistingVideos() {
document.querySelectorAll('video').forEach(disableWebkitEnterFullscreen);
}
function handleMutations(mutations) {
mutations.forEach((mutation) => {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach((newNode) => {
if (newNode.tagName === 'VIDEO') {
disableWebkitEnterFullscreen(newNode);
}
if (newNode.querySelectorAll) {
newNode.querySelectorAll('video').forEach(disableWebkitEnterFullscreen);
}
});
}
});
}
disableFullscreenOnExistingVideos();
const observer = new MutationObserver(handleMutations);
observer.observe(document.body, {
childList: true,
subtree: true
});
function disconnectObserver() {
observer.disconnect();
}
"""
@available(iOS 16.0, *)
final class BrowserSearchOptions: UITextSearchOptions {
override var wordMatchMethod: UITextSearchOptions.WordMatchMethod {

View file

@ -245,6 +245,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var botStart: ChatControllerInitialBotStart?
var attachBotStart: ChatControllerInitialAttachBotStart?
var botAppStart: ChatControllerInitialBotAppStart?
let mode: ChatControllerPresentationMode
let peerDisposable = MetaDisposable()
let titleDisposable = MetaDisposable()
@ -658,6 +659,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.botStart = botStart
self.attachBotStart = attachBotStart
self.botAppStart = botAppStart
self.mode = mode
self.peekData = peekData
self.currentChatListFilter = chatListFilter
self.chatNavigationStack = chatNavigationStack
@ -6548,6 +6550,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return value - 1
}
self.hasBrowserOrAppInFront.set(.single(false))
let deallocate: () -> Void = {
self.historyStateDisposable?.dispose()
self.messageIndexDisposable.dispose()
@ -7133,18 +7137,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
let hasBrowserOrWebAppInFront: Signal<Bool, NoError> = .single([])
|> then(
self.effectiveNavigationController?.viewControllersSignal ?? .single([])
)
|> map { controllers in
if controllers.last is BrowserScreen || controllers.last is AttachmentController {
return true
} else {
return false
if case .standard(.default) = self.mode {
let hasBrowserOrWebAppInFront: Signal<Bool, NoError> = .single([])
|> then(
self.effectiveNavigationController?.viewControllersSignal ?? .single([])
)
|> map { controllers in
if controllers.last is BrowserScreen || controllers.last is AttachmentController {
return true
} else {
return false
}
}
self.hasBrowserOrAppInFront.set(hasBrowserOrWebAppInFront)
}
self.hasBrowserOrAppInFront.set(hasBrowserOrWebAppInFront)
}
var returnInputViewFocus = false
@ -7634,6 +7640,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let _ = self.peekData {
self.peekTimerDisposable.set(nil)
}
if case .standard(.default) = self.mode {
self.hasBrowserOrAppInFront.set(.single(false))
}
}
func saveInterfaceState(includeScrollState: Bool = true) {