mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-18 03:20:09 +00:00
Merge commit 'e0f95989d422e5318eeecc50a26ad7d5a2a43f52'
# Conflicts: # Telegram/Telegram-iOS/en.lproj/Localizable.strings
This commit is contained in:
commit
cb016384d0
@ -12643,3 +12643,6 @@ Sorry for the inconvenience.";
|
|||||||
"BotPreviews.DefaultFooter.Text" = "This preview will be shown by default. You can also add translations into specific languages.";
|
"BotPreviews.DefaultFooter.Text" = "This preview will be shown by default. You can also add translations into specific languages.";
|
||||||
"BotPreviews.SelectLanguage.Title" = "Add a Translation";
|
"BotPreviews.SelectLanguage.Title" = "Add a Translation";
|
||||||
"BotPreview.ViewContextDelete" = "Delete Preview";
|
"BotPreview.ViewContextDelete" = "Delete Preview";
|
||||||
|
|
||||||
|
"WebBrowser.Download.Confirmation" = "Do you want to download \"%@\"?";
|
||||||
|
"WebBrowser.Download.Download" = "Download";
|
||||||
|
@ -46,6 +46,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode",
|
"//submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode",
|
||||||
"//submodules/SearchUI",
|
"//submodules/SearchUI",
|
||||||
"//submodules/SearchBarNode",
|
"//submodules/SearchBarNode",
|
||||||
|
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -206,9 +206,9 @@ final class AddressBarContentComponent: Component {
|
|||||||
self.activated(true)
|
self.activated(true)
|
||||||
if let textField = self.textField {
|
if let textField = self.textField {
|
||||||
textField.becomeFirstResponder()
|
textField.becomeFirstResponder()
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().after(0.3, {
|
||||||
textField.selectAll(nil)
|
textField.selectAll(nil)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +238,9 @@ final class AddressBarContentComponent: Component {
|
|||||||
|
|
||||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||||
if let component = self.component {
|
if let component = self.component {
|
||||||
component.performAction.invoke(.navigateTo(explicitUrl(textField.text ?? "")))
|
let finalUrl = explicitUrl(textField.text ?? "")
|
||||||
|
// finalUrl = finalUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? finalUrl
|
||||||
|
component.performAction.invoke(.navigateTo(finalUrl))
|
||||||
}
|
}
|
||||||
textField.endEditing(true)
|
textField.endEditing(true)
|
||||||
return false
|
return false
|
||||||
@ -273,7 +275,12 @@ final class AddressBarContentComponent: Component {
|
|||||||
var title: String = ""
|
var title: String = ""
|
||||||
if let parsedUrl = URL(string: component.url) {
|
if let parsedUrl = URL(string: component.url) {
|
||||||
title = parsedUrl.host ?? component.url
|
title = parsedUrl.host ?? component.url
|
||||||
|
if title.hasPrefix("www.") {
|
||||||
|
title.removeSubrange(title.startIndex ..< title.index(title.startIndex, offsetBy: 4))
|
||||||
|
}
|
||||||
|
title = title.idnaDecoded ?? title
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, transition: transition)
|
self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, transition: transition)
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
@ -333,6 +340,7 @@ final class AddressBarContentComponent: Component {
|
|||||||
transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame)
|
transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame)
|
||||||
|
|
||||||
transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height)))
|
transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height)))
|
||||||
|
self.cancelButton.isUserInteractionEnabled = isActiveWithText
|
||||||
|
|
||||||
let textX: CGFloat = backgroundFrame.minX + sideInset
|
let textX: CGFloat = backgroundFrame.minX + sideInset
|
||||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||||
@ -431,7 +439,25 @@ final class AddressBarContentComponent: Component {
|
|||||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.text = self.component?.url ?? ""
|
var address = self.component?.url ?? ""
|
||||||
|
if let components = URLComponents(string: address) {
|
||||||
|
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||||
|
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||||
|
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||||
|
}
|
||||||
|
} else if let encodedHost = components.host {
|
||||||
|
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||||
|
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if textField.text != address {
|
||||||
|
textField.text = address
|
||||||
|
self.clearIconView.isHidden = address.isEmpty
|
||||||
|
self.clearIconButton.isHidden = address.isEmpty
|
||||||
|
self.placeholderContent.view?.isHidden = !address.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
||||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
||||||
|
@ -13,17 +13,20 @@ final class BrowserAddressListComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
|
let insets: UIEdgeInsets
|
||||||
let navigateTo: (String) -> Void
|
let navigateTo: (String) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
|
insets: UIEdgeInsets,
|
||||||
navigateTo: @escaping (String) -> Void
|
navigateTo: @escaping (String) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.insets = insets
|
||||||
self.navigateTo = navigateTo
|
self.navigateTo = navigateTo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +40,9 @@ final class BrowserAddressListComponent: Component {
|
|||||||
if lhs.strings !== rhs.strings {
|
if lhs.strings !== rhs.strings {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +154,7 @@ final class BrowserAddressListComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
self.endEditing(true)
|
self.window?.endEditing(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateScrolling(transition: ComponentTransition) {
|
private func updateScrolling(transition: ComponentTransition) {
|
||||||
@ -211,6 +217,7 @@ final class BrowserAddressListComponent: Component {
|
|||||||
theme: component.theme,
|
theme: component.theme,
|
||||||
style: .plain,
|
style: .plain,
|
||||||
title: sectionTitle,
|
title: sectionTitle,
|
||||||
|
insets: component.insets,
|
||||||
actionTitle: section.id == 0 ? "Clear" : nil,
|
actionTitle: section.id == 0 ? "Clear" : nil,
|
||||||
action: { [weak self] in
|
action: { [weak self] in
|
||||||
if let self, let component = self.component {
|
if let self, let component = self.component {
|
||||||
@ -292,6 +299,7 @@ final class BrowserAddressListComponent: Component {
|
|||||||
webPage: webPage!,
|
webPage: webPage!,
|
||||||
message: itemMessage,
|
message: itemMessage,
|
||||||
hasNext: true,
|
hasNext: true,
|
||||||
|
insets: component.insets,
|
||||||
action: {
|
action: {
|
||||||
if let url = webPage?.content.url {
|
if let url = webPage?.content.url {
|
||||||
navigateTo(url)
|
navigateTo(url)
|
||||||
@ -393,6 +401,7 @@ final class BrowserAddressListComponent: Component {
|
|||||||
webPage: TelegramMediaWebpage(webpageId: EngineMedia.Id(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: "https://telegram.org", displayUrl: "https://telegram.org", hash: 0, type: nil, websiteName: "Telegram", title: "Telegram Telegram", text: "Telegram", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil))),
|
webPage: TelegramMediaWebpage(webpageId: EngineMedia.Id(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: "https://telegram.org", displayUrl: "https://telegram.org", hash: 0, type: nil, websiteName: "Telegram", title: "Telegram Telegram", text: "Telegram", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil))),
|
||||||
message: nil,
|
message: nil,
|
||||||
hasNext: true,
|
hasNext: true,
|
||||||
|
insets: .zero,
|
||||||
action: {}
|
action: {}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
@ -16,6 +16,7 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
let webPage: TelegramMediaWebpage
|
let webPage: TelegramMediaWebpage
|
||||||
var message: Message?
|
var message: Message?
|
||||||
let hasNext: Bool
|
let hasNext: Bool
|
||||||
|
let insets: UIEdgeInsets
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -24,6 +25,7 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
webPage: TelegramMediaWebpage,
|
webPage: TelegramMediaWebpage,
|
||||||
message: Message?,
|
message: Message?,
|
||||||
hasNext: Bool,
|
hasNext: Bool,
|
||||||
|
insets: UIEdgeInsets,
|
||||||
action: @escaping () -> Void
|
action: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -31,6 +33,7 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
self.webPage = webPage
|
self.webPage = webPage
|
||||||
self.message = message
|
self.message = message
|
||||||
self.hasNext = hasNext
|
self.hasNext = hasNext
|
||||||
|
self.insets = insets
|
||||||
self.action = action
|
self.action = action
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +47,9 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
if lhs.hasNext != rhs.hasNext {
|
if lhs.hasNext != rhs.hasNext {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +98,7 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
|
|
||||||
let iconSize = CGSize(width: 40.0, height: 40.0)
|
let iconSize = CGSize(width: 40.0, height: 40.0)
|
||||||
let height: CGFloat = 60.0
|
let height: CGFloat = 60.0
|
||||||
let leftInset: CGFloat = 11.0 + iconSize.width + 11.0
|
let leftInset: CGFloat = component.insets.left + 11.0 + iconSize.width + 11.0
|
||||||
let rightInset: CGFloat = 16.0
|
let rightInset: CGFloat = 16.0
|
||||||
let titleSpacing: CGFloat = 2.0
|
let titleSpacing: CGFloat = 2.0
|
||||||
|
|
||||||
@ -181,7 +187,7 @@ final class BrowserAddressListItemComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let iconFrame = CGRect(origin: CGPoint(x: 11.0, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
|
let iconFrame = CGRect(origin: CGPoint(x: 11.0 + component.insets.left, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
|
||||||
|
|
||||||
let iconImageLayout = self.icon.asyncLayout()
|
let iconImageLayout = self.icon.asyncLayout()
|
||||||
var iconImageApply: (() -> Void)?
|
var iconImageApply: (() -> Void)?
|
||||||
|
@ -162,11 +162,13 @@ protocol BrowserContent: UIView {
|
|||||||
var present: (ViewController, Any?) -> Void { get set }
|
var present: (ViewController, Any?) -> Void { get set }
|
||||||
var presentInGlobalOverlay: (ViewController) -> Void { get set }
|
var presentInGlobalOverlay: (ViewController) -> Void { get set }
|
||||||
var getNavigationController: () -> NavigationController? { get set }
|
var getNavigationController: () -> NavigationController? { get set }
|
||||||
|
var openAppUrl: (String) -> Void { get set }
|
||||||
|
|
||||||
var minimize: () -> Void { get set }
|
var minimize: () -> Void { get set }
|
||||||
var close: () -> Void { get set }
|
var close: () -> Void { get set }
|
||||||
|
|
||||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void { get set }
|
var onScrollingUpdate: (ContentScrollingUpdate) -> Void { get set }
|
||||||
|
func resetScrolling()
|
||||||
|
|
||||||
func reload()
|
func reload()
|
||||||
func stop()
|
func stop()
|
||||||
@ -186,7 +188,7 @@ protocol BrowserContent: UIView {
|
|||||||
|
|
||||||
func addToRecentlyVisited()
|
func addToRecentlyVisited()
|
||||||
|
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition)
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition)
|
||||||
|
|
||||||
func makeContentSnapshotView() -> UIView?
|
func makeContentSnapshotView() -> UIView?
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import ShareController
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import UrlEscaping
|
import UrlEscaping
|
||||||
|
|
||||||
|
|
||||||
final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
@ -37,6 +36,7 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||||
|
var openAppUrl: (String) -> Void = { _ in }
|
||||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||||
var minimize: () -> Void = { }
|
var minimize: () -> Void = { }
|
||||||
var close: () -> Void = { }
|
var close: () -> Void = { }
|
||||||
@ -101,7 +101,7 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
|
|||||||
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
if let (size, insets, fullInsets) = self.validLayout {
|
if let (size, insets, fullInsets) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: .immediate)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||||
self.validLayout = (size, insets, fullInsets)
|
self.validLayout = (size, insets, fullInsets)
|
||||||
|
|
||||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.webView.scrollView.contentOffset.y, isDraggingOrDecelerating: self.webView.scrollView.isDragging || self.webView.scrollView.isDecelerating)
|
self.previousScrollingOffset = ScrollingOffsetState(value: self.webView.scrollView.contentOffset.y, isDraggingOrDecelerating: self.webView.scrollView.isDragging || self.webView.scrollView.isDecelerating)
|
||||||
@ -360,6 +360,10 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetScrolling() {
|
||||||
|
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||||
// self.currentError = nil
|
// self.currentError = nil
|
||||||
self.updateFontState(self.currentFontState, force: true)
|
self.updateFontState(self.currentFontState, force: true)
|
||||||
|
@ -66,6 +66,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
var currentAccessibilityAreas: [AccessibilityAreaNode] = []
|
var currentAccessibilityAreas: [AccessibilityAreaNode] = []
|
||||||
|
|
||||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||||
|
var openAppUrl: (String) -> Void = { _ in }
|
||||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||||
var minimize: () -> Void = { }
|
var minimize: () -> Void = { }
|
||||||
var close: () -> Void = { }
|
var close: () -> Void = { }
|
||||||
@ -300,7 +301,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
guard let (size, insets, fullInsets) = self.containerLayout else {
|
guard let (size, insets, fullInsets) = self.containerLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: transition)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reload() {
|
func reload() {
|
||||||
@ -374,11 +375,11 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: true)
|
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: transition.containedViewLayoutTransition)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: safeInsets, transition: transition.containedViewLayoutTransition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||||
self.containerLayout = (size, insets, fullInsets)
|
self.containerLayout = (size, insets, fullInsets)
|
||||||
|
|
||||||
var updateVisibleItems = false
|
var updateVisibleItems = false
|
||||||
@ -766,6 +767,10 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
self.readingProgress.set(readingProgress)
|
self.readingProgress.set(readingProgress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetScrolling() {
|
||||||
|
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
|
||||||
private func scrollableContentOffset(item: InstantPageScrollableItem) -> CGPoint {
|
private func scrollableContentOffset(item: InstantPageScrollableItem) -> CGPoint {
|
||||||
var contentOffset = CGPoint()
|
var contentOffset = CGPoint()
|
||||||
for (_, itemNode) in self.visibleItemsWithNodes {
|
for (_, itemNode) in self.visibleItemsWithNodes {
|
||||||
|
@ -35,6 +35,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
let readingProgress: CGFloat
|
let readingProgress: CGFloat
|
||||||
let loadingProgress: Double?
|
let loadingProgress: Double?
|
||||||
let collapseFraction: CGFloat
|
let collapseFraction: CGFloat
|
||||||
|
let activate: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
backgroundColor: UIColor,
|
backgroundColor: UIColor,
|
||||||
@ -50,7 +51,8 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?,
|
centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?,
|
||||||
readingProgress: CGFloat,
|
readingProgress: CGFloat,
|
||||||
loadingProgress: Double?,
|
loadingProgress: Double?,
|
||||||
collapseFraction: CGFloat
|
collapseFraction: CGFloat,
|
||||||
|
activate: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.separatorColor = separatorColor
|
self.separatorColor = separatorColor
|
||||||
@ -66,6 +68,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
self.readingProgress = readingProgress
|
self.readingProgress = readingProgress
|
||||||
self.loadingProgress = loadingProgress
|
self.loadingProgress = loadingProgress
|
||||||
self.collapseFraction = collapseFraction
|
self.collapseFraction = collapseFraction
|
||||||
|
self.activate = activate
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: BrowserNavigationBarComponent, rhs: BrowserNavigationBarComponent) -> Bool {
|
static func ==(lhs: BrowserNavigationBarComponent, rhs: BrowserNavigationBarComponent) -> Bool {
|
||||||
@ -122,6 +125,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
let leftItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
let leftItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||||
let rightItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
let rightItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||||
let centerItems = ChildMap(environment: BrowserNavigationBarEnvironment.self, keyedBy: AnyHashable.self)
|
let centerItems = ChildMap(environment: BrowserNavigationBarEnvironment.self, keyedBy: AnyHashable.self)
|
||||||
|
let activate = Child(Button.self)
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
var availableWidth = context.availableSize.width
|
var availableWidth = context.availableSize.width
|
||||||
@ -241,6 +245,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
if !leftItemList.isEmpty || !rightItemList.isEmpty {
|
if !leftItemList.isEmpty || !rightItemList.isEmpty {
|
||||||
availableWidth -= 20.0
|
availableWidth -= 20.0
|
||||||
}
|
}
|
||||||
|
availableWidth -= context.component.sideInset * 2.0
|
||||||
|
|
||||||
let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction)
|
let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction)
|
||||||
|
|
||||||
@ -266,6 +271,23 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.component.collapseFraction == 1.0 {
|
||||||
|
let activateAction = context.component.activate
|
||||||
|
let activate = activate.update(
|
||||||
|
component: Button(
|
||||||
|
content: AnyComponent(Rectangle(color: UIColor(rgb: 0x000000, alpha: 0.001))),
|
||||||
|
action: {
|
||||||
|
activateAction()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: size,
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(activate
|
||||||
|
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||||
|
var openAppUrl: (String) -> Void = { _ in }
|
||||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||||
var minimize: () -> Void = { }
|
var minimize: () -> Void = { }
|
||||||
var close: () -> Void = { }
|
var close: () -> Void = { }
|
||||||
@ -110,7 +111,7 @@ final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
if let (size, insets, fullInsets) = self.validLayout {
|
if let (size, insets, fullInsets) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: .immediate)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +250,7 @@ final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||||
self.validLayout = (size, insets, fullInsets)
|
self.validLayout = (size, insets, fullInsets)
|
||||||
|
|
||||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.scrollView.contentOffset.y, isDraggingOrDecelerating: self.scrollView.isDragging || self.scrollView.isDecelerating)
|
self.previousScrollingOffset = ScrollingOffsetState(value: self.scrollView.contentOffset.y, isDraggingOrDecelerating: self.scrollView.isDragging || self.scrollView.isDecelerating)
|
||||||
@ -352,6 +353,10 @@ final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetScrolling() {
|
||||||
|
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||||
// self.currentError = nil
|
// self.currentError = nil
|
||||||
self.updateFontState(self.currentFontState, force: true)
|
self.updateFontState(self.currentFontState, force: true)
|
||||||
|
@ -188,7 +188,10 @@ private final class BrowserScreenComponent: CombinedComponent {
|
|||||||
centerItem: navigationContent,
|
centerItem: navigationContent,
|
||||||
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
||||||
loadingProgress: context.component.contentState?.estimatedProgress,
|
loadingProgress: context.component.contentState?.estimatedProgress,
|
||||||
collapseFraction: collapseFraction
|
collapseFraction: collapseFraction,
|
||||||
|
activate: {
|
||||||
|
performAction.invoke(.expand)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
availableSize: context.availableSize,
|
availableSize: context.availableSize,
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
@ -258,6 +261,7 @@ private final class BrowserScreenComponent: CombinedComponent {
|
|||||||
context: context.component.context,
|
context: context.component.context,
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
|
insets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||||
navigateTo: { url in
|
navigateTo: { url in
|
||||||
performAction.invoke(.navigateTo(url))
|
performAction.invoke(.navigateTo(url))
|
||||||
}
|
}
|
||||||
@ -267,6 +271,7 @@ private final class BrowserScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(addressList
|
context.add(addressList
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0))
|
||||||
|
.clipsToBounds(true)
|
||||||
.appear(.default(alpha: true))
|
.appear(.default(alpha: true))
|
||||||
.disappear(.default(alpha: true))
|
.disappear(.default(alpha: true))
|
||||||
)
|
)
|
||||||
@ -314,6 +319,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
case openAddressBar
|
case openAddressBar
|
||||||
case closeAddressBar
|
case closeAddressBar
|
||||||
case navigateTo(String)
|
case navigateTo(String)
|
||||||
|
case expand
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate final class Node: ViewControllerTracingNode {
|
fileprivate final class Node: ViewControllerTracingNode {
|
||||||
@ -568,6 +574,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
updatedState.addressFocused = false
|
updatedState.addressFocused = false
|
||||||
return updatedState
|
return updatedState
|
||||||
})
|
})
|
||||||
|
case .expand:
|
||||||
|
if let content = self.content.last {
|
||||||
|
content.resetScrolling()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,6 +636,14 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
}
|
}
|
||||||
self.pushContent(content, transition: .spring(duration: 0.4))
|
self.pushContent(content, transition: .spring(duration: 0.4))
|
||||||
}
|
}
|
||||||
|
browserContent.openAppUrl = { [weak self] url in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: false, presentationData: self.presentationData, navigationController: self.controller?.navigationController as? NavigationController, dismissInput: { [weak self] in
|
||||||
|
self?.view.window?.endEditing(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
browserContent.present = { [weak self] c, a in
|
browserContent.present = { [weak self] c, a in
|
||||||
guard let self, let controller = self.controller else {
|
guard let self, let controller = self.controller else {
|
||||||
return
|
return
|
||||||
@ -989,6 +1007,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update.isReset {
|
||||||
|
scrollingPanelOffsetFraction = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
if scrollingPanelOffsetFraction != self.scrollingPanelOffsetFraction {
|
if scrollingPanelOffsetFraction != self.scrollingPanelOffsetFraction {
|
||||||
self.scrollingPanelOffsetFraction = scrollingPanelOffsetFraction
|
self.scrollingPanelOffsetFraction = scrollingPanelOffsetFraction
|
||||||
self.requestLayout(transition: transition)
|
self.requestLayout(transition: transition)
|
||||||
@ -1168,7 +1190,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let subject: Subject
|
private let subject: Subject
|
||||||
|
|
||||||
var openPreviousOnClose = false
|
private var openPreviousOnClose = false
|
||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
@ -1184,9 +1206,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
// "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
// "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||||
]
|
]
|
||||||
|
|
||||||
public init(context: AccountContext, subject: Subject) {
|
public init(context: AccountContext, subject: Subject, openPreviousOnClose: Bool = false) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
|
self.openPreviousOnClose = openPreviousOnClose
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
@ -1222,9 +1245,18 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) {
|
public func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) {
|
||||||
|
self.openPreviousOnClose = false
|
||||||
self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity)
|
self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
if self.openPreviousOnClose, let navigationController = self.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer, let controller = minimizedContainer.controllers.last {
|
||||||
|
navigationController.maximizeViewController(controller, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var isMinimized = false {
|
public var isMinimized = false {
|
||||||
didSet {
|
didSet {
|
||||||
if let webContent = self.node.content.last as? BrowserWebContent {
|
if let webContent = self.node.content.last as? BrowserWebContent {
|
||||||
@ -1340,7 +1372,7 @@ private final class BrowserContentComponent: Component {
|
|||||||
let insets = UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right)
|
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)
|
let fullInsets = UIEdgeInsets(top: component.insets.top + component.navigationBarHeight, left: component.insets.left, bottom: 49.0 + component.insets.bottom, right: component.insets.right)
|
||||||
|
|
||||||
component.content.updateLayout(size: availableSize, insets: insets, fullInsets: fullInsets, transition: transition)
|
component.content.updateLayout(size: availableSize, insets: insets, fullInsets: fullInsets, safeInsets: component.insets, transition: transition)
|
||||||
transition.setFrame(view: component.content, frame: CGRect(origin: .zero, size: availableSize))
|
transition.setFrame(view: component.content, frame: CGRect(origin: .zero, size: availableSize))
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
|
@ -18,6 +18,8 @@ import UndoUI
|
|||||||
import LottieComponent
|
import LottieComponent
|
||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
import UrlEscaping
|
import UrlEscaping
|
||||||
|
import UrlHandling
|
||||||
|
import SaveProgressScreen
|
||||||
|
|
||||||
private final class TonSchemeHandler: NSObject, WKURLSchemeHandler {
|
private final class TonSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||||
private final class PendingTask {
|
private final class PendingTask {
|
||||||
@ -115,11 +117,23 @@ private final class TonSchemeHandler: NSObject, WKURLSchemeHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class WebView: WKWebView {
|
||||||
|
var customBottomInset: CGFloat = 0.0 {
|
||||||
|
didSet {
|
||||||
|
self.setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var safeAreaInsets: UIEdgeInsets {
|
||||||
|
return UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.customBottomInset, right: 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
|
||||||
let webView: WKWebView
|
let webView: WebView
|
||||||
|
|
||||||
private let errorView: ComponentHostView<Empty>
|
private let errorView: ComponentHostView<Empty>
|
||||||
private var currentError: Error?
|
private var currentError: Error?
|
||||||
@ -139,6 +153,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
private let faviconDisposable = MetaDisposable()
|
private let faviconDisposable = MetaDisposable()
|
||||||
|
|
||||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||||
|
var openAppUrl: (String) -> Void = { _ in }
|
||||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||||
var minimize: () -> Void = { }
|
var minimize: () -> Void = { }
|
||||||
var close: () -> Void = { }
|
var close: () -> Void = { }
|
||||||
@ -155,23 +170,19 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
|
|
||||||
let configuration = WKWebViewConfiguration()
|
let configuration = WKWebViewConfiguration()
|
||||||
|
|
||||||
// let bundle = Bundle.main
|
|
||||||
// let bundleVersion = bundle.infoDictionary?["CFBundleShortVersionString"] ?? ""
|
|
||||||
//
|
|
||||||
var proxyServerHost = "magic.org"
|
var proxyServerHost = "magic.org"
|
||||||
if let data = context.currentAppConfiguration.with({ $0 }).data, let hostValue = data["ton_proxy_address"] as? String {
|
if let data = context.currentAppConfiguration.with({ $0 }).data, let hostValue = data["ton_proxy_address"] as? String {
|
||||||
proxyServerHost = hostValue
|
proxyServerHost = hostValue
|
||||||
}
|
}
|
||||||
configuration.setURLSchemeHandler(TonSchemeHandler(proxyServerHost: proxyServerHost), forURLScheme: "tonsite")
|
configuration.setURLSchemeHandler(TonSchemeHandler(proxyServerHost: proxyServerHost), forURLScheme: "tonsite")
|
||||||
configuration.allowsInlineMediaPlayback = true
|
configuration.allowsInlineMediaPlayback = true
|
||||||
// configuration.applicationNameForUserAgent = "Telegram-iOS/\(bundleVersion)"
|
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||||
configuration.mediaTypesRequiringUserActionForPlayback = []
|
configuration.mediaTypesRequiringUserActionForPlayback = []
|
||||||
} else {
|
} else {
|
||||||
configuration.mediaPlaybackRequiresUserAction = false
|
configuration.mediaPlaybackRequiresUserAction = false
|
||||||
}
|
}
|
||||||
|
|
||||||
self.webView = WKWebView(frame: CGRect(), configuration: configuration)
|
self.webView = WebView(frame: CGRect(), configuration: configuration)
|
||||||
self.webView.allowsLinkPreview = true
|
self.webView.allowsLinkPreview = true
|
||||||
|
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
@ -201,6 +212,9 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
|
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
|
self.webView.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
|
|
||||||
self.webView.allowsBackForwardNavigationGestures = true
|
self.webView.allowsBackForwardNavigationGestures = true
|
||||||
self.webView.scrollView.delegate = self
|
self.webView.scrollView.delegate = self
|
||||||
self.webView.scrollView.clipsToBounds = false
|
self.webView.scrollView.clipsToBounds = false
|
||||||
@ -214,7 +228,6 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoForward), options: [], context: nil)
|
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoForward), options: [], context: nil)
|
||||||
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.hasOnlySecureContent), options: [], context: nil)
|
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.hasOnlySecureContent), options: [], context: nil)
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
|
||||||
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
if #available(iOS 16.4, *) {
|
if #available(iOS 16.4, *) {
|
||||||
@ -244,8 +257,8 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
if let (size, insets, fullInsets) = self.validLayout {
|
if let (size, insets, fullInsets, safeInsets) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: .immediate)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: safeInsets, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,13 +446,13 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.webView.scrollView.setContentOffset(CGPoint(x: 0.0, y: -self.webView.scrollView.contentInset.top), animated: true)
|
self.webView.scrollView.setContentOffset(CGPoint(x: 0.0, y: -self.webView.scrollView.contentInset.top), animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets, UIEdgeInsets)?
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||||
self.validLayout = (size, insets, fullInsets)
|
self.validLayout = (size, insets, fullInsets, safeInsets)
|
||||||
|
|
||||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.webView.scrollView.contentOffset.y, isDraggingOrDecelerating: self.webView.scrollView.isDragging || self.webView.scrollView.isDecelerating)
|
self.previousScrollingOffset = ScrollingOffsetState(value: self.webView.scrollView.contentOffset.y, isDraggingOrDecelerating: self.webView.scrollView.isDragging || self.webView.scrollView.isDecelerating)
|
||||||
|
|
||||||
let webViewFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: size.width - insets.left - insets.right, height: size.height - insets.top - fullInsets.bottom))
|
let webViewFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: size.width - insets.left - insets.right, height: size.height - insets.top))
|
||||||
var refresh = false
|
var refresh = false
|
||||||
if self.webView.frame.width > 0 && webViewFrame.width != self.webView.frame.width {
|
if self.webView.frame.width > 0 && webViewFrame.width != self.webView.frame.width {
|
||||||
refresh = true
|
refresh = true
|
||||||
@ -450,6 +463,9 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.webView.reloadInputViews()
|
self.webView.reloadInputViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.webView.scrollView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: fullInsets.bottom, right: 0.0)
|
||||||
|
self.webView.customBottomInset = max(insets.bottom, safeInsets.bottom)
|
||||||
|
// self.webView.scrollView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 34.0, right: 0.0)
|
||||||
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
||||||
self.webView.scrollView.horizontalScrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
self.webView.scrollView.horizontalScrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
||||||
|
|
||||||
@ -460,11 +476,12 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
ErrorComponent(
|
ErrorComponent(
|
||||||
theme: self.presentationData.theme,
|
theme: self.presentationData.theme,
|
||||||
title: self.presentationData.strings.Browser_ErrorTitle,
|
title: self.presentationData.strings.Browser_ErrorTitle,
|
||||||
text: error.localizedDescription
|
text: error.localizedDescription,
|
||||||
|
insets: insets
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: size.width - insets.left - insets.right - 72.0, height: size.height)
|
containerSize: CGSize(width: size.width, height: size.height)
|
||||||
)
|
)
|
||||||
if self.errorView.superview == nil {
|
if self.errorView.superview == nil {
|
||||||
self.addSubview(self.errorView)
|
self.addSubview(self.errorView)
|
||||||
@ -521,14 +538,25 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
if !decelerate {
|
if !decelerate {
|
||||||
self.snapScrollingOffsetToInsets()
|
self.snapScrollingOffsetToInsets()
|
||||||
|
|
||||||
|
if self.ignoreUpdatesUntilScrollingStopped {
|
||||||
|
self.ignoreUpdatesUntilScrollingStopped = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
self.snapScrollingOffsetToInsets()
|
self.snapScrollingOffsetToInsets()
|
||||||
|
|
||||||
|
if self.ignoreUpdatesUntilScrollingStopped {
|
||||||
|
self.ignoreUpdatesUntilScrollingStopped = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateScrollingOffset(isReset: Bool, transition: ComponentTransition) {
|
private func updateScrollingOffset(isReset: Bool, transition: ComponentTransition) {
|
||||||
|
guard !self.ignoreUpdatesUntilScrollingStopped else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let scrollView = self.webView.scrollView
|
let scrollView = self.webView.scrollView
|
||||||
let isInteracting = scrollView.isDragging || scrollView.isDecelerating
|
let isInteracting = scrollView.isDragging || scrollView.isDecelerating
|
||||||
if let previousScrollingOffsetValue = self.previousScrollingOffset {
|
if let previousScrollingOffsetValue = self.previousScrollingOffset {
|
||||||
@ -558,6 +586,61 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var ignoreUpdatesUntilScrollingStopped = false
|
||||||
|
func resetScrolling() {
|
||||||
|
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||||
|
self.ignoreUpdatesUntilScrollingStopped = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
|
||||||
|
if #available(iOS 14.5, *), navigationAction.shouldPerformDownload {
|
||||||
|
self.presentDownloadConfirmation(fileName: navigationAction.request.mainDocumentURL?.lastPathComponent ?? "file", proceed: { download in
|
||||||
|
if download {
|
||||||
|
decisionHandler(.download, preferences)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.cancel, preferences)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if let url = navigationAction.request.url?.absoluteString {
|
||||||
|
if isTelegramMeLink(url) || isTelegraPhLink(url) {
|
||||||
|
decisionHandler(.cancel, preferences)
|
||||||
|
self.minimize()
|
||||||
|
self.openAppUrl(url)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow, preferences)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow, preferences)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
||||||
|
if navigationResponse.canShowMIMEType {
|
||||||
|
decisionHandler(.allow)
|
||||||
|
} else if #available(iOS 14.5, *) {
|
||||||
|
decisionHandler(.download)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||||
|
if let url = navigationAction.request.url?.absoluteString {
|
||||||
|
if isTelegramMeLink(url) || isTelegraPhLink(url) {
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
self.minimize()
|
||||||
|
self.openAppUrl(url)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||||
self.currentError = nil
|
self.currentError = nil
|
||||||
self.updateFontState(self.currentFontState, force: true)
|
self.updateFontState(self.currentFontState, force: true)
|
||||||
@ -578,8 +661,8 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
} else {
|
} else {
|
||||||
self.currentError = nil
|
self.currentError = nil
|
||||||
}
|
}
|
||||||
if let (size, insets, fullInsets) = self.validLayout {
|
if let (size, insets, fullInsets, safeInsets) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: .immediate)
|
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: safeInsets, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +679,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.close()
|
self.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
@available(iOS 15.0, *)
|
||||||
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
||||||
decisionHandler(.prompt)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
@ -699,12 +782,37 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
completionHandler(configuration)
|
completionHandler(configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentDownloadConfirmation(fileName: String, proceed: @escaping (Bool) -> Void) {
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
var completed = false
|
||||||
|
let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
if !completed {
|
||||||
|
completed = true
|
||||||
|
proceed(false)
|
||||||
|
}
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_Download_Download, action: {
|
||||||
|
if !completed {
|
||||||
|
completed = true
|
||||||
|
proceed(true)
|
||||||
|
}
|
||||||
|
})])
|
||||||
|
alertController.dismissed = { byOutsideTap in
|
||||||
|
if byOutsideTap {
|
||||||
|
if !completed {
|
||||||
|
completed = true
|
||||||
|
proceed(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.present(alertController, nil)
|
||||||
|
}
|
||||||
|
|
||||||
private func open(url: String, new: Bool) {
|
private func open(url: String, new: Bool) {
|
||||||
let subject: BrowserScreen.Subject = .webPage(url: url)
|
let subject: BrowserScreen.Subject = .webPage(url: url)
|
||||||
if new, let navigationController = self.getNavigationController() {
|
if new, let navigationController = self.getNavigationController() {
|
||||||
navigationController._keepModalDismissProgress = true
|
navigationController._keepModalDismissProgress = true
|
||||||
self.minimize()
|
self.minimize()
|
||||||
let controller = BrowserScreen(context: self.context, subject: subject)
|
let controller = BrowserScreen(context: self.context, subject: subject, openPreviousOnClose: true)
|
||||||
navigationController._keepModalDismissProgress = true
|
navigationController._keepModalDismissProgress = true
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
} else {
|
} else {
|
||||||
@ -881,15 +989,18 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let title: String
|
let title: String
|
||||||
let text: String
|
let text: String
|
||||||
|
let insets: UIEdgeInsets
|
||||||
|
|
||||||
init(
|
init(
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
title: String,
|
title: String,
|
||||||
text: String
|
text: String,
|
||||||
|
insets: UIEdgeInsets
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.insets = insets
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: ErrorComponent, rhs: ErrorComponent) -> Bool {
|
static func ==(lhs: ErrorComponent, rhs: ErrorComponent) -> Bool {
|
||||||
@ -902,10 +1013,14 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
if lhs.text != rhs.text {
|
if lhs.text != rhs.text {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
|
let background = Child(Rectangle.self)
|
||||||
let animation = Child(LottieComponent.self)
|
let animation = Child(LottieComponent.self)
|
||||||
let title = Child(MultilineTextComponent.self)
|
let title = Child(MultilineTextComponent.self)
|
||||||
let text = Child(MultilineTextComponent.self)
|
let text = Child(MultilineTextComponent.self)
|
||||||
@ -916,6 +1031,17 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
let animationSpacing: CGFloat = 8.0
|
let animationSpacing: CGFloat = 8.0
|
||||||
let textSpacing: CGFloat = 8.0
|
let textSpacing: CGFloat = 8.0
|
||||||
|
|
||||||
|
let constrainedWidth = context.availableSize.width - 76.0 - context.component.insets.left - context.component.insets.right
|
||||||
|
|
||||||
|
let background = background.update(
|
||||||
|
component: Rectangle(color: context.component.theme.list.plainBackgroundColor),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(background
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
let animation = animation.update(
|
let animation = animation.update(
|
||||||
component: LottieComponent(
|
component: LottieComponent(
|
||||||
content: LottieComponent.AppBundleContent(name: "ChatListNoResults")
|
content: LottieComponent.AppBundleContent(name: "ChatListNoResults")
|
||||||
@ -924,9 +1050,6 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
availableSize: CGSize(width: animationSize, height: animationSize),
|
availableSize: CGSize(width: animationSize, height: animationSize),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
context.add(animation
|
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + animation.size.height / 2.0))
|
|
||||||
)
|
|
||||||
contentHeight += animation.size.height + animationSpacing
|
contentHeight += animation.size.height + animationSpacing
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
@ -939,12 +1062,9 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
horizontalAlignment: .center
|
horizontalAlignment: .center
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
availableSize: context.availableSize,
|
availableSize: CGSize(width: constrainedWidth, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
context.add(title
|
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + title.size.height / 2.0))
|
|
||||||
)
|
|
||||||
contentHeight += title.size.height + textSpacing
|
contentHeight += title.size.height + textSpacing
|
||||||
|
|
||||||
let text = text.update(
|
let text = text.update(
|
||||||
@ -958,15 +1078,27 @@ private final class ErrorComponent: CombinedComponent {
|
|||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
availableSize: context.availableSize,
|
availableSize: CGSize(width: constrainedWidth, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
context.add(text
|
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + text.size.height / 2.0))
|
|
||||||
)
|
|
||||||
contentHeight += text.size.height
|
contentHeight += text.size.height
|
||||||
|
|
||||||
return CGSize(width: context.availableSize.width, height: contentHeight)
|
var originY = floor((context.availableSize.height - contentHeight) / 2.0)
|
||||||
|
context.add(animation
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + animation.size.height / 2.0))
|
||||||
|
)
|
||||||
|
originY += animation.size.height + animationSpacing
|
||||||
|
|
||||||
|
context.add(title
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + title.size.height / 2.0))
|
||||||
|
)
|
||||||
|
originY += title.size.height + textSpacing
|
||||||
|
|
||||||
|
context.add(text
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + text.size.height / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
return context.availableSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
317
submodules/BrowserUI/Sources/Punycode.swift
Normal file
317
submodules/BrowserUI/Sources/Punycode.swift
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
//
|
||||||
|
// Created by kojirof on 2018-11-19.
|
||||||
|
// Copyright (c) 2018 Gumob. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
//MIT License
|
||||||
|
//
|
||||||
|
//Copyright (c) 2018 Gumob
|
||||||
|
//
|
||||||
|
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
//of this software and associated documentation files (the "Software"), to deal
|
||||||
|
//in the Software without restriction, including without limitation the rights
|
||||||
|
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
//copies of the Software, and to permit persons to whom the Software is
|
||||||
|
//furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
//The above copyright notice and this permission notice shall be included in all
|
||||||
|
//copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
//SOFTWARE.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Punycode {
|
||||||
|
|
||||||
|
/// Punycode RFC 3492
|
||||||
|
/// See https://www.ietf.org/rfc/rfc3492.txt for standard details
|
||||||
|
|
||||||
|
private let base: Int = 36
|
||||||
|
private let tMin: Int = 1
|
||||||
|
private let tMax: Int = 26
|
||||||
|
private let skew: Int = 38
|
||||||
|
private let damp: Int = 700
|
||||||
|
private let initialBias: Int = 72
|
||||||
|
private let initialN: Int = 128
|
||||||
|
|
||||||
|
/// RFC 3492 specific
|
||||||
|
private let delimiter: Character = "-"
|
||||||
|
private let lowercase: ClosedRange<Character> = "a"..."z"
|
||||||
|
private let digits: ClosedRange<Character> = "0"..."9"
|
||||||
|
private let lettersBase: UInt32 = Character("a").unicodeScalars.first!.value
|
||||||
|
private let digitsBase: UInt32 = Character("0").unicodeScalars.first!.value
|
||||||
|
|
||||||
|
/// IDNA
|
||||||
|
private let ace: String = "xn--"
|
||||||
|
|
||||||
|
private func adaptBias(_ delta: Int, _ numberOfPoints: Int, _ firstTime: Bool) -> Int {
|
||||||
|
var delta: Int = delta
|
||||||
|
if firstTime {
|
||||||
|
delta /= damp
|
||||||
|
} else {
|
||||||
|
delta /= 2
|
||||||
|
}
|
||||||
|
delta += delta / numberOfPoints
|
||||||
|
var k: Int = 0
|
||||||
|
while delta > ((base - tMin) * tMax) / 2 {
|
||||||
|
delta /= base - tMin
|
||||||
|
k += base
|
||||||
|
}
|
||||||
|
return k + ((base - tMin + 1) * delta) / (delta + skew)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps a punycode character to index
|
||||||
|
private func punycodeIndex(for character: Character) -> Int? {
|
||||||
|
if lowercase.contains(character) {
|
||||||
|
return Int(character.unicodeScalars.first!.value - lettersBase)
|
||||||
|
} else if digits.contains(character) {
|
||||||
|
return Int(character.unicodeScalars.first!.value - digitsBase) + 26 /// count of lowercase letters range
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps an index to corresponding punycode character
|
||||||
|
private func punycodeValue(for digit: Int) -> Character? {
|
||||||
|
guard digit < base else { return nil }
|
||||||
|
if digit < 26 {
|
||||||
|
return Character(UnicodeScalar(lettersBase.advanced(by: digit))!)
|
||||||
|
} else {
|
||||||
|
return Character(UnicodeScalar(digitsBase.advanced(by: digit - 26))!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes punycode encoded string to original representation
|
||||||
|
///
|
||||||
|
/// - Parameter punycode: Punycode encoding (RFC 3492)
|
||||||
|
/// - Returns: Decoded string or nil if the input cannot be decoded
|
||||||
|
public func decodePunycode(_ punycode: Substring) -> String? {
|
||||||
|
var n: Int = initialN
|
||||||
|
var i: Int = 0
|
||||||
|
var bias: Int = initialBias
|
||||||
|
var output: [Character] = []
|
||||||
|
var inputPosition = punycode.startIndex
|
||||||
|
|
||||||
|
let delimiterPosition: Substring.Index = punycode.lastIndex(of: delimiter) ?? punycode.startIndex
|
||||||
|
if delimiterPosition > punycode.startIndex {
|
||||||
|
output.append(contentsOf: punycode[..<delimiterPosition])
|
||||||
|
inputPosition = punycode.index(after: delimiterPosition)
|
||||||
|
}
|
||||||
|
var punycodeInput: Substring = punycode[inputPosition..<punycode.endIndex]
|
||||||
|
while !punycodeInput.isEmpty {
|
||||||
|
let oldI: Int = i
|
||||||
|
var w: Int = 1
|
||||||
|
var k: Int = base
|
||||||
|
repeat {
|
||||||
|
let character: Character = punycodeInput.removeFirst()
|
||||||
|
guard let digit: Int = punycodeIndex(for: character) else {
|
||||||
|
return nil /// Failing on badly formatted punycode
|
||||||
|
}
|
||||||
|
i += digit * w
|
||||||
|
let t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias)
|
||||||
|
if digit < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
w *= base - t
|
||||||
|
k += base
|
||||||
|
} while !punycodeInput.isEmpty
|
||||||
|
bias = adaptBias(i - oldI, output.count + 1, oldI == 0)
|
||||||
|
n += i / (output.count + 1)
|
||||||
|
i %= (output.count + 1)
|
||||||
|
guard n >= 0x80, let scalar = UnicodeScalar(n) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
output.insert(Character(scalar), at: i)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes string to punycode (RFC 3492)
|
||||||
|
///
|
||||||
|
/// - Parameter input: Input string
|
||||||
|
/// - Returns: Punycode encoded string
|
||||||
|
public func encodePunycode(_ input: Substring) -> String? {
|
||||||
|
var n: Int = initialN
|
||||||
|
var delta: Int = 0
|
||||||
|
var bias: Int = initialBias
|
||||||
|
var output: String = ""
|
||||||
|
for scalar in input.unicodeScalars {
|
||||||
|
if scalar.isASCII {
|
||||||
|
let char = Character(scalar)
|
||||||
|
output.append(char)
|
||||||
|
} else if !scalar.isValid {
|
||||||
|
return nil /// Encountered a scalar out of acceptable range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var handled: Int = output.count
|
||||||
|
let basic: Int = handled
|
||||||
|
if basic > 0 {
|
||||||
|
output.append(delimiter)
|
||||||
|
}
|
||||||
|
while handled < input.unicodeScalars.count {
|
||||||
|
var minimumCodepoint: Int = 0x10FFFF
|
||||||
|
for scalar: Unicode.Scalar in input.unicodeScalars {
|
||||||
|
if scalar.value < minimumCodepoint && scalar.value >= n {
|
||||||
|
minimumCodepoint = Int(scalar.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delta += (minimumCodepoint - n) * (handled + 1)
|
||||||
|
n = minimumCodepoint
|
||||||
|
for scalar: Unicode.Scalar in input.unicodeScalars {
|
||||||
|
if scalar.value < n {
|
||||||
|
delta += 1
|
||||||
|
} else if scalar.value == n {
|
||||||
|
var q: Int = delta
|
||||||
|
var k: Int = base
|
||||||
|
while true {
|
||||||
|
let t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias)
|
||||||
|
if q < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
guard let character: Character = punycodeValue(for: t + ((q - t) % (base - t))) else { return nil }
|
||||||
|
output.append(character)
|
||||||
|
q = (q - t) / (base - t)
|
||||||
|
k += base
|
||||||
|
}
|
||||||
|
guard let character: Character = punycodeValue(for: q) else { return nil }
|
||||||
|
output.append(character)
|
||||||
|
bias = adaptBias(delta, handled + 1, handled == basic)
|
||||||
|
delta = 0
|
||||||
|
handled += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delta += 1
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing IDNA-encoded hostname
|
||||||
|
///
|
||||||
|
/// - Returns: IDNA encoded hostname or nil if the string can't be encoded
|
||||||
|
public func encodeIDNA(_ input: Substring) -> String? {
|
||||||
|
let parts: [Substring] = input.split(separator: ".")
|
||||||
|
var output: String = ""
|
||||||
|
for part: Substring in parts {
|
||||||
|
if output.count > 0 {
|
||||||
|
output.append(".")
|
||||||
|
}
|
||||||
|
if part.rangeOfCharacter(from: CharacterSet.urlHostAllowed.inverted) != nil {
|
||||||
|
guard let encoded: String = part.lowercased().punycodeEncoded else { return nil }
|
||||||
|
output += ace + encoded
|
||||||
|
} else {
|
||||||
|
output += part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing hostname decoded from IDNA representation
|
||||||
|
///
|
||||||
|
/// - Returns: Original hostname or nil if the string doesn't contain correct encoding
|
||||||
|
public func decodedIDNA(_ input: Substring) -> String? {
|
||||||
|
let parts: [Substring] = input.split(separator: ".")
|
||||||
|
var output: String = ""
|
||||||
|
for part: Substring in parts {
|
||||||
|
if output.count > 0 {
|
||||||
|
output.append(".")
|
||||||
|
}
|
||||||
|
if part.hasPrefix(ace) {
|
||||||
|
guard let decoded: String = part.dropFirst(ace.count).punycodeDecoded else { return nil }
|
||||||
|
output += decoded
|
||||||
|
} else {
|
||||||
|
output += part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Substring {
|
||||||
|
func lastIndex(of element: Character) -> String.Index? {
|
||||||
|
var position: Index = endIndex
|
||||||
|
while position > startIndex {
|
||||||
|
position = self.index(before: position)
|
||||||
|
if self[position] == element {
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension UnicodeScalar {
|
||||||
|
var isValid: Bool {
|
||||||
|
return value < 0xD880 || (value >= 0xE000 && value <= 0x1FFFFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Substring {
|
||||||
|
/// Returns new string in punycode encoding (RFC 3492)
|
||||||
|
///
|
||||||
|
/// - Returns: Punycode encoded string or nil if the string can't be encoded
|
||||||
|
var punycodeEncoded: String? {
|
||||||
|
return Punycode().encodePunycode(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string decoded from punycode representation (RFC 3492)
|
||||||
|
///
|
||||||
|
/// - Returns: Original string or nil if the string doesn't contain correct encoding
|
||||||
|
var punycodeDecoded: String? {
|
||||||
|
return Punycode().decodePunycode(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing IDNA-encoded hostname
|
||||||
|
///
|
||||||
|
/// - Returns: IDNA encoded hostname or nil if the string can't be encoded
|
||||||
|
var idnaEncoded: String? {
|
||||||
|
return Punycode().encodeIDNA(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing hostname decoded from IDNA representation
|
||||||
|
///
|
||||||
|
/// - Returns: Original hostname or nil if the string doesn't contain correct encoding
|
||||||
|
var idnaDecoded: String? {
|
||||||
|
return Punycode().decodedIDNA(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension String {
|
||||||
|
|
||||||
|
/// Returns new string in punycode encoding (RFC 3492)
|
||||||
|
///
|
||||||
|
/// - Returns: Punycode encoded string or nil if the string can't be encoded
|
||||||
|
var punycodeEncoded: String? {
|
||||||
|
return self[..<self.endIndex].punycodeEncoded
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string decoded from punycode representation (RFC 3492)
|
||||||
|
///
|
||||||
|
/// - Returns: Original string or nil if the string doesn't contain correct encoding
|
||||||
|
var punycodeDecoded: String? {
|
||||||
|
return self[..<self.endIndex].punycodeDecoded
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing IDNA-encoded hostname
|
||||||
|
///
|
||||||
|
/// - Returns: IDNA encoded hostname or nil if the string can't be encoded
|
||||||
|
var idnaEncoded: String? {
|
||||||
|
return self[..<self.endIndex].idnaEncoded
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns new string containing hostname decoded from IDNA representation
|
||||||
|
///
|
||||||
|
/// - Returns: Original hostname or nil if the string doesn't contain correct encoding
|
||||||
|
var idnaDecoded: String? {
|
||||||
|
return self[..<self.endIndex].idnaDecoded
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ final class SectionHeaderComponent: Component {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let style: Style
|
let style: Style
|
||||||
let title: String
|
let title: String
|
||||||
|
let insets: UIEdgeInsets
|
||||||
let actionTitle: String?
|
let actionTitle: String?
|
||||||
let action: (() -> Void)?
|
let action: (() -> Void)?
|
||||||
|
|
||||||
@ -20,12 +21,14 @@ final class SectionHeaderComponent: Component {
|
|||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
style: Style,
|
style: Style,
|
||||||
title: String,
|
title: String,
|
||||||
|
insets: UIEdgeInsets,
|
||||||
actionTitle: String?,
|
actionTitle: String?,
|
||||||
action: (() -> Void)?
|
action: (() -> Void)?
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.style = style
|
self.style = style
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.insets = insets
|
||||||
self.actionTitle = actionTitle
|
self.actionTitle = actionTitle
|
||||||
self.action = action
|
self.action = action
|
||||||
}
|
}
|
||||||
@ -40,6 +43,9 @@ final class SectionHeaderComponent: Component {
|
|||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.actionTitle != rhs.actionTitle {
|
if lhs.actionTitle != rhs.actionTitle {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -73,7 +79,7 @@ final class SectionHeaderComponent: Component {
|
|||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
let height: CGFloat = 28.0
|
let height: CGFloat = 28.0
|
||||||
let leftInset: CGFloat = 16.0
|
let leftInset: CGFloat = 16.0 + component.insets.left
|
||||||
let rightInset: CGFloat = 0.0
|
let rightInset: CGFloat = 0.0
|
||||||
|
|
||||||
let previousTitleFrame = self.title.view?.frame
|
let previousTitleFrame = self.title.view?.frame
|
||||||
|
@ -64,6 +64,7 @@ swift_library(
|
|||||||
"//submodules/WebsiteType",
|
"//submodules/WebsiteType",
|
||||||
"//submodules/UrlEscaping",
|
"//submodules/UrlEscaping",
|
||||||
"//submodules/DeviceLocationManager",
|
"//submodules/DeviceLocationManager",
|
||||||
|
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -48,6 +48,7 @@ import StickerPackEditTitleController
|
|||||||
import StickerPickerScreen
|
import StickerPickerScreen
|
||||||
import UIKitRuntimeUtils
|
import UIKitRuntimeUtils
|
||||||
import ImageObjectSeparation
|
import ImageObjectSeparation
|
||||||
|
import SaveProgressScreen
|
||||||
|
|
||||||
private let playbackButtonTag = GenericComponentViewTag()
|
private let playbackButtonTag = GenericComponentViewTag()
|
||||||
private let muteButtonTag = GenericComponentViewTag()
|
private let muteButtonTag = GenericComponentViewTag()
|
||||||
|
@ -30,6 +30,7 @@ swift_library(
|
|||||||
"//submodules/SaveToCameraRoll",
|
"//submodules/SaveToCameraRoll",
|
||||||
"//submodules/ShareController",
|
"//submodules/ShareController",
|
||||||
"//submodules/OpenInExternalAppUI",
|
"//submodules/OpenInExternalAppUI",
|
||||||
|
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -14,7 +14,7 @@ import ChatTitleView
|
|||||||
import BottomButtonPanelComponent
|
import BottomButtonPanelComponent
|
||||||
import UndoUI
|
import UndoUI
|
||||||
import MoreHeaderButton
|
import MoreHeaderButton
|
||||||
import MediaEditorScreen
|
import SaveProgressScreen
|
||||||
import SaveToCameraRoll
|
import SaveToCameraRoll
|
||||||
|
|
||||||
final class PeerInfoStoryGridScreenComponent: Component {
|
final class PeerInfoStoryGridScreenComponent: Component {
|
||||||
|
@ -14,7 +14,6 @@ import ChatTitleView
|
|||||||
import BottomButtonPanelComponent
|
import BottomButtonPanelComponent
|
||||||
import UndoUI
|
import UndoUI
|
||||||
import MoreHeaderButton
|
import MoreHeaderButton
|
||||||
import MediaEditorScreen
|
|
||||||
import SaveToCameraRoll
|
import SaveToCameraRoll
|
||||||
import ShareController
|
import ShareController
|
||||||
import OpenInExternalAppUI
|
import OpenInExternalAppUI
|
||||||
|
24
submodules/TelegramUI/Components/SaveProgressScreen/BUILD
Normal file
24
submodules/TelegramUI/Components/SaveProgressScreen/BUILD
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "SaveProgressScreen",
|
||||||
|
module_name = "SaveProgressScreen",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/Display",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/Components/ViewControllerComponent",
|
||||||
|
"//submodules/Components/MultilineTextComponent",
|
||||||
|
"//submodules/Components/BundleIconComponent",
|
||||||
|
"//submodules/Components/LottieAnimationComponent",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -98,6 +98,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stories/StoryQualityUpgradeSheetScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryQualityUpgradeSheetScreen",
|
||||||
"//submodules/TelegramUI/Components/SliderContextItem",
|
"//submodules/TelegramUI/Components/SliderContextItem",
|
||||||
"//submodules/TelegramUI/Components/InteractiveTextComponent",
|
"//submodules/TelegramUI/Components/InteractiveTextComponent",
|
||||||
|
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -43,6 +43,7 @@ import TelegramUIPreferences
|
|||||||
import StoryFooterPanelComponent
|
import StoryFooterPanelComponent
|
||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import SliderContextItem
|
import SliderContextItem
|
||||||
|
import SaveProgressScreen
|
||||||
|
|
||||||
public final class StoryAvailableReactions: Equatable {
|
public final class StoryAvailableReactions: Equatable {
|
||||||
let reactionItems: [ReactionItem]
|
let reactionItems: [ReactionItem]
|
||||||
|
@ -9,6 +9,11 @@ var uiWebview_SearchResultCount = 0;
|
|||||||
keyword - string to search
|
keyword - string to search
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function isElementVisible(element) {
|
||||||
|
var style = window.getComputedStyle(element);
|
||||||
|
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
|
||||||
|
}
|
||||||
|
|
||||||
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
|
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
|
||||||
if (element) {
|
if (element) {
|
||||||
if (element.nodeType == 3) { // Text node
|
if (element.nodeType == 3) { // Text node
|
||||||
@ -20,6 +25,8 @@ function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
|
|||||||
|
|
||||||
if (idx < 0) break;
|
if (idx < 0) break;
|
||||||
|
|
||||||
|
// if (!isElementVisible(element)) break;
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
|
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
|
||||||
}
|
}
|
||||||
@ -86,7 +93,7 @@ function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
|
|||||||
|
|
||||||
|
|
||||||
} else if (element.nodeType == 1) { // Element node
|
} else if (element.nodeType == 1) { // Element node
|
||||||
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
|
if (element.nodeName.toLowerCase() != 'select') {
|
||||||
for (var i=element.childNodes.length-1; i>=0; i--) {
|
for (var i=element.childNodes.length-1; i>=0; i--) {
|
||||||
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
|
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
|
||||||
}
|
}
|
||||||
|
@ -1008,11 +1008,12 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let urlScheme = (parsedUrl.scheme ?? "").lowercased()
|
||||||
var isInternetUrl = false
|
var isInternetUrl = false
|
||||||
if parsedUrl.scheme == "http" || parsedUrl.scheme == "https" {
|
if ["http", "https"].contains(urlScheme) {
|
||||||
isInternetUrl = true
|
isInternetUrl = true
|
||||||
}
|
}
|
||||||
if parsedUrl.scheme == "tonsite" {
|
if urlScheme == "tonsite" {
|
||||||
isInternetUrl = true
|
isInternetUrl = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,7 +1033,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
settings = .defaultSettings
|
settings = .defaultSettings
|
||||||
}
|
}
|
||||||
if accessChallengeData.data.isLockable {
|
if accessChallengeData.data.isLockable {
|
||||||
if passcodeSettings.autolockTimeout != nil && settings.defaultWebBrowser == nil {
|
if passcodeSettings.autolockTimeout != nil && settings.defaultWebBrowser == "inApp" {
|
||||||
settings = WebBrowserSettings(defaultWebBrowser: "safari", exceptions: [])
|
settings = WebBrowserSettings(defaultWebBrowser: "safari", exceptions: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user