mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Various improvements
This commit is contained in:
parent
3b5fc9afb3
commit
b4cc4e0bd0
@ -12518,4 +12518,14 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Bot.Settings" = "Bot Settings";
|
"Bot.Settings" = "Bot Settings";
|
||||||
|
|
||||||
"Browser.OpenInNewTap" = "";
|
"Browser.ErrorTitle" = "An Error Occurred";
|
||||||
|
|
||||||
|
"Browser.ContextMenu.Open" = "Open";
|
||||||
|
"Browser.ContextMenu.OpenInNewTab" = "Open in New Tab";
|
||||||
|
"Browser.ContextMenu.AddToReadingList" = "Add to Reading List";
|
||||||
|
"Browser.ContextMenu.CopyLink" = "Copy Link";
|
||||||
|
"Browser.ContextMenu.Share" = "Share";
|
||||||
|
|
||||||
|
"Monetization.Proceeds.Ton.Info" = "TON from your total balance can be used for ads or withdrawn as rewards 3 days after they are earned.";
|
||||||
|
"Monetization.Proceeds.Stars.Info" = "Stars from your total balance can be used for ads or withdrawn as rewards 21 days after they are earned.";
|
||||||
|
"Monetization.Proceeds.TonAndStars.Info" = "Stars and TON from your total balance can be used for ads or withdrawn as rewards 21 and 3 days respectively after they are earned.";
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import Display
|
|||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import WebKit
|
import WebKit
|
||||||
|
import TelegramPresentationData
|
||||||
|
|
||||||
final class BrowserContentState: Equatable {
|
final class BrowserContentState: Equatable {
|
||||||
enum ContentType: Equatable {
|
enum ContentType: Equatable {
|
||||||
@ -162,6 +163,7 @@ protocol BrowserContent: UIView {
|
|||||||
func navigateForward()
|
func navigateForward()
|
||||||
func navigateTo(historyItem: BrowserContentState.HistoryItem)
|
func navigateTo(historyItem: BrowserContentState.HistoryItem)
|
||||||
|
|
||||||
|
func updatePresentationData(_ presentationData: PresentationData)
|
||||||
func updateFontState(_ state: BrowserPresentationState.FontState)
|
func updateFontState(_ state: BrowserPresentationState.FontState)
|
||||||
|
|
||||||
func setSearch(_ query: String?, completion: ((Int) -> Void)?)
|
func setSearch(_ query: String?, completion: ((Int) -> Void)?)
|
||||||
|
|||||||
@ -24,8 +24,8 @@ import GalleryUI
|
|||||||
|
|
||||||
final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDelegate {
|
final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private let theme: InstantPageTheme
|
private var theme: InstantPageTheme
|
||||||
private let sourceLocation: InstantPageSourceLocation
|
private let sourceLocation: InstantPageSourceLocation
|
||||||
|
|
||||||
private var webPage: TelegramMediaWebpage?
|
private var webPage: TelegramMediaWebpage?
|
||||||
@ -87,11 +87,11 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
private var containerLayout: (size: CGSize, insets: UIEdgeInsets)?
|
private var containerLayout: (size: CGSize, insets: UIEdgeInsets)?
|
||||||
private var setupScrollOffsetOnLayout = false
|
private var setupScrollOffsetOnLayout = false
|
||||||
|
|
||||||
init(context: AccountContext, webPage: TelegramMediaWebpage, anchor: String?, url: String, sourceLocation: InstantPageSourceLocation) {
|
init(context: AccountContext, presentationData: PresentationData, webPage: TelegramMediaWebpage, anchor: String?, url: String, sourceLocation: InstantPageSourceLocation) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.webPage = webPage
|
self.webPage = webPage
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = presentationData
|
||||||
self.theme = instantPageThemeForType(.light, settings: .defaultSettings)
|
self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: .defaultSettings)
|
||||||
self.sourceLocation = sourceLocation
|
self.sourceLocation = sourceLocation
|
||||||
|
|
||||||
self.uuid = UUID()
|
self.uuid = UUID()
|
||||||
@ -171,6 +171,13 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
|
self.presentationData = presentationData
|
||||||
|
|
||||||
|
self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: .defaultSettings)
|
||||||
|
self.updatePageLayout()
|
||||||
|
}
|
||||||
|
|
||||||
func tapActionAtPoint(_ point: CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction {
|
func tapActionAtPoint(_ point: CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction {
|
||||||
if let currentLayout = self.currentLayout {
|
if let currentLayout = self.currentLayout {
|
||||||
for item in currentLayout.items {
|
for item in currentLayout.items {
|
||||||
|
|||||||
@ -455,6 +455,9 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
for content in self.content {
|
||||||
|
content.updatePresentationData(presentationData)
|
||||||
|
}
|
||||||
self.requestLayout(transition: .immediate)
|
self.requestLayout(transition: .immediate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -480,9 +483,9 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
let browserContent: BrowserContent
|
let browserContent: BrowserContent
|
||||||
switch content {
|
switch content {
|
||||||
case let .webPage(url):
|
case let .webPage(url):
|
||||||
browserContent = BrowserWebContent(context: self.context, url: url)
|
browserContent = BrowserWebContent(context: self.context, presentationData: self.presentationData, url: url)
|
||||||
case let .instantPage(webPage, anchor, sourceLocation):
|
case let .instantPage(webPage, anchor, sourceLocation):
|
||||||
let instantPageContent = BrowserInstantPageContent(context: self.context, webPage: webPage, anchor: anchor, url: webPage.content.url ?? "", sourceLocation: sourceLocation)
|
let instantPageContent = BrowserInstantPageContent(context: self.context, presentationData: self.presentationData, webPage: webPage, anchor: anchor, url: webPage.content.url ?? "", sourceLocation: sourceLocation)
|
||||||
instantPageContent.openPeer = { [weak self] peer in
|
instantPageContent.openPeer = { [weak self] peer in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -580,14 +583,15 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
self.controller?.title = state.title
|
self.controller?.title = state.title
|
||||||
self.contentState = state
|
self.contentState = state
|
||||||
|
|
||||||
let transition: ComponentTransition
|
if !self.isUpdating {
|
||||||
if let previousState, previousState.withUpdatedReadingProgress(state.readingProgress) == state {
|
let transition: ComponentTransition
|
||||||
transition = .immediate
|
if let previousState, previousState.withUpdatedReadingProgress(state.readingProgress) == state {
|
||||||
} else {
|
transition = .immediate
|
||||||
transition = .easeInOut(duration: 0.25)
|
} else {
|
||||||
|
transition = .easeInOut(duration: 0.25)
|
||||||
|
}
|
||||||
|
self.requestLayout(transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.requestLayout(transition: transition)
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
content.onScrollingUpdate = { [weak self] update in
|
content.onScrollingUpdate = { [weak self] update in
|
||||||
@ -823,13 +827,19 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
self.controller?.present(contextController, in: .window(.root))
|
self.controller?.present(contextController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isUpdating = false
|
||||||
func requestLayout(transition: ComponentTransition) {
|
func requestLayout(transition: ComponentTransition) {
|
||||||
if let (layout, navigationBarHeight) = self.validLayout {
|
if !self.isUpdating, let (layout, navigationBarHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
self.containerLayoutUpdated(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ComponentTransition) {
|
func containerLayoutUpdated(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ComponentTransition) {
|
||||||
|
self.isUpdating = true
|
||||||
|
defer {
|
||||||
|
self.isUpdating = false
|
||||||
|
}
|
||||||
|
|
||||||
self.validLayout = (layout, navigationBarHeight)
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
let environment = ViewControllerComponentContainer.Environment(
|
let environment = ViewControllerComponentContainer.Environment(
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import PromptUI
|
|||||||
import SafariServices
|
import SafariServices
|
||||||
import ShareController
|
import ShareController
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import LottieComponent
|
||||||
|
import MultilineTextComponent
|
||||||
|
|
||||||
private final class IpfsSchemeHandler: NSObject, WKURLSchemeHandler {
|
private final class IpfsSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||||
private final class PendingTask {
|
private final class PendingTask {
|
||||||
@ -88,9 +90,13 @@ private final class IpfsSchemeHandler: NSObject, WKURLSchemeHandler {
|
|||||||
|
|
||||||
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 let webView: WKWebView
|
private let webView: WKWebView
|
||||||
|
|
||||||
|
private let errorView: ComponentHostView<Empty>
|
||||||
|
private var currentError: Error?
|
||||||
|
|
||||||
let uuid: UUID
|
let uuid: UUID
|
||||||
|
|
||||||
private var _state: BrowserContentState
|
private var _state: BrowserContentState
|
||||||
@ -112,9 +118,10 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
||||||
var getNavigationController: () -> NavigationController? = { return nil }
|
var getNavigationController: () -> NavigationController? = { return nil }
|
||||||
|
|
||||||
init(context: AccountContext, url: String) {
|
init(context: AccountContext, presentationData: PresentationData, url: String) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.uuid = UUID()
|
self.uuid = UUID()
|
||||||
|
self.presentationData = presentationData
|
||||||
|
|
||||||
let configuration = WKWebViewConfiguration()
|
let configuration = WKWebViewConfiguration()
|
||||||
|
|
||||||
@ -126,7 +133,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.webView = WKWebView(frame: CGRect(), configuration: configuration)
|
self.webView = WKWebView(frame: CGRect(), configuration: configuration)
|
||||||
self.webView.allowsLinkPreview = true
|
self.webView.allowsLinkPreview = true
|
||||||
|
|
||||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
self.webView.scrollView.contentInsetAdjustmentBehavior = .never
|
self.webView.scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +145,8 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
title = parsedUrl.host ?? ""
|
title = parsedUrl.host ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.errorView = ComponentHostView()
|
||||||
|
|
||||||
self._state = BrowserContentState(title: title, url: url, estimatedProgress: 0.0, readingProgress: 0.0, contentType: .webPage)
|
self._state = BrowserContentState(title: title, url: url, estimatedProgress: 0.0, readingProgress: 0.0, contentType: .webPage)
|
||||||
self.statePromise = Promise<BrowserContentState>(self._state)
|
self.statePromise = Promise<BrowserContentState>(self._state)
|
||||||
|
|
||||||
@ -152,7 +161,9 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: [], context: nil)
|
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: [], context: nil)
|
||||||
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoBack), options: [], context: nil)
|
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoBack), options: [], context: nil)
|
||||||
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoForward), options: [], context: nil)
|
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.canGoForward), options: [], context: nil)
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
|
}
|
||||||
self.addSubview(self.webView)
|
self.addSubview(self.webView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +181,16 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
self.faviconDisposable.dispose()
|
self.faviconDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
|
self.presentationData = presentationData
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||||
|
}
|
||||||
|
if let (size, insets) = self.validLayout {
|
||||||
|
self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false)
|
var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false)
|
||||||
func updateFontState(_ state: BrowserPresentationState.FontState) {
|
func updateFontState(_ state: BrowserPresentationState.FontState) {
|
||||||
self.updateFontState(state, force: false)
|
self.updateFontState(state, force: false)
|
||||||
@ -313,15 +334,42 @@ 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)?
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ComponentTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ComponentTransition) {
|
||||||
|
self.validLayout = (size, insets)
|
||||||
|
|
||||||
var scrollInsets = insets
|
var scrollInsets = insets
|
||||||
|
scrollInsets.left = 0.0
|
||||||
|
scrollInsets.right = 0.0
|
||||||
scrollInsets.top = 0.0
|
scrollInsets.top = 0.0
|
||||||
if self.webView.scrollView.contentInset != insets {
|
if self.webView.scrollView.contentInset != insets {
|
||||||
self.webView.scrollView.contentInset = scrollInsets
|
self.webView.scrollView.contentInset = scrollInsets
|
||||||
self.webView.scrollView.scrollIndicatorInsets = scrollInsets
|
self.webView.scrollView.scrollIndicatorInsets = scrollInsets
|
||||||
}
|
}
|
||||||
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)
|
||||||
transition.setFrame(view: self.webView, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top)))
|
transition.setFrame(view: self.webView, frame: CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: size.width - insets.left - insets.right, height: size.height - insets.top)))
|
||||||
|
|
||||||
|
if let error = self.currentError {
|
||||||
|
let errorSize = self.errorView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(
|
||||||
|
ErrorComponent(
|
||||||
|
theme: self.presentationData.theme,
|
||||||
|
title: self.presentationData.strings.Browser_ErrorTitle,
|
||||||
|
text: error.localizedDescription
|
||||||
|
)
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: size.width - insets.left - insets.right - 72.0, height: size.height)
|
||||||
|
)
|
||||||
|
if self.errorView.superview == nil {
|
||||||
|
self.addSubview(self.errorView)
|
||||||
|
self.errorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
self.errorView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - errorSize.width) / 2.0), y: insets.top + floorToScreenPixels((size.height - insets.top - insets.bottom - errorSize.height) / 2.0)), size: errorSize)
|
||||||
|
} else if self.errorView.superview != nil {
|
||||||
|
self.errorView.removeFromSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateState(_ f: (BrowserContentState) -> BrowserContentState) {
|
private func updateState(_ f: (BrowserContentState) -> BrowserContentState) {
|
||||||
@ -403,6 +451,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||||
|
self.currentError = nil
|
||||||
self.updateFontState(self.currentFontState, force: true)
|
self.updateFontState(self.currentFontState, force: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,10 +460,24 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
$0
|
$0
|
||||||
.withUpdatedBackList(webView.backForwardList.backList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
.withUpdatedBackList(webView.backForwardList.backList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||||
.withUpdatedForwardList(webView.backForwardList.forwardList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
.withUpdatedForwardList(webView.backForwardList.forwardList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||||
}
|
}
|
||||||
self.parseFavicon()
|
self.parseFavicon()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||||
|
self.currentError = error
|
||||||
|
if let (size, insets) = self.validLayout {
|
||||||
|
self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||||
|
self.currentError = error
|
||||||
|
if let (size, insets) = self.validLayout {
|
||||||
|
self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
@available(iOSApplicationExtension 15.0, 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)
|
||||||
@ -494,24 +557,23 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO:localize
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
||||||
return UIMenu(title: "", children: [
|
return UIMenu(title: "", children: [
|
||||||
UIAction(title: "Open", image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
UIAction(title: presentationData.strings.Browser_ContextMenu_Open, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||||
self?.open(url: url.absoluteString, new: false)
|
self?.open(url: url.absoluteString, new: false)
|
||||||
}),
|
}),
|
||||||
UIAction(title: "Open in New Tab", image: generateTintedImage(image: UIImage(bundleImageName: "Instant View/NewTab"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
UIAction(title: presentationData.strings.Browser_ContextMenu_OpenInNewTab, image: generateTintedImage(image: UIImage(bundleImageName: "Instant View/NewTab"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||||
self?.open(url: url.absoluteString, new: true)
|
self?.open(url: url.absoluteString, new: true)
|
||||||
}),
|
}),
|
||||||
UIAction(title: "Add to Reading List", image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadingList"), color: presentationData.theme.contextMenu.primaryColor), handler: { _ in
|
UIAction(title: presentationData.strings.Browser_ContextMenu_AddToReadingList, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadingList"), color: presentationData.theme.contextMenu.primaryColor), handler: { _ in
|
||||||
let _ = try? SSReadingList.default()?.addItem(with: url, title: nil, previewText: nil)
|
let _ = try? SSReadingList.default()?.addItem(with: url, title: nil, previewText: nil)
|
||||||
}),
|
}),
|
||||||
UIAction(title: "Copy Link", image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
UIAction(title: presentationData.strings.Browser_ContextMenu_CopyLink, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||||
UIPasteboard.general.string = url.absoluteString
|
UIPasteboard.general.string = url.absoluteString
|
||||||
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
}),
|
}),
|
||||||
UIAction(title: "Share", image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
UIAction(title: presentationData.strings.Browser_ContextMenu_Share, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||||
self?.share(url: url.absoluteString)
|
self?.share(url: url.absoluteString)
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
@ -625,3 +687,97 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ErrorComponent: CombinedComponent {
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let title: String
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
init(
|
||||||
|
theme: PresentationTheme,
|
||||||
|
title: String,
|
||||||
|
text: String
|
||||||
|
) {
|
||||||
|
self.theme = theme
|
||||||
|
self.title = title
|
||||||
|
self.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: ErrorComponent, rhs: ErrorComponent) -> Bool {
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
static var body: Body {
|
||||||
|
let animation = Child(LottieComponent.self)
|
||||||
|
let title = Child(MultilineTextComponent.self)
|
||||||
|
let text = Child(MultilineTextComponent.self)
|
||||||
|
|
||||||
|
return { context in
|
||||||
|
var contentHeight: CGFloat = 0.0
|
||||||
|
let animationSize = 148.0
|
||||||
|
let animationSpacing: CGFloat = 8.0
|
||||||
|
let textSpacing: CGFloat = 8.0
|
||||||
|
|
||||||
|
let animation = animation.update(
|
||||||
|
component: LottieComponent(
|
||||||
|
content: LottieComponent.AppBundleContent(name: "ChatListNoResults")
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
availableSize: CGSize(width: animationSize, height: animationSize),
|
||||||
|
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
|
||||||
|
|
||||||
|
let title = title.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: context.component.title,
|
||||||
|
font: Font.semibold(17.0),
|
||||||
|
textColor: context.component.theme.list.itemSecondaryTextColor
|
||||||
|
)),
|
||||||
|
horizontalAlignment: .center
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
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
|
||||||
|
|
||||||
|
let text = text.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: context.component.text,
|
||||||
|
font: Font.regular(15.0),
|
||||||
|
textColor: context.component.theme.list.itemSecondaryTextColor
|
||||||
|
)),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(text
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + text.size.height / 2.0))
|
||||||
|
)
|
||||||
|
contentHeight += text.size.height
|
||||||
|
|
||||||
|
return CGSize(width: context.availableSize.width, height: contentHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -585,9 +585,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
var typeIcon: UIImage?
|
var typeIcon: UIImage?
|
||||||
var duration: String?
|
var duration: String?
|
||||||
if asset.isFavorite {
|
if asset.mediaType == .video {
|
||||||
typeIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Favorite"), color: .white)
|
|
||||||
} else if asset.mediaType == .video {
|
|
||||||
if asset.mediaSubtypes.contains(.videoHighFrameRate) {
|
if asset.mediaSubtypes.contains(.videoHighFrameRate) {
|
||||||
typeIcon = UIImage(bundleImageName: "Media Editor/MediaSlomo")
|
typeIcon = UIImage(bundleImageName: "Media Editor/MediaSlomo")
|
||||||
} else if asset.mediaSubtypes.contains(.videoTimelapse) {
|
} else if asset.mediaSubtypes.contains(.videoTimelapse) {
|
||||||
@ -597,6 +595,9 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
duration = stringForDuration(Int32(asset.duration))
|
duration = stringForDuration(Int32(asset.duration))
|
||||||
}
|
}
|
||||||
|
if asset.isFavorite {
|
||||||
|
typeIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Favorite"), color: .white)
|
||||||
|
}
|
||||||
|
|
||||||
if typeIcon != nil {
|
if typeIcon != nil {
|
||||||
if self.leftShadowNode.supernode == nil {
|
if self.leftShadowNode.supernode == nil {
|
||||||
|
|||||||
@ -3186,6 +3186,7 @@ private class SelectedButtonNode: HighlightableButtonNode {
|
|||||||
|
|
||||||
var theme: PresentationTheme {
|
var theme: PresentationTheme {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.icon.image = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/SelectedIcon"), color: self.theme.list.itemCheckColors.foregroundColor)
|
||||||
self.background.image = generateStretchableFilledCircleImage(radius: 21.0 / 2.0, color: self.theme.list.itemCheckColors.fillColor)
|
self.background.image = generateStretchableFilledCircleImage(radius: 21.0 / 2.0, color: self.theme.list.itemCheckColors.fillColor)
|
||||||
let _ = self.update(count: self.count)
|
let _ = self.update(count: self.count)
|
||||||
}
|
}
|
||||||
@ -3202,7 +3203,7 @@ private class SelectedButtonNode: HighlightableButtonNode {
|
|||||||
self.icon.displaysAsynchronously = false
|
self.icon.displaysAsynchronously = false
|
||||||
self.label.displaysAsynchronously = false
|
self.label.displaysAsynchronously = false
|
||||||
|
|
||||||
self.icon.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)
|
self.icon.image = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/SelectedIcon"), color: self.theme.list.itemCheckColors.foregroundColor)
|
||||||
self.background.image = generateStretchableFilledCircleImage(radius: 21.0 / 2.0, color: self.theme.list.itemCheckColors.fillColor)
|
self.background.image = generateStretchableFilledCircleImage(radius: 21.0 / 2.0, color: self.theme.list.itemCheckColors.fillColor)
|
||||||
|
|
||||||
self.addSubnode(self.background)
|
self.addSubnode(self.background)
|
||||||
@ -3229,8 +3230,8 @@ private class SelectedButtonNode: HighlightableButtonNode {
|
|||||||
let size = CGSize(width: textSize.width + 28.0, height: diameter)
|
let size = CGSize(width: textSize.width + 28.0, height: diameter)
|
||||||
|
|
||||||
if let _ = self.icon.image {
|
if let _ = self.icon.image {
|
||||||
let iconSize = CGSize(width: 22.0, height: 22.0)
|
let iconSize = CGSize(width: 14.0, height: 11.0)
|
||||||
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
let iconFrame = CGRect(origin: CGPoint(x: 5.0, y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||||
self.icon.frame = iconFrame
|
self.icon.frame = iconFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1555,7 +1555,17 @@ private func monetizationEntries(
|
|||||||
|
|
||||||
entries.append(.adsProceedsTitle(presentationData.theme, presentationData.strings.Monetization_StarsProceeds_Title))
|
entries.append(.adsProceedsTitle(presentationData.theme, presentationData.strings.Monetization_StarsProceeds_Title))
|
||||||
entries.append(.adsProceedsOverview(presentationData.theme, canViewRevenue ? data : nil, canViewStarsRevenue ? starsData : nil))
|
entries.append(.adsProceedsOverview(presentationData.theme, canViewRevenue ? data : nil, canViewStarsRevenue ? starsData : nil))
|
||||||
entries.append(.adsProceedsInfo(presentationData.theme, presentationData.strings.Monetization_StarsProceeds_Info))
|
|
||||||
|
|
||||||
|
let proceedsInfo: String
|
||||||
|
if canViewStarsRevenue && canViewRevenue {
|
||||||
|
proceedsInfo = presentationData.strings.Monetization_Proceeds_TonAndStars_Info
|
||||||
|
} else if canViewStarsRevenue {
|
||||||
|
proceedsInfo = presentationData.strings.Monetization_Proceeds_Stars_Info
|
||||||
|
} else {
|
||||||
|
proceedsInfo = presentationData.strings.Monetization_Proceeds_Ton_Info
|
||||||
|
}
|
||||||
|
entries.append(.adsProceedsInfo(presentationData.theme, proceedsInfo))
|
||||||
|
|
||||||
var isCreator = false
|
var isCreator = false
|
||||||
if let peer, case let .channel(channel) = peer, channel.flags.contains(.isCreator) {
|
if let peer, case let .channel(channel) = peer, channel.flags.contains(.isCreator) {
|
||||||
|
|||||||
@ -1186,7 +1186,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
|
|
||||||
let starsRevenueContextAndState = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let starsRevenueContextAndState = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> mapToSignal { peer -> Signal<(StarsRevenueStatsContext?, StarsRevenueStats?), NoError> in
|
|> mapToSignal { peer -> Signal<(StarsRevenueStatsContext?, StarsRevenueStats?), NoError> in
|
||||||
guard let peer, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) || context.sharedContext.applicationBindings.appBuildType == .internal else {
|
guard let peer, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) || context.sharedContext.applicationBindings.appBuildType == .public else {
|
||||||
return .single((nil, nil))
|
return .single((nil, nil))
|
||||||
}
|
}
|
||||||
let starsRevenueStatsContext = StarsRevenueStatsContext(account: context.account, peerId: peerId)
|
let starsRevenueStatsContext = StarsRevenueStatsContext(account: context.account, peerId: peerId)
|
||||||
|
|||||||
@ -193,6 +193,10 @@ final class StarsStatisticsScreenComponent: Component {
|
|||||||
deinit {
|
deinit {
|
||||||
self.stateDisposable?.dispose()
|
self.stateDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrollToTop() {
|
||||||
|
self.scrollView.setContentOffset(CGPoint(x: 0.0, y: -self.scrollView.contentInset.top), animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
if !self.ignoreScrolling {
|
if !self.ignoreScrolling {
|
||||||
@ -783,6 +787,13 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.transactionsContext.loadMore()
|
self.transactionsContext.loadMore()
|
||||||
|
|
||||||
|
self.scrollToTop = { [weak self] in
|
||||||
|
guard let self, let componentView = self.node.hostView.componentView as? StarsStatisticsScreenComponent.View else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
componentView.scrollToTop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Media Gallery/SelectedIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Media Gallery/SelectedIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "albumcheck.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
93
submodules/TelegramUI/Images.xcassets/Media Gallery/SelectedIcon.imageset/albumcheck.pdf
vendored
Normal file
93
submodules/TelegramUI/Images.xcassets/Media Gallery/SelectedIcon.imageset/albumcheck.pdf
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.500000 -1.125229 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
0.752577 6.783733 m
|
||||||
|
0.388895 7.199370 -0.242868 7.241488 -0.658505 6.877806 c
|
||||||
|
-1.074141 6.514123 -1.116259 5.882361 -0.752577 5.466724 c
|
||||||
|
0.752577 6.783733 l
|
||||||
|
h
|
||||||
|
3.500000 2.125229 m
|
||||||
|
2.747423 1.466724 l
|
||||||
|
2.940081 1.246544 3.219498 1.121778 3.512046 1.125301 c
|
||||||
|
3.804593 1.128825 4.080924 1.260287 4.268221 1.485044 c
|
||||||
|
3.500000 2.125229 l
|
||||||
|
h
|
||||||
|
11.768221 10.485044 m
|
||||||
|
12.121785 10.909322 12.064462 11.539886 11.640184 11.893450 c
|
||||||
|
11.215907 12.247014 10.585342 12.189691 10.231779 11.765413 c
|
||||||
|
11.768221 10.485044 l
|
||||||
|
h
|
||||||
|
-0.752577 5.466724 m
|
||||||
|
2.747423 1.466724 l
|
||||||
|
4.252577 2.783733 l
|
||||||
|
0.752577 6.783733 l
|
||||||
|
-0.752577 5.466724 l
|
||||||
|
h
|
||||||
|
4.268221 1.485044 m
|
||||||
|
11.768221 10.485044 l
|
||||||
|
10.231779 11.765413 l
|
||||||
|
2.731779 2.765413 l
|
||||||
|
4.268221 1.485044 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
839
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 14.000000 11.000031 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000000929 00000 n
|
||||||
|
0000000951 00000 n
|
||||||
|
0000001124 00000 n
|
||||||
|
0000001198 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1257
|
||||||
|
%%EOF
|
||||||
Loading…
x
Reference in New Issue
Block a user