mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-25 17:00:50 +02:00
chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
This commit is contained in:
@@ -9,6 +9,9 @@ import AccountContext
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import UrlEscaping
|
||||
import GlassBackgroundComponent
|
||||
import GlassBarButtonComponent
|
||||
import EdgeEffect
|
||||
|
||||
final class AddressBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
@@ -19,6 +22,8 @@ final class AddressBarContentComponent: Component {
|
||||
let url: String
|
||||
let isSecure: Bool
|
||||
let isExpanded: Bool
|
||||
let readingProgress: CGFloat
|
||||
let loadingProgress: Double?
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
|
||||
init(
|
||||
@@ -28,6 +33,8 @@ final class AddressBarContentComponent: Component {
|
||||
url: String,
|
||||
isSecure: Bool,
|
||||
isExpanded: Bool,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
performAction: ActionSlot<BrowserScreen.Action>
|
||||
) {
|
||||
self.theme = theme
|
||||
@@ -36,6 +43,8 @@ final class AddressBarContentComponent: Component {
|
||||
self.url = url
|
||||
self.isSecure = isSecure
|
||||
self.isExpanded = isExpanded
|
||||
self.readingProgress = readingProgress
|
||||
self.loadingProgress = loadingProgress
|
||||
self.performAction = performAction
|
||||
}
|
||||
|
||||
@@ -58,6 +67,12 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.isExpanded != rhs.isExpanded {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -85,6 +100,8 @@ final class AddressBarContentComponent: Component {
|
||||
var isSecure: Bool
|
||||
var collapseFraction: CGFloat
|
||||
var isTablet: Bool
|
||||
var readingProgress: CGFloat
|
||||
var loadingProgress: Double?
|
||||
|
||||
static func ==(lhs: Params, rhs: Params) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
@@ -111,26 +128,33 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.isTablet != rhs.isTablet {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let activated: (Bool) -> Void = { _ in }
|
||||
private let deactivated: (Bool) -> Void = { _ in }
|
||||
|
||||
private let backgroundLayer: SimpleLayer
|
||||
|
||||
private let iconView: UIImageView
|
||||
|
||||
private let backgroundView: GlassBackgroundView
|
||||
|
||||
private let clearIconView: UIImageView
|
||||
private let clearIconButton: HighlightTrackingButton
|
||||
|
||||
private let cancelButtonTitle: ComponentView<Empty>
|
||||
private let cancelButton: HighlightTrackingButton
|
||||
private let cancelButton = ComponentView<Empty>()
|
||||
|
||||
private var placeholderContent = ComponentView<Empty>()
|
||||
private var titleContent = ComponentView<Empty>()
|
||||
|
||||
private let clippingView = UIView()
|
||||
private var loadingProgress = ComponentView<Empty>()
|
||||
private var readingProgressView = UIView()
|
||||
|
||||
private var textFrame: CGRect?
|
||||
private var textField: TextField?
|
||||
|
||||
@@ -144,50 +168,30 @@ final class AddressBarContentComponent: Component {
|
||||
}
|
||||
|
||||
init() {
|
||||
self.backgroundLayer = SimpleLayer()
|
||||
|
||||
self.iconView = UIImageView()
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
|
||||
self.clearIconView = UIImageView()
|
||||
self.clearIconButton = HighlightableButton()
|
||||
self.clearIconView.isHidden = false
|
||||
self.clearIconButton.isHidden = false
|
||||
|
||||
self.cancelButtonTitle = ComponentView()
|
||||
self.cancelButton = HighlightTrackingButton()
|
||||
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.addSublayer(self.backgroundLayer)
|
||||
self.clippingView.clipsToBounds = true
|
||||
|
||||
self.addSubview(self.iconView)
|
||||
self.addSubview(self.clearIconView)
|
||||
self.addSubview(self.clearIconButton)
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.contentView.addSubview(self.clippingView)
|
||||
self.clippingView.addSubview(self.readingProgressView)
|
||||
|
||||
self.backgroundView.contentView.addSubview(self.clearIconView)
|
||||
self.backgroundView.contentView.addSubview(self.clearIconButton)
|
||||
|
||||
self.addSubview(self.cancelButton)
|
||||
self.clipsToBounds = true
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
self.tapRecognizer = tapRecognizer
|
||||
self.addGestureRecognizer(tapRecognizer)
|
||||
|
||||
self.cancelButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.layer.removeAnimation(forKey: "opacity")
|
||||
cancelButtonTitleView.alpha = 0.4
|
||||
}
|
||||
} else {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.alpha = 1.0
|
||||
cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside)
|
||||
|
||||
|
||||
self.clearIconButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
@@ -263,11 +267,16 @@ final class AddressBarContentComponent: Component {
|
||||
self.placeholderContent.view?.isHidden = !text.isEmpty
|
||||
|
||||
if let params = self.params {
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, transition: .immediate)
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, readingProgress: params.readingProgress, loadingProgress: params.loadingProgress, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: AddressBarContentComponent, availableSize: CGSize, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
func update(
|
||||
component: AddressBarContentComponent,
|
||||
availableSize: CGSize,
|
||||
environment: Environment<BrowserNavigationBarEnvironment>,
|
||||
transition: ComponentTransition
|
||||
) -> CGSize {
|
||||
let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction
|
||||
|
||||
let wasExpanded = self.component?.isExpanded ?? false
|
||||
@@ -282,12 +291,36 @@ final class AddressBarContentComponent: Component {
|
||||
let isActive = self.textField?.isFirstResponder ?? false
|
||||
|
||||
let title = getDisplayUrl(component.url, hostOnly: true)
|
||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, isTablet: component.metrics.isTablet, transition: transition)
|
||||
self.update(
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
size: availableSize,
|
||||
isActive: isActive,
|
||||
title: title.lowercased(),
|
||||
isSecure: component.isSecure,
|
||||
collapseFraction: collapseFraction,
|
||||
isTablet: component.metrics.isTablet,
|
||||
readingProgress: component.readingProgress,
|
||||
loadingProgress: component.loadingProgress,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, isActive: Bool, title: String, isSecure: Bool, collapseFraction: CGFloat, isTablet: Bool, transition: ComponentTransition) {
|
||||
public func update(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
size: CGSize,
|
||||
isActive: Bool,
|
||||
title: String,
|
||||
isSecure: Bool,
|
||||
collapseFraction: CGFloat,
|
||||
isTablet: Bool,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
transition: ComponentTransition
|
||||
) {
|
||||
let params = Params(
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
@@ -296,7 +329,9 @@ final class AddressBarContentComponent: Component {
|
||||
title: title,
|
||||
isSecure: isSecure,
|
||||
collapseFraction: collapseFraction,
|
||||
isTablet: isTablet
|
||||
isTablet: isTablet,
|
||||
readingProgress: readingProgress,
|
||||
loadingProgress: loadingProgress
|
||||
)
|
||||
|
||||
if self.params == params {
|
||||
@@ -306,8 +341,6 @@ final class AddressBarContentComponent: Component {
|
||||
let isActiveWithText = self.component?.isExpanded ?? false
|
||||
|
||||
if self.params?.theme !== theme {
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Lock"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor
|
||||
self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor
|
||||
}
|
||||
@@ -315,57 +348,9 @@ final class AddressBarContentComponent: Component {
|
||||
self.params = params
|
||||
|
||||
let sideInset: CGFloat = 10.0
|
||||
let inputHeight: CGFloat = 36.0
|
||||
let inputHeight: CGFloat = 44.0
|
||||
let topInset: CGFloat = (size.height - inputHeight) / 2.0
|
||||
|
||||
self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor
|
||||
self.backgroundLayer.cornerRadius = 10.5
|
||||
transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.0, min(1.0, 1.0 - collapseFraction * 1.5)))
|
||||
|
||||
let cancelTextSize = self.cancelButtonTitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
text: strings.Common_Cancel,
|
||||
font: Font.regular(17.0),
|
||||
color: theme.rootController.navigationBar.accentTextColor
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 32.0, height: 100.0)
|
||||
)
|
||||
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight))
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing
|
||||
}
|
||||
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)))
|
||||
self.cancelButton.isUserInteractionEnabled = isActiveWithText && !isTablet
|
||||
|
||||
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 placeholderSize = self.placeholderContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
placeholderContentView.alpha = 0.0
|
||||
placeholderContentView.isHidden = true
|
||||
self.addSubview(placeholderContentView)
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let titleSize = self.titleContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
@@ -379,51 +364,96 @@ final class AddressBarContentComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 36.0, height: size.height)
|
||||
)
|
||||
var titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.midX - titleSize.width / 2.0, y: backgroundFrame.midY - titleSize.height / 2.0), size: titleSize)
|
||||
if isSecure && !isActiveWithText {
|
||||
titleContentFrame.origin.x += 7.0
|
||||
|
||||
let expandedBackgroundWidth = size.width - 14.0 * 2.0
|
||||
let collapsedBackgroundWidth = titleSize.width + 32.0
|
||||
var backgroundSize = CGSize(width: expandedBackgroundWidth * (1.0 - collapseFraction) + collapsedBackgroundWidth * collapseFraction, height: 44.0)
|
||||
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
let cancelSize = self.cancelButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
GlassBarButtonComponent(
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: nil,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(name: "Navigation/Close", tintColor: theme.chat.inputPanel.panelControlColor)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
self?.cancelPressed()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
)
|
||||
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundSize.width -= cancelSize.width + cancelButtonSpacing + 4.0
|
||||
}
|
||||
var titleSizeChanged = false
|
||||
self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
|
||||
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: floor((size.width - backgroundSize.width) / 2.0), y: topInset), size: backgroundSize)
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundFrame.origin.x = 16.0
|
||||
}
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
|
||||
transition.setFrame(view: self.clippingView, frame: CGRect(origin: .zero, size: backgroundFrame.size))
|
||||
transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: backgroundFrame.size.height * 0.5)
|
||||
|
||||
let textX: CGFloat = sideInset
|
||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: 0.0), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||
|
||||
let placeholderSize = self.placeholderContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
placeholderContentView.alpha = 0.0
|
||||
placeholderContentView.isHidden = true
|
||||
self.backgroundView.contentView.addSubview(placeholderContentView)
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.size.height / 2.0 - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.width / 2.0 - titleSize.width / 2.0, y: backgroundFrame.height / 2.0 - titleSize.height / 2.0), size: titleSize)
|
||||
if let titleContentView = self.titleContent.view {
|
||||
if titleContentView.superview == nil {
|
||||
self.addSubview(titleContentView)
|
||||
}
|
||||
if titleContentView.frame.width != titleContentFrame.size.width {
|
||||
titleSizeChanged = true
|
||||
self.backgroundView.contentView.addSubview(titleContentView)
|
||||
}
|
||||
transition.setPosition(view: titleContentView, position: titleContentFrame.center)
|
||||
titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size)
|
||||
transition.setAlpha(view: titleContentView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: titleContentFrame.minX - image.size.width - 3.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
var iconTransition = transition
|
||||
if titleSizeChanged {
|
||||
iconTransition = .immediate
|
||||
}
|
||||
iconTransition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
transition.setAlpha(view: self.iconView, alpha: isActiveWithText || !isSecure ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
|
||||
if let image = self.clearIconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.width - image.size.width - 4.0, y: floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.clearIconView, frame: iconFrame)
|
||||
transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0))
|
||||
transition.setAlpha(view: self.clearIconView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
self.clearIconButton.isUserInteractionEnabled = isActiveWithText
|
||||
}
|
||||
|
||||
if let cancelButtonTitleComponentView = self.cancelButtonTitle.view {
|
||||
if cancelButtonTitleComponentView.superview == nil {
|
||||
self.addSubview(cancelButtonTitleComponentView)
|
||||
cancelButtonTitleComponentView.isUserInteractionEnabled = false
|
||||
if let cancelButtonView = self.cancelButton.view {
|
||||
if cancelButtonView.superview == nil {
|
||||
self.addSubview(cancelButtonView)
|
||||
}
|
||||
transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
|
||||
transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0)
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelSize.height) / 2.0)), size: cancelSize))
|
||||
transition.setAlpha(view: cancelButtonView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: backgroundFrame.width - sideInset, height: backgroundFrame.height))
|
||||
|
||||
let textField: TextField
|
||||
if let current = self.textField {
|
||||
@@ -434,7 +464,7 @@ final class AddressBarContentComponent: Component {
|
||||
textField.autocorrectionType = .no
|
||||
textField.keyboardType = .URL
|
||||
textField.returnKeyType = .go
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
self.backgroundView.contentView.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
self.textField = textField
|
||||
|
||||
textField.delegate = self
|
||||
@@ -450,9 +480,34 @@ final class AddressBarContentComponent: Component {
|
||||
}
|
||||
|
||||
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: sideInset, y: -UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
||||
transition.setAlpha(view: textField, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
textField.isUserInteractionEnabled = isActiveWithText
|
||||
|
||||
|
||||
let loadingProgressInset: CGFloat = 12.0
|
||||
let loadingProgressSize = CGSize(width: backgroundSize.width - loadingProgressInset * 2.0, height: 2.0)
|
||||
let _ = self.loadingProgress.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(LoadingProgressComponent(
|
||||
color: theme.rootController.navigationBar.accentTextColor,
|
||||
height: loadingProgressSize.height,
|
||||
value: params.loadingProgress ?? 0.0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: loadingProgressSize
|
||||
)
|
||||
if let loadingProgressView = self.loadingProgress.view {
|
||||
if loadingProgressView.superview == nil {
|
||||
self.clippingView.addSubview(loadingProgressView)
|
||||
}
|
||||
transition.setFrame(view: loadingProgressView, frame: CGRect(origin: CGPoint(x: loadingProgressInset, y: backgroundSize.height - loadingProgressSize.height), size: loadingProgressSize))
|
||||
transition.setAlpha(view: loadingProgressView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
self.readingProgressView.backgroundColor = theme.rootController.navigationBar.primaryTextColor.withMultipliedAlpha(0.07)
|
||||
self.readingProgressView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundSize.width * params.readingProgress, height: backgroundSize.height))
|
||||
transition.setAlpha(view: self.readingProgressView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ final class BrowserAddressListComponent: Component {
|
||||
let insets: UIEdgeInsets
|
||||
let metrics: LayoutMetrics
|
||||
let addressBarFrame: CGRect
|
||||
let navigationBarHeight: CGFloat
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
let presentInGlobalOverlay: (ViewController) -> Void
|
||||
|
||||
@@ -29,6 +30,7 @@ final class BrowserAddressListComponent: Component {
|
||||
insets: UIEdgeInsets,
|
||||
metrics: LayoutMetrics,
|
||||
addressBarFrame: CGRect,
|
||||
navigationBarHeight: CGFloat,
|
||||
performAction: ActionSlot<BrowserScreen.Action>,
|
||||
presentInGlobalOverlay: @escaping (ViewController) -> Void
|
||||
) {
|
||||
@@ -38,6 +40,7 @@ final class BrowserAddressListComponent: Component {
|
||||
self.insets = insets
|
||||
self.metrics = metrics
|
||||
self.addressBarFrame = addressBarFrame
|
||||
self.navigationBarHeight = navigationBarHeight
|
||||
self.performAction = performAction
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
}
|
||||
@@ -61,6 +64,9 @@ final class BrowserAddressListComponent: Component {
|
||||
if lhs.addressBarFrame != rhs.addressBarFrame {
|
||||
return false
|
||||
}
|
||||
if lhs.navigationBarHeight != rhs.navigationBarHeight {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -214,15 +220,16 @@ final class BrowserAddressListComponent: Component {
|
||||
var validIds: [AnyHashable] = []
|
||||
var validSectionHeaders: [AnyHashable] = []
|
||||
var sectionOffset: CGFloat = 0.0
|
||||
let headerOffset: CGFloat = self.scrollView.frame.minY
|
||||
|
||||
let sideInset: CGFloat = 0.0
|
||||
let containerInset: CGFloat = 0.0
|
||||
let containerInset: CGFloat = self.scrollView.frame.minY
|
||||
|
||||
for sectionIndex in 0 ..< itemLayout.sections.count {
|
||||
let section = itemLayout.sections[sectionIndex]
|
||||
|
||||
do {
|
||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: headerOffset + sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||
|
||||
let sectionHeaderMinY = topOffset + containerInset
|
||||
let sectionHeaderMaxY = containerInset + sectionOffset - self.scrollView.bounds.minY + section.totalHeight - 28.0
|
||||
@@ -540,7 +547,7 @@ final class BrowserAddressListComponent: Component {
|
||||
let containerFrame: CGRect
|
||||
if component.metrics.isTablet {
|
||||
let containerSize = CGSize(width: component.addressBarFrame.width + 32.0, height: 540.0)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 72.0), size: containerSize)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 100.0), size: containerSize)
|
||||
|
||||
self.backgroundView.layer.cornerRadius = 10.0
|
||||
} else {
|
||||
@@ -602,23 +609,28 @@ final class BrowserAddressListComponent: Component {
|
||||
let itemLayout = ItemLayout(containerSize: containerFrame.size, insets: .zero, sections: sections)
|
||||
self.itemLayout = itemLayout
|
||||
|
||||
let containerWidth = containerFrame.size.width
|
||||
let scrollContentHeight = max(itemLayout.contentHeight, containerFrame.size.height)
|
||||
|
||||
self.ignoreScrolling = true
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: containerFrame.size))
|
||||
let contentSize = CGSize(width: containerWidth, height: scrollContentHeight)
|
||||
let scrollFrame: CGRect
|
||||
if component.metrics.isTablet {
|
||||
scrollFrame = CGRect(origin: .zero, size: containerFrame.size)
|
||||
} else {
|
||||
scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: component.navigationBarHeight), size: CGSize(width: containerFrame.size.width, height: containerFrame.size.height - component.navigationBarHeight))
|
||||
}
|
||||
transition.setFrame(view: self.scrollView, frame: scrollFrame)
|
||||
let contentSize = CGSize(width: scrollFrame.width, height: scrollContentHeight)
|
||||
if contentSize != self.scrollView.contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
if resetScrolling {
|
||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: containerFrame.size.height))
|
||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollFrame.size)
|
||||
}
|
||||
self.ignoreScrolling = false
|
||||
self.updateScrolling(transition: transition)
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: containerFrame)
|
||||
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: scrollContentHeight)))
|
||||
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: scrollFrame.width, height: scrollContentHeight)))
|
||||
|
||||
if component.metrics.isTablet {
|
||||
transition.setFrame(view: self.shadowView, frame: containerFrame.insetBy(dx: -60.0, dy: -60.0))
|
||||
|
||||
@@ -12,8 +12,15 @@ import AccountContext
|
||||
import ContextUI
|
||||
import UrlEscaping
|
||||
|
||||
private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold)
|
||||
private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radius: 6.0, color: UIColor(rgb: 0xFF9500))
|
||||
private let iconFont = Font.with(size: 28.0, design: .round, weight: .bold)
|
||||
private let iconTextBackgroundImage = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor(rgb: 0xFF9500).cgColor)
|
||||
|
||||
let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 12.0)
|
||||
context.addPath(path.cgPath)
|
||||
context.fillPath()
|
||||
})
|
||||
|
||||
final class BrowserAddressListItemComponent: Component {
|
||||
let context: AccountContext
|
||||
@@ -261,7 +268,7 @@ final class BrowserAddressListItemComponent: Component {
|
||||
let iconImageLayout = self.icon.asyncLayout()
|
||||
var iconImageApply: (() -> Void)?
|
||||
if let iconImageReferenceAndRepresentation = iconImageReferenceAndRepresentation {
|
||||
let imageCorners = ImageCorners(radius: 6.0)
|
||||
let imageCorners = ImageCorners(radius: 12.0, curve: .continuous)
|
||||
let arguments = TransformImageArguments(corners: imageCorners, imageSize: iconImageReferenceAndRepresentation.1.dimensions.cgSize.aspectFilled(iconSize), boundingSize: iconSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor)
|
||||
iconImageApply = iconImageLayout(arguments)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ import SearchBarNode
|
||||
import ChatHistorySearchContainerNode
|
||||
import ContextUI
|
||||
import UndoUI
|
||||
import ComponentFlow
|
||||
import EdgeEffect
|
||||
import ButtonComponent
|
||||
import BundleIconComponent
|
||||
|
||||
public final class BrowserBookmarksScreen: ViewController {
|
||||
final class Node: ViewControllerTracingNode, ASScrollViewDelegate {
|
||||
@@ -196,7 +200,6 @@ public final class BrowserBookmarksScreen: ViewController {
|
||||
controllerInteraction: self.controllerInteraction,
|
||||
selectedMessages: .single(nil),
|
||||
mode: .list(
|
||||
search: false,
|
||||
reversed: false,
|
||||
reverseGroups: false,
|
||||
displayHeaders: .none,
|
||||
@@ -287,9 +290,9 @@ public final class BrowserBookmarksScreen: ViewController {
|
||||
}
|
||||
let tagMask: MessageTags = .webPage
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.context.account.peerId, threadId: nil, tagMask: tagMask, interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.context.account.peerId, threadId: nil, tagMask: tagMask, interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in
|
||||
self?.controller?.deactivateSearch()
|
||||
})
|
||||
}, fieldStyle: placeholderNode.fieldStyle)
|
||||
|
||||
self.searchDisplayController?.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in
|
||||
@@ -372,7 +375,9 @@ public final class BrowserBookmarksScreen: ViewController {
|
||||
self.openUrl = openUrl
|
||||
self.addBookmark = addBookmark
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass))
|
||||
|
||||
self._hasGlassStyle = true
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
@@ -429,19 +434,13 @@ public final class BrowserBookmarksScreen: ViewController {
|
||||
searchContentNode.updateListVisibleContentOffset(offset)
|
||||
}
|
||||
}
|
||||
//
|
||||
// self.node.historyNode.didEndScrolling = { [weak self] _ in
|
||||
// if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
||||
// let _ = fixNavigationSearchableListNodeScrolling(strongSelf.node.historyNode, searchNode: searchContentNode)
|
||||
// }
|
||||
// }
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate)
|
||||
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search)
|
||||
}
|
||||
|
||||
@@ -484,10 +483,8 @@ private class BottomPanelNode: ASDisplayNode {
|
||||
private let strings: PresentationStrings
|
||||
private let action: () -> Void
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let button: HighlightTrackingButtonNode
|
||||
private let iconNode: ASImageNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let edgeEffectView = EdgeEffectView()
|
||||
private let button = ComponentView<Empty>()
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
||||
|
||||
@@ -496,49 +493,13 @@ private class BottomPanelNode: ASDisplayNode {
|
||||
self.strings = strings
|
||||
self.action = action
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
self.iconNode.isUserInteractionEnabled = false
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: strings.WebBrowser_Bookmarks_BookmarkCurrent, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor)
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
||||
self.button = HighlightTrackingButtonNode()
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor
|
||||
|
||||
self.addSubnode(self.button)
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.button)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let self {
|
||||
if highlighted {
|
||||
self.iconNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.iconNode.alpha = 0.4
|
||||
|
||||
self.textNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.textNode.alpha = 0.4
|
||||
} else {
|
||||
self.iconNode.alpha = 1.0
|
||||
self.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
|
||||
self.textNode.alpha = 1.0
|
||||
self.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.view.addSubview(self.edgeEffectView)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
@@ -551,26 +512,76 @@ private class BottomPanelNode: ASDisplayNode {
|
||||
var bottomInset = bottomInset
|
||||
bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0)
|
||||
|
||||
let buttonHeight: CGFloat = 40.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width, height: 44.0))
|
||||
// let buttonHeight: CGFloat = 40.0
|
||||
// let textSize = self.textNode.updateLayout(CGSize(width: width, height: 44.0))
|
||||
//
|
||||
// let spacing: CGFloat = 8.0
|
||||
// var contentWidth = textSize.width
|
||||
// var contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
// if let icon = self.iconNode.image {
|
||||
// contentWidth += icon.size.width + spacing
|
||||
// contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
// transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: contentOriginX, y: 12.0 + UIScreenPixel), size: icon.size))
|
||||
// contentOriginX += icon.size.width + spacing
|
||||
// }
|
||||
// let textFrame = CGRect(origin: CGPoint(x: contentOriginX, y: 17.0), size: textSize)
|
||||
// transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
//
|
||||
// transition.updateFrame(node: self.button, frame: textFrame.insetBy(dx: -10.0, dy: -10.0))
|
||||
//
|
||||
// transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
//
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let height: CGFloat = 52.0 + buttonInsets.bottom
|
||||
|
||||
let spacing: CGFloat = 8.0
|
||||
var contentWidth = textSize.width
|
||||
var contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
if let icon = self.iconNode.image {
|
||||
contentWidth += icon.size.width + spacing
|
||||
contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: contentOriginX, y: 12.0 + UIScreenPixel), size: icon.size))
|
||||
contentOriginX += icon.size.width + spacing
|
||||
let edgeEffectHeight: CGFloat = height
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: height - edgeEffectHeight), size: CGSize(width: width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(
|
||||
content: self.theme.list.plainBackgroundColor,
|
||||
blur: true,
|
||||
rect: edgeEffectFrame,
|
||||
edge: .bottom,
|
||||
edgeSize: edgeEffectFrame.height,
|
||||
transition: ComponentTransition(transition)
|
||||
)
|
||||
|
||||
var buttonItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
buttonItems.append(AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Fave", tintColor: self.theme.list.itemCheckColors.foregroundColor))))
|
||||
buttonItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: self.strings.WebBrowser_Bookmarks_BookmarkCurrent, font: Font.semibold(17.0), color: self.theme.list.itemCheckColors.foregroundColor))))
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: self.theme.list.itemCheckColors.fillColor,
|
||||
foreground: self.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: self.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(HStack(buttonItems, spacing: 7.0))
|
||||
),
|
||||
action: { [weak self] in
|
||||
self?.action()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: width - sideInset * 2.0 - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset + buttonInsets.left, y: height - buttonInsets.bottom - buttonSize.height), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.view.addSubview(buttonView)
|
||||
}
|
||||
transition.updateFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: contentOriginX, y: 17.0), size: textSize)
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
transition.updateFrame(node: self.button, frame: textFrame.insetBy(dx: -10.0, dy: -10.0))
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
return topInset + buttonHeight + bottomInset
|
||||
return height
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,12 +90,21 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
private let updateLayoutDisposable = MetaDisposable()
|
||||
|
||||
private let loadProgress = ValuePromise<CGFloat>(1.0, ignoreRepeated: true)
|
||||
private let readingProgress = ValuePromise<CGFloat>(1.0, ignoreRepeated: true)
|
||||
private let readingProgress = ValuePromise<CGFloat>(0.0, ignoreRepeated: true)
|
||||
|
||||
private var containerLayout: (size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets)?
|
||||
private var setupScrollOffsetOnLayout = false
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, webPage: TelegramMediaWebpage, anchor: String?, url: String, sourceLocation: InstantPageSourceLocation, preloadedResouces: [Any]?, originalContent: BrowserContent? = nil) {
|
||||
init(
|
||||
context: AccountContext,
|
||||
presentationData: PresentationData,
|
||||
webPage: TelegramMediaWebpage,
|
||||
anchor: String?,
|
||||
url: String,
|
||||
sourceLocation: InstantPageSourceLocation,
|
||||
preloadedResouces: [Any]?,
|
||||
originalContent: BrowserContent? = nil
|
||||
) {
|
||||
self.context = context
|
||||
var instantPage: InstantPage?
|
||||
if case let .Loaded(content) = webPage.content {
|
||||
@@ -132,6 +141,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.backgroundColor = self.theme.pageBackgroundColor
|
||||
|
||||
self.statePromise.set(.single(self._state)
|
||||
|> then(
|
||||
combineLatest(
|
||||
@@ -193,6 +204,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: self.settings)
|
||||
self.backgroundColor = self.theme.pageBackgroundColor
|
||||
|
||||
self.updatePageLayout()
|
||||
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
|
||||
}
|
||||
@@ -415,8 +428,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
var updateVisibleItems = false
|
||||
let resetContentOffset = self.scrollNode.bounds.size.width.isZero || self.setupScrollOffsetOnLayout || !(self.initialAnchor ?? "").isEmpty
|
||||
|
||||
var scrollInsets = insets
|
||||
scrollInsets.top = 0.0
|
||||
let scrollInsets = fullInsets
|
||||
if self.scrollNode.view.contentInset != scrollInsets {
|
||||
self.scrollNode.view.contentInset = scrollInsets
|
||||
self.scrollNode.view.scrollIndicatorInsets = scrollInsets
|
||||
@@ -424,7 +436,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
|
||||
self.wrapperNode.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
let scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top))
|
||||
let scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
let scrollFrameUpdated = self.scrollNode.bounds.size != scrollFrame.size
|
||||
if scrollFrameUpdated {
|
||||
let widthUpdated = self.scrollNode.bounds.size.width != scrollFrame.width
|
||||
|
||||
@@ -2,8 +2,10 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import BlurredBackgroundComponent
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
import GlassBackgroundComponent
|
||||
import EdgeEffect
|
||||
|
||||
final class BrowserNavigationBarEnvironment: Equatable {
|
||||
public let fraction: CGFloat
|
||||
@@ -20,7 +22,7 @@ final class BrowserNavigationBarEnvironment: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
final class BrowserNavigationBarComponent: Component {
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var centerItemFrame: CGRect
|
||||
|
||||
@@ -29,11 +31,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
let backgroundColor: UIColor
|
||||
let separatorColor: UIColor
|
||||
let textColor: UIColor
|
||||
let progressColor: UIColor
|
||||
let accentColor: UIColor
|
||||
let theme: PresentationTheme
|
||||
let topInset: CGFloat
|
||||
let height: CGFloat
|
||||
let sideInset: CGFloat
|
||||
@@ -42,17 +40,11 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
let leftItems: [AnyComponentWithIdentity<Empty>]
|
||||
let rightItems: [AnyComponentWithIdentity<Empty>]
|
||||
let centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?
|
||||
let readingProgress: CGFloat
|
||||
let loadingProgress: Double?
|
||||
let collapseFraction: CGFloat
|
||||
let activate: () -> Void
|
||||
|
||||
init(
|
||||
backgroundColor: UIColor,
|
||||
separatorColor: UIColor,
|
||||
textColor: UIColor,
|
||||
progressColor: UIColor,
|
||||
accentColor: UIColor,
|
||||
theme: PresentationTheme,
|
||||
topInset: CGFloat,
|
||||
height: CGFloat,
|
||||
sideInset: CGFloat,
|
||||
@@ -61,16 +53,10 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
leftItems: [AnyComponentWithIdentity<Empty>],
|
||||
rightItems: [AnyComponentWithIdentity<Empty>],
|
||||
centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
collapseFraction: CGFloat,
|
||||
activate: @escaping () -> Void
|
||||
) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.separatorColor = separatorColor
|
||||
self.textColor = textColor
|
||||
self.progressColor = progressColor
|
||||
self.accentColor = accentColor
|
||||
self.theme = theme
|
||||
self.topInset = topInset
|
||||
self.height = height
|
||||
self.sideInset = sideInset
|
||||
@@ -79,26 +65,12 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
self.leftItems = leftItems
|
||||
self.rightItems = rightItems
|
||||
self.centerItem = centerItem
|
||||
self.readingProgress = readingProgress
|
||||
self.loadingProgress = loadingProgress
|
||||
self.collapseFraction = collapseFraction
|
||||
self.activate = activate
|
||||
}
|
||||
|
||||
static func ==(lhs: BrowserNavigationBarComponent, rhs: BrowserNavigationBarComponent) -> Bool {
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
if lhs.separatorColor != rhs.separatorColor {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor != rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.progressColor != rhs.progressColor {
|
||||
return false
|
||||
}
|
||||
if lhs.accentColor != rhs.accentColor {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.topInset != rhs.topInset {
|
||||
@@ -122,203 +94,353 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
if lhs.centerItem != rhs.centerItem {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.collapseFraction != rhs.collapseFraction {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let background = Child(Rectangle.self)
|
||||
let readingProgress = Child(Rectangle.self)
|
||||
let separator = Child(Rectangle.self)
|
||||
let loadingProgress = Child(LoadingProgressComponent.self)
|
||||
let leftItems = 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 activate = Child(Button.self)
|
||||
final class View: UIView {
|
||||
private var edgeEffectView = EdgeEffectView()
|
||||
private let containerView = GlassBackgroundContainerView()
|
||||
|
||||
private var leftItemsBackground: GlassBackgroundView?
|
||||
private var leftItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
return { context in
|
||||
var availableWidth = context.availableSize.width
|
||||
let sideInset: CGFloat = (context.component.metrics.isTablet ? 20.0 : 16.0) + context.component.sideInset
|
||||
private var rightItemsBackground: GlassBackgroundView?
|
||||
private var rightItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var centerItems: [AnyHashable: ComponentView<BrowserNavigationBarEnvironment>] = [:]
|
||||
|
||||
private let activateButton = HighlightTrackingButton()
|
||||
|
||||
private var component: BrowserNavigationBarComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
let collapsedHeight: CGFloat = 24.0
|
||||
let expandedHeight = context.component.height
|
||||
let contentHeight: CGFloat = expandedHeight * (1.0 - context.component.collapseFraction) + collapsedHeight * context.component.collapseFraction
|
||||
let size = CGSize(width: context.availableSize.width, height: context.component.topInset + contentHeight)
|
||||
let verticalOffset: CGFloat = context.component.metrics.isTablet ? -2.0 : 0.0
|
||||
let itemSpacing: CGFloat = context.component.metrics.isTablet ? 26.0 : 8.0
|
||||
self.addSubview(self.edgeEffectView)
|
||||
|
||||
let background = background.update(
|
||||
component: Rectangle(color: context.component.backgroundColor.withAlphaComponent(1.0)),
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let readingProgress = readingProgress.update(
|
||||
component: Rectangle(color: context.component.progressColor),
|
||||
availableSize: CGSize(width: size.width * context.component.readingProgress, height: size.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let separator = separator.update(
|
||||
component: Rectangle(color: context.component.separatorColor, height: UIScreenPixel),
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let loadingProgressHeight: CGFloat = 2.0
|
||||
let loadingProgress = loadingProgress.update(
|
||||
component: LoadingProgressComponent(
|
||||
color: context.component.accentColor,
|
||||
height: loadingProgressHeight,
|
||||
value: context.component.loadingProgress ?? 0.0
|
||||
),
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
var leftItemList: [_UpdatedChildComponent] = []
|
||||
for item in context.component.leftItems {
|
||||
let item = leftItems[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: availableWidth, height: expandedHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
leftItemList.append(item)
|
||||
availableWidth -= item.size.width
|
||||
}
|
||||
|
||||
var rightItemList: [_UpdatedChildComponent] = []
|
||||
for item in context.component.rightItems {
|
||||
let item = rightItems[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: availableWidth, height: expandedHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
rightItemList.append(item)
|
||||
availableWidth -= item.size.width
|
||||
self.addSubview(self.containerView)
|
||||
self.activateButton.addTarget(self, action: #selector(self.activatePressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func activatePressed() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.activate()
|
||||
}
|
||||
|
||||
func update(component: BrowserNavigationBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
var availableWidth = availableSize.width
|
||||
let sideInset: CGFloat = (component.metrics.isTablet ? 20.0 : 16.0) + component.sideInset
|
||||
|
||||
let collapsedHeight: CGFloat = 54.0
|
||||
let expandedHeight = component.height
|
||||
let contentHeight: CGFloat = expandedHeight * (1.0 - component.collapseFraction) + collapsedHeight * component.collapseFraction
|
||||
let size = CGSize(width: availableSize.width, height: component.topInset + contentHeight)
|
||||
let verticalOffset: CGFloat = component.metrics.isTablet ? -2.0 : 0.0
|
||||
let itemSpacing: CGFloat = 0.0 //component.metrics.isTablet ? 26.0 : 8.0
|
||||
let panelHeight: CGFloat = 44.0
|
||||
|
||||
var leftItemsBackground: GlassBackgroundView?
|
||||
var leftItemsBackgroundTransition = transition
|
||||
if !component.leftItems.isEmpty {
|
||||
if let current = self.leftItemsBackground {
|
||||
leftItemsBackground = current
|
||||
} else {
|
||||
leftItemsBackgroundTransition = .immediate
|
||||
leftItemsBackground = GlassBackgroundView()
|
||||
self.containerView.contentView.addSubview(leftItemsBackground!)
|
||||
self.leftItemsBackground = leftItemsBackground
|
||||
|
||||
context.add(background
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||
)
|
||||
|
||||
var readingProgressAlpha = context.component.collapseFraction
|
||||
if leftItemList.isEmpty && rightItemList.isEmpty {
|
||||
readingProgressAlpha = 0.0
|
||||
transition.animateScale(view: leftItemsBackground!, from: 0.1, to: 1.0)
|
||||
transition.animateAlpha(view: leftItemsBackground!, from: 0.0, to: 1.0)
|
||||
}
|
||||
}
|
||||
context.add(readingProgress
|
||||
.position(CGPoint(x: readingProgress.size.width / 2.0, y: size.height / 2.0))
|
||||
.opacity(readingProgressAlpha)
|
||||
)
|
||||
|
||||
context.add(separator
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height))
|
||||
)
|
||||
|
||||
context.add(loadingProgress
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height - loadingProgressHeight / 2.0))
|
||||
)
|
||||
var rightItemsBackground: GlassBackgroundView?
|
||||
var rightItemsBackgroundTransition = transition
|
||||
if !component.rightItems.isEmpty {
|
||||
if let current = self.rightItemsBackground {
|
||||
rightItemsBackground = current
|
||||
} else {
|
||||
rightItemsBackgroundTransition = .immediate
|
||||
rightItemsBackground = GlassBackgroundView()
|
||||
self.containerView.contentView.addSubview(rightItemsBackground!)
|
||||
self.rightItemsBackground = rightItemsBackground
|
||||
|
||||
transition.animateScale(view: rightItemsBackground!, from: 0.1, to: 1.0)
|
||||
transition.animateAlpha(view: rightItemsBackground!, from: 0.0, to: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
var validLeftItemIds: Set<AnyHashable> = Set()
|
||||
var leftItemTransitions: [AnyHashable: (CGSize, ComponentTransition)] = [:]
|
||||
var leftItemsWidth: CGFloat = 0.0
|
||||
for item in component.leftItems {
|
||||
validLeftItemIds.insert(item.id)
|
||||
var itemTransition = transition
|
||||
let itemView: ComponentView<Empty>
|
||||
if let current = self.leftItems[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
itemView = ComponentView<Empty>()
|
||||
self.leftItems[item.id] = itemView
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: item.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableWidth, height: expandedHeight)
|
||||
)
|
||||
leftItemTransitions[item.id] = (itemSize, itemTransition)
|
||||
availableWidth -= itemSize.width
|
||||
leftItemsWidth += itemSize.width
|
||||
}
|
||||
|
||||
var validRightItemIds: Set<AnyHashable> = Set()
|
||||
var rightItemTransitions: [AnyHashable: (CGSize, ComponentTransition)] = [:]
|
||||
var rightItemsWidth: CGFloat = 0.0
|
||||
for item in component.rightItems {
|
||||
validRightItemIds.insert(item.id)
|
||||
var itemTransition = transition
|
||||
let itemView: ComponentView<Empty>
|
||||
if let current = self.rightItems[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
itemView = ComponentView<Empty>()
|
||||
self.rightItems[item.id] = itemView
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: item.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableWidth, height: expandedHeight)
|
||||
)
|
||||
rightItemTransitions[item.id] = (itemSize, itemTransition)
|
||||
availableWidth -= itemSize.width
|
||||
rightItemsWidth += itemSize.width
|
||||
}
|
||||
|
||||
var centerLeftInset = sideInset
|
||||
var leftItemX = sideInset
|
||||
for item in leftItemList {
|
||||
context.add(item
|
||||
.position(CGPoint(x: leftItemX + item.size.width / 2.0 - (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset))
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.opacity(1.0 - context.component.collapseFraction)
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
leftItemX += item.size.width + itemSpacing
|
||||
centerLeftInset += item.size.width + itemSpacing
|
||||
var leftItemX = 0.0
|
||||
for item in component.leftItems {
|
||||
guard let (itemSize, itemTransition) = leftItemTransitions[item.id], let itemView = self.leftItems[item.id]?.view else {
|
||||
continue
|
||||
}
|
||||
let itemPosition = CGPoint(x: leftItemX + itemSize.width / 2.0, y: panelHeight * 0.5)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize)
|
||||
if itemView.superview == nil {
|
||||
leftItemsBackground?.contentView.addSubview(itemView)
|
||||
transition.animateAlpha(view: itemView, from: 0.0, to: 1.0)
|
||||
transition.animateScale(view: itemView, from: 0.01, to: 1.0)
|
||||
}
|
||||
itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size))
|
||||
itemTransition.setPosition(view: itemView, position: itemFrame.center)
|
||||
|
||||
leftItemX += itemSize.width + itemSpacing
|
||||
centerLeftInset += itemSize.width + itemSpacing
|
||||
}
|
||||
|
||||
var centerRightInset = sideInset - 5.0
|
||||
var rightItemX = context.availableSize.width - (sideInset - 5.0)
|
||||
for item in rightItemList.reversed() {
|
||||
context.add(item
|
||||
.position(CGPoint(x: rightItemX - item.size.width / 2.0 + (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset))
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.opacity(1.0 - context.component.collapseFraction)
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
rightItemX -= item.size.width + itemSpacing
|
||||
centerRightInset += item.size.width + itemSpacing
|
||||
|
||||
var centerRightInset = sideInset
|
||||
var rightItemX = rightItemsWidth
|
||||
for item in component.rightItems.reversed() {
|
||||
guard let (itemSize, itemTransition) = rightItemTransitions[item.id], let itemView = self.rightItems[item.id]?.view else {
|
||||
continue
|
||||
}
|
||||
let itemPosition = CGPoint(x: rightItemX - itemSize.width / 2.0, y: panelHeight * 0.5)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize)
|
||||
if itemView.superview == nil {
|
||||
rightItemsBackground?.contentView.addSubview(itemView)
|
||||
transition.animateAlpha(view: itemView, from: 0.0, to: 1.0)
|
||||
transition.animateScale(view: itemView, from: 0.01, to: 1.0)
|
||||
}
|
||||
itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size))
|
||||
itemTransition.setPosition(view: itemView, position: itemFrame.center)
|
||||
itemTransition.setScale(view: itemView, scale: 1.0 - 0.35 * component.collapseFraction)
|
||||
itemTransition.setAlpha(view: itemView, alpha: 1.0 - component.collapseFraction)
|
||||
|
||||
rightItemX -= itemSize.width + itemSpacing
|
||||
centerRightInset += itemSize.width + itemSpacing
|
||||
}
|
||||
|
||||
if let leftItemsBackground {
|
||||
let leftItemsFrame = CGRect(origin: CGPoint(x: sideInset - (leftItemsWidth / 2.0 * 0.35 * component.collapseFraction), y: component.topInset + contentHeight / 2.0 + verticalOffset - panelHeight / 2.0), size: CGSize(width: leftItemsWidth, height: panelHeight))
|
||||
leftItemsBackgroundTransition.setFrame(view: leftItemsBackground, frame: leftItemsFrame)
|
||||
leftItemsBackground.update(size: leftItemsFrame.size, shape: .roundedRect(cornerRadius: leftItemsFrame.height * 0.5), isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: leftItemsBackgroundTransition)
|
||||
|
||||
leftItemsBackgroundTransition.setScale(view: leftItemsBackground, scale: 1.0 - 0.999 * component.collapseFraction)
|
||||
leftItemsBackgroundTransition.setAlpha(view: leftItemsBackground.contentView, alpha: 1.0 - component.collapseFraction)
|
||||
} else if let leftItemsBackground = self.leftItemsBackground {
|
||||
self.leftItemsBackground = nil
|
||||
leftItemsBackground.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let rightItemsBackground {
|
||||
let rightItemsFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - rightItemsWidth * (1.0 - component.collapseFraction) + (rightItemsWidth / 2.0 * 0.35 * component.collapseFraction), y: component.topInset + contentHeight / 2.0 + verticalOffset - panelHeight / 2.0), size: CGSize(width: rightItemsWidth, height: panelHeight))
|
||||
rightItemsBackgroundTransition.setFrame(view: rightItemsBackground, frame: rightItemsFrame)
|
||||
rightItemsBackground.update(size: rightItemsFrame.size, shape: .roundedRect(cornerRadius: rightItemsFrame.height * 0.5), isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightItemsBackgroundTransition)
|
||||
|
||||
rightItemsBackgroundTransition.setScale(view: rightItemsBackground, scale: 1.0 - 0.999 * component.collapseFraction)
|
||||
rightItemsBackgroundTransition.setAlpha(view: rightItemsBackground.contentView, alpha: 1.0 - component.collapseFraction)
|
||||
} else if let rightItemsBackground = self.rightItemsBackground {
|
||||
self.rightItemsBackground = nil
|
||||
rightItemsBackground.removeFromSuperview()
|
||||
}
|
||||
|
||||
var removeLeftItemIds: [AnyHashable] = []
|
||||
for (id, item) in self.leftItems {
|
||||
if !validLeftItemIds.contains(id) {
|
||||
removeLeftItemIds.append(id)
|
||||
if let itemView = item.view {
|
||||
transition.setScale(view: itemView, scale: 0.01)
|
||||
transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeLeftItemIds {
|
||||
self.leftItems.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
var removeRightItemIds: [AnyHashable] = []
|
||||
for (id, item) in self.rightItems {
|
||||
if !validRightItemIds.contains(id) {
|
||||
removeRightItemIds.append(id)
|
||||
if let itemView = item.view {
|
||||
transition.setScale(view: itemView, scale: 0.01)
|
||||
transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeRightItemIds {
|
||||
self.rightItems.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
let maxCenterInset = max(centerLeftInset, centerRightInset)
|
||||
|
||||
if !leftItemList.isEmpty || !rightItemList.isEmpty {
|
||||
availableWidth -= itemSpacing * CGFloat(max(0, leftItemList.count - 1)) + itemSpacing * CGFloat(max(0, rightItemList.count - 1)) + 30.0
|
||||
if !component.leftItems.isEmpty || !component.rightItems.isEmpty {
|
||||
availableWidth -= itemSpacing * CGFloat(max(0, component.leftItems.count - 1)) + itemSpacing * CGFloat(max(0, component.rightItems.count - 1)) + 30.0
|
||||
}
|
||||
availableWidth -= context.component.sideInset * 2.0
|
||||
availableWidth -= component.sideInset * 2.0
|
||||
|
||||
let canCenter = availableWidth > 660.0
|
||||
availableWidth = min(660.0, availableWidth)
|
||||
let canCenter = availableWidth > 390.0
|
||||
availableWidth = min(390.0, availableWidth)
|
||||
|
||||
let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction)
|
||||
let environment = BrowserNavigationBarEnvironment(fraction: component.collapseFraction)
|
||||
|
||||
let centerItem = context.component.centerItem.flatMap { item in
|
||||
centerItems[item.id].update(
|
||||
var centerX = maxCenterInset + (availableSize.width - maxCenterInset * 2.0) / 2.0
|
||||
if canCenter {
|
||||
centerX = availableSize.width / 2.0
|
||||
} else {
|
||||
centerX = centerLeftInset + (availableSize.width - centerLeftInset - centerRightInset) / 2.0
|
||||
}
|
||||
|
||||
var validCenterItemIds: Set<AnyHashable> = Set()
|
||||
if let item = component.centerItem {
|
||||
validCenterItemIds.insert(item.id)
|
||||
|
||||
var itemTransition = transition
|
||||
let itemView: ComponentView<BrowserNavigationBarEnvironment>
|
||||
if let current = self.centerItems[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
itemView = ComponentView<BrowserNavigationBarEnvironment>()
|
||||
self.centerItems[item.id] = itemView
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: item.component,
|
||||
environment: { environment },
|
||||
availableSize: CGSize(width: availableWidth, height: expandedHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
}
|
||||
|
||||
var centerX = maxCenterInset + (context.availableSize.width - maxCenterInset * 2.0) / 2.0
|
||||
if "".isEmpty {
|
||||
if canCenter {
|
||||
centerX = context.availableSize.width / 2.0
|
||||
} else {
|
||||
centerX = centerLeftInset + (context.availableSize.width - centerLeftInset - centerRightInset) / 2.0
|
||||
}
|
||||
}
|
||||
if let centerItem = centerItem {
|
||||
let centerItemPosition = CGPoint(x: centerX, y: context.component.topInset + contentHeight / 2.0 + verticalOffset)
|
||||
context.add(centerItem
|
||||
.position(centerItemPosition)
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.appear(.default(scale: false, alpha: true))
|
||||
.disappear(.default(scale: false, alpha: true))
|
||||
containerSize: CGSize(width: availableWidth, height: expandedHeight)
|
||||
)
|
||||
|
||||
context.component.externalState?.centerItemFrame = centerItem.size.centered(around: centerItemPosition)
|
||||
let itemPosition = CGPoint(x: centerX, y: component.topInset + contentHeight / 2.0 + verticalOffset)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize)
|
||||
if let itemView = itemView.view {
|
||||
if itemView.superview == nil {
|
||||
self.containerView.contentView.addSubview(itemView)
|
||||
transition.animateAlpha(view: itemView, from: 0.0, to: 1.0)
|
||||
}
|
||||
itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size))
|
||||
itemTransition.setPosition(view: itemView, position: itemFrame.center)
|
||||
itemTransition.setScale(view: itemView, scale: 1.0 - 0.25 * component.collapseFraction)
|
||||
}
|
||||
component.externalState?.centerItemFrame = itemFrame
|
||||
}
|
||||
|
||||
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))
|
||||
)
|
||||
var removeCenterItemIds: [AnyHashable] = []
|
||||
for (id, item) in self.centerItems {
|
||||
if !validCenterItemIds.contains(id) {
|
||||
removeCenterItemIds.append(id)
|
||||
if let itemView = item.view {
|
||||
transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeCenterItemIds {
|
||||
self.centerItems.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if component.collapseFraction == 1.0 {
|
||||
if self.activateButton.superview == nil {
|
||||
self.addSubview(self.activateButton)
|
||||
}
|
||||
self.activateButton.frame = CGRect(origin: .zero, size: size)
|
||||
} else {
|
||||
self.activateButton.removeFromSuperview()
|
||||
}
|
||||
|
||||
self.containerView.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition)
|
||||
transition.setFrame(view: self.containerView, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
let edgeEffectHeight: CGFloat = 80.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(
|
||||
content: .clear,
|
||||
blur: true,
|
||||
rect: edgeEffectFrame,
|
||||
edge: .top,
|
||||
edgeSize: edgeEffectFrame.height,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class LoadingProgressComponent: Component {
|
||||
final class LoadingProgressComponent: Component {
|
||||
let color: UIColor
|
||||
let height: CGFloat
|
||||
let value: CGFloat
|
||||
@@ -414,111 +536,3 @@ private final class LoadingProgressComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class ReferenceButtonComponent: Component {
|
||||
let content: AnyComponent<Empty>
|
||||
let tag: AnyObject?
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
content: AnyComponent<Empty>,
|
||||
tag: AnyObject? = nil,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.content = content
|
||||
self.tag = tag
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: ReferenceButtonComponent, rhs: ReferenceButtonComponent) -> Bool {
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
if lhs.tag !== rhs.tag {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: HighlightTrackingButton, ComponentTaggedView {
|
||||
private let sourceView: ContextControllerSourceView
|
||||
let referenceNode: ContextReferenceContentNode
|
||||
let componentView: ComponentView<Empty>
|
||||
|
||||
private var component: ReferenceButtonComponent?
|
||||
|
||||
public func matches(tag: Any) -> Bool {
|
||||
if let component = self.component, let componentTag = component.tag {
|
||||
let tag = tag as AnyObject
|
||||
if componentTag === tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
init() {
|
||||
self.componentView = ComponentView()
|
||||
self.sourceView = ContextControllerSourceView()
|
||||
self.sourceView.animateScale = false
|
||||
self.referenceNode = ContextReferenceContentNode()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.sourceView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.sourceView)
|
||||
self.sourceView.addSubnode(self.referenceNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self, let contentView = strongSelf.componentView.view {
|
||||
if highlighted {
|
||||
contentView.layer.removeAnimation(forKey: "opacity")
|
||||
contentView.alpha = 0.4
|
||||
} else {
|
||||
contentView.alpha = 1.0
|
||||
contentView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.component?.action()
|
||||
}
|
||||
|
||||
func update(component: ReferenceButtonComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let componentSize = self.componentView.update(
|
||||
transition: transition,
|
||||
component: component.content,
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let componentView = self.componentView.view {
|
||||
if componentView.superview == nil {
|
||||
self.referenceNode.view.addSubview(componentView)
|
||||
}
|
||||
transition.setFrame(view: componentView, frame: CGRect(origin: .zero, size: componentSize))
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.sourceView, frame: CGRect(origin: .zero, size: componentSize))
|
||||
self.referenceNode.frame = CGRect(origin: .zero, size: componentSize)
|
||||
|
||||
return componentSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import ShareController
|
||||
import UndoUI
|
||||
import UrlEscaping
|
||||
import PDFKit
|
||||
import GlassBackgroundComponent
|
||||
|
||||
final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDFDocumentDelegate {
|
||||
private let context: AccountContext
|
||||
@@ -25,7 +26,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
private let pdfView: PDFView
|
||||
private let scrollView: UIScrollView!
|
||||
|
||||
private let pageIndicatorBackgorund: UIVisualEffectView
|
||||
private let pageIndicatorBackground = GlassBackgroundView()
|
||||
private let pageIndicator = ComponentView<Empty>()
|
||||
private var pageNumber: (Int, Int)?
|
||||
private var pageTimer: SwiftSignalKit.Timer?
|
||||
@@ -61,11 +62,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
|
||||
self.pdfView = PDFView()
|
||||
self.pdfView.clipsToBounds = false
|
||||
|
||||
self.pageIndicatorBackgorund = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
self.pageIndicatorBackgorund.clipsToBounds = true
|
||||
self.pageIndicatorBackgorund.layer.cornerRadius = 10.0
|
||||
|
||||
|
||||
var scrollView: UIScrollView?
|
||||
for view in self.pdfView.subviews {
|
||||
if let view = view as? UIScrollView {
|
||||
@@ -170,7 +167,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
return
|
||||
}
|
||||
let transition = ComponentTransition.easeInOut(duration: 0.25)
|
||||
transition.setAlpha(view: self.pageIndicatorBackgorund, alpha: 0.0)
|
||||
transition.setAlpha(view: self.pageIndicatorBackground, alpha: 0.0)
|
||||
}, queue: Queue.mainQueue())
|
||||
self.pageTimer?.start()
|
||||
}
|
||||
@@ -354,7 +351,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
self.validLayout = (size, insets, fullInsets)
|
||||
|
||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.scrollView.contentOffset.y, isDraggingOrDecelerating: self.scrollView.isDragging || self.scrollView.isDecelerating)
|
||||
|
||||
|
||||
let currentBounds = self.scrollView.bounds
|
||||
let offsetToBottomEdge = max(0.0, self.scrollView.contentSize.height - currentBounds.maxY)
|
||||
var bottomInset = insets.bottom
|
||||
@@ -368,23 +365,24 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
let pageIndicatorSize = self.pageIndicator.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
Text(text: "\(self.pageNumber?.0 ?? 1) of \(self.pageNumber?.1 ?? 1)", font: Font.with(size: 15.0, weight: .semibold, traits: .monospacedNumbers), color: self.presentationData.theme.list.itemSecondaryTextColor)
|
||||
Text(text: "\(self.pageNumber?.0 ?? 1) of \(self.pageNumber?.1 ?? 1)", font: Font.with(size: 15.0, weight: .regular, traits: .monospacedNumbers), color: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let view = self.pageIndicator.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(self.pageIndicatorBackgorund)
|
||||
self.pageIndicatorBackgorund.contentView.addSubview(view)
|
||||
self.addSubview(self.pageIndicatorBackground)
|
||||
self.pageIndicatorBackground.contentView.addSubview(view)
|
||||
}
|
||||
|
||||
|
||||
let horizontalPadding: CGFloat = 10.0
|
||||
let verticalPadding: CGFloat = 8.0
|
||||
let pageBackgroundFrame = CGRect(origin: CGPoint(x: insets.left + 20.0, y: insets.top + 16.0), size: CGSize(width: horizontalPadding * 2.0 + pageIndicatorSize.width, height: verticalPadding * 2.0 + pageIndicatorSize.height))
|
||||
let pageBackgroundFrame = CGRect(origin: CGPoint(x: insets.left + 16.0, y: insets.top + 16.0), size: CGSize(width: horizontalPadding * 2.0 + pageIndicatorSize.width, height: verticalPadding * 2.0 + pageIndicatorSize.height))
|
||||
|
||||
self.pageIndicatorBackgorund.bounds = CGRect(origin: .zero, size: pageBackgroundFrame.size)
|
||||
transition.setPosition(view: self.pageIndicatorBackgorund, position: pageBackgroundFrame.center)
|
||||
self.pageIndicatorBackground.update(size: pageBackgroundFrame.size, cornerRadius: pageBackgroundFrame.size.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition)
|
||||
self.pageIndicatorBackground.bounds = CGRect(origin: .zero, size: pageBackgroundFrame.size)
|
||||
transition.setPosition(view: self.pageIndicatorBackground, position: pageBackgroundFrame.center)
|
||||
view.frame = CGRect(origin: CGPoint(x: horizontalPadding, y: verticalPadding), size: pageIndicatorSize)
|
||||
}
|
||||
|
||||
@@ -459,7 +457,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
|
||||
}
|
||||
|
||||
let transition = ComponentTransition.easeInOut(duration: 0.1)
|
||||
transition.setAlpha(view: self.pageIndicatorBackgorund, alpha: 1.0)
|
||||
transition.setAlpha(view: self.pageIndicatorBackground, alpha: 1.0)
|
||||
|
||||
self.pageTimer?.invalidate()
|
||||
self.pageTimer = nil
|
||||
|
||||
@@ -20,6 +20,7 @@ import InstantPageUI
|
||||
import NavigationStackComponent
|
||||
import LottieComponent
|
||||
import WebKit
|
||||
import GlassBarButtonComponent
|
||||
|
||||
private let settingsTag = GenericComponentViewTag()
|
||||
|
||||
@@ -85,6 +86,8 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
|
||||
let navigationBarExternalState = BrowserNavigationBarComponent.ExternalState()
|
||||
|
||||
let moreButtonPlayOnce = ActionSlot<Void>()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let performAction = context.component.performAction
|
||||
@@ -123,6 +126,8 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
url: context.component.contentState?.url ?? "",
|
||||
isSecure: context.component.contentState?.isSecure ?? false,
|
||||
isExpanded: context.component.presentationState.addressFocused,
|
||||
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
||||
loadingProgress: context.component.contentState?.estimatedProgress,
|
||||
performAction: performAction
|
||||
)
|
||||
)
|
||||
@@ -134,7 +139,9 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
component: AnyComponent(
|
||||
TitleBarContentComponent(
|
||||
theme: environment.theme,
|
||||
title: title
|
||||
title: title,
|
||||
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
||||
loadingProgress: context.component.contentState?.estimatedProgress
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -150,37 +157,40 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.WebBrowser_Done, font: Font.semibold(17.0), textColor: environment.theme.rootController.navigationBar.accentTextColor, paragraphAlignment: .center)), horizontalAlignment: .left, maximumNumberOfLines: 1)
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.close)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
if isTablet {
|
||||
#if DEBUG
|
||||
navigationLeftItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "minimize",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Media Gallery/PictureInPictureButton",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.close)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
#endif
|
||||
// #if DEBUG
|
||||
// navigationLeftItems.append(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "minimize",
|
||||
// component: AnyComponent(
|
||||
// Button(
|
||||
// content: AnyComponent(
|
||||
// BundleIconComponent(
|
||||
// name: "Media Gallery/PictureInPictureButton",
|
||||
// tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
// performAction.invoke(.close)
|
||||
// }
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// #endif
|
||||
|
||||
let canGoBack = context.component.contentState?.canGoBack ?? false
|
||||
let canGoForward = context.component.contentState?.canGoForward ?? false
|
||||
@@ -193,13 +203,13 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Back",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoBack ? 1.0 : 0.4)
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor.withAlphaComponent(canGoBack ? 1.0 : 0.4)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.navigateBack)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -212,13 +222,13 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Forward",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoForward ? 1.0 : 0.4)
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor.withAlphaComponent(canGoForward ? 1.0 : 0.4)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.navigateForward)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -228,21 +238,22 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
AnyComponentWithIdentity(
|
||||
id: "settings",
|
||||
component: AnyComponent(
|
||||
ReferenceButtonComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(
|
||||
name: "anim_moredots"
|
||||
name: "anim_morewide"
|
||||
),
|
||||
color: environment.theme.rootController.navigationBar.accentTextColor,
|
||||
size: CGSize(width: 30.0, height: 30.0)
|
||||
color: environment.theme.chat.inputPanel.panelControlColor,
|
||||
size: CGSize(width: 34.0, height: 34.0),
|
||||
playOnce: moreButtonPlayOnce
|
||||
)
|
||||
),
|
||||
tag: settingsTag,
|
||||
action: {
|
||||
performAction.invoke(.openSettings)
|
||||
moreButtonPlayOnce.invoke(Void())
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(settingsTag)
|
||||
)
|
||||
)
|
||||
]
|
||||
@@ -256,13 +267,13 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Bookmark",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openBookmarks)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
),
|
||||
at: 0
|
||||
@@ -275,14 +286,14 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat List/NavigationShare",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
name: "Instant View/Share",
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.share)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
),
|
||||
at: 0
|
||||
@@ -297,13 +308,13 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Browser",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openIn)
|
||||
}
|
||||
)
|
||||
).minSize(CGSize(width: 44.0, height: 44.0))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -316,21 +327,15 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
|
||||
let navigationBar = navigationBar.update(
|
||||
component: BrowserNavigationBarComponent(
|
||||
backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor,
|
||||
separatorColor: environment.theme.rootController.navigationBar.separatorColor,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
progressColor: environment.theme.rootController.navigationBar.segmentedBackgroundColor,
|
||||
accentColor: environment.theme.rootController.navigationBar.accentTextColor,
|
||||
theme: environment.theme,
|
||||
topInset: environment.statusBarHeight,
|
||||
height: environment.navigationHeight - environment.statusBarHeight,
|
||||
height: environment.navigationHeight - environment.statusBarHeight + 8.0,
|
||||
sideInset: environment.safeInsets.left,
|
||||
metrics: environment.metrics,
|
||||
externalState: navigationBarExternalState,
|
||||
leftItems: navigationLeftItems,
|
||||
rightItems: navigationRightItems,
|
||||
centerItem: navigationContent,
|
||||
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
||||
loadingProgress: context.component.contentState?.estimatedProgress,
|
||||
collapseFraction: collapseFraction,
|
||||
activate: {
|
||||
performAction.invoke(.expand)
|
||||
@@ -339,9 +344,6 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(navigationBar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0))
|
||||
)
|
||||
|
||||
let toolbarContent: AnyComponentWithIdentity<Empty>?
|
||||
if context.component.presentationState.isSearching {
|
||||
@@ -349,8 +351,8 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
id: "search",
|
||||
component: AnyComponent(
|
||||
SearchToolbarContentComponent(
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
index: context.component.presentationState.searchResultIndex,
|
||||
count: context.component.presentationState.searchResultCount,
|
||||
isEmpty: context.component.presentationState.searchQueryIsEmpty,
|
||||
@@ -363,8 +365,7 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
id: "navigation",
|
||||
component: AnyComponent(
|
||||
NavigationToolbarContentComponent(
|
||||
accentColor: environment.theme.rootController.navigationBar.accentTextColor,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
theme: environment.theme,
|
||||
canGoBack: context.component.contentState?.canGoBack ?? false,
|
||||
canGoForward: context.component.contentState?.canGoForward ?? false,
|
||||
canOpenIn: canOpenIn,
|
||||
@@ -384,15 +385,12 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
toolbarBottomInset = environment.safeInsets.bottom
|
||||
}
|
||||
|
||||
var toolbarSize: CGFloat = 0.0
|
||||
if isTablet && !context.component.presentationState.isSearching {
|
||||
|
||||
} else {
|
||||
let toolbar = toolbar.update(
|
||||
component: BrowserToolbarComponent(
|
||||
backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor,
|
||||
separatorColor: environment.theme.rootController.navigationBar.separatorColor,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
theme: environment.theme,
|
||||
bottomInset: toolbarBottomInset,
|
||||
sideInset: environment.safeInsets.left,
|
||||
item: toolbarContent,
|
||||
@@ -412,16 +410,9 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
})
|
||||
})
|
||||
)
|
||||
toolbarSize = toolbar.size.height
|
||||
}
|
||||
|
||||
if context.component.presentationState.addressFocused {
|
||||
let addressListSize: CGSize
|
||||
if isTablet {
|
||||
addressListSize = context.availableSize
|
||||
} else {
|
||||
addressListSize = CGSize(width: context.availableSize.width, height: context.availableSize.height - navigationBar.size.height - toolbarSize)
|
||||
}
|
||||
let controller = environment.controller
|
||||
let addressList = addressList.update(
|
||||
component: BrowserAddressListComponent(
|
||||
@@ -431,12 +422,13 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
insets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
metrics: environment.metrics,
|
||||
addressBarFrame: navigationBarExternalState.centerItemFrame,
|
||||
navigationBarHeight: navigationBar.size.height,
|
||||
performAction: performAction,
|
||||
presentInGlobalOverlay: { c in
|
||||
controller()?.presentInGlobalOverlay(c)
|
||||
}
|
||||
),
|
||||
availableSize: addressListSize,
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
@@ -448,7 +440,7 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
)
|
||||
} else {
|
||||
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: addressList.size.height / 2.0))
|
||||
.clipsToBounds(true)
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
@@ -456,6 +448,10 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
context.add(navigationBar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0))
|
||||
)
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
@@ -1075,7 +1071,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
|
||||
func openSettings() {
|
||||
guard let referenceView = self.componentHost.findTaggedView(tag: settingsTag) as? ReferenceButtonComponent.View else {
|
||||
guard let referenceView = self.componentHost.findTaggedView(tag: settingsTag) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1083,10 +1079,6 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
return
|
||||
}
|
||||
|
||||
if let animationComponentView = referenceView.componentView.view as? LottieComponent.View {
|
||||
animationComponentView.playOnce()
|
||||
}
|
||||
|
||||
if let webContent = content as? BrowserWebContent {
|
||||
webContent.requestInstantView()
|
||||
}
|
||||
@@ -1103,7 +1095,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
}
|
||||
|
||||
let source: ContextContentSource = .reference(BrowserReferenceContentSource(controller: controller, sourceView: referenceView.referenceNode.view))
|
||||
let source: ContextContentSource = .reference(BrowserReferenceContentSource(controller: controller, sourceView: referenceView))
|
||||
|
||||
let items: Signal<ContextController.Items, NoError> = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
@@ -1549,6 +1541,8 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self._hasGlassStyle = true
|
||||
|
||||
self.navigationPresentation = .modalInCompactLayout
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
|
||||
@@ -1731,7 +1725,7 @@ private final class BrowserContentComponent: Component {
|
||||
self.addSubview(component.content)
|
||||
}
|
||||
|
||||
let collapsedHeight: CGFloat = 24.0
|
||||
let collapsedHeight: CGFloat = 54.0
|
||||
let topInset: CGFloat = component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + (component.insets.top + collapsedHeight) * component.scrollingPanelOffsetFraction
|
||||
let bottomInset = component.hasBottomPanel ? (49.0 + component.insets.bottom) * (1.0 - component.scrollingPanelOffsetFraction) : 0.0
|
||||
let insets = UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right)
|
||||
|
||||
@@ -7,6 +7,7 @@ import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import BundleIconComponent
|
||||
import SearchInputPanelComponent
|
||||
|
||||
final class SearchBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
@@ -35,112 +36,16 @@ final class SearchBarContentComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView, UITextFieldDelegate {
|
||||
private final class SearchTextField: UITextField {
|
||||
override func textRect(forBounds bounds: CGRect) -> CGRect {
|
||||
return bounds.integral
|
||||
}
|
||||
}
|
||||
|
||||
private struct Params: Equatable {
|
||||
var theme: PresentationTheme
|
||||
var strings: PresentationStrings
|
||||
var size: CGSize
|
||||
|
||||
static func ==(lhs: Params, rhs: Params) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let queryPromise = ValuePromise<String>()
|
||||
private var queryDisposable: Disposable?
|
||||
|
||||
private let backgroundLayer: SimpleLayer
|
||||
private let searchInput = ComponentView<Empty>()
|
||||
|
||||
private let iconView: UIImageView
|
||||
|
||||
private let clearIconView: UIImageView
|
||||
private let clearIconButton: HighlightTrackingButton
|
||||
|
||||
private let cancelButtonTitle: ComponentView<Empty>
|
||||
private let cancelButton: HighlightTrackingButton
|
||||
|
||||
private var placeholderContent = ComponentView<Empty>()
|
||||
|
||||
private var textFrame: CGRect?
|
||||
private var textField: SearchTextField?
|
||||
|
||||
private var tapRecognizer: UITapGestureRecognizer?
|
||||
|
||||
private var params: Params?
|
||||
private var component: SearchBarContentComponent?
|
||||
|
||||
init() {
|
||||
self.backgroundLayer = SimpleLayer()
|
||||
|
||||
self.iconView = UIImageView()
|
||||
|
||||
self.clearIconView = UIImageView()
|
||||
self.clearIconButton = HighlightableButton()
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
|
||||
self.cancelButtonTitle = ComponentView()
|
||||
self.cancelButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.addSublayer(self.backgroundLayer)
|
||||
|
||||
self.addSubview(self.iconView)
|
||||
self.addSubview(self.clearIconView)
|
||||
self.addSubview(self.clearIconButton)
|
||||
|
||||
self.addSubview(self.cancelButton)
|
||||
self.clipsToBounds = true
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
self.tapRecognizer = tapRecognizer
|
||||
self.addGestureRecognizer(tapRecognizer)
|
||||
|
||||
self.cancelButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.layer.removeAnimation(forKey: "opacity")
|
||||
cancelButtonTitleView.alpha = 0.4
|
||||
}
|
||||
} else {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.alpha = 1.0
|
||||
cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside)
|
||||
|
||||
self.clearIconButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.clearIconView.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.clearIconView.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.clearIconView.alpha = 1.0
|
||||
strongSelf.clearIconView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.clearIconButton.addTarget(self, action: #selector(self.clearPressed), for: .touchUpInside)
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
let throttledSearchQuery = self.queryPromise.get()
|
||||
|> mapToSignal { query -> Signal<String, NoError> in
|
||||
@@ -164,194 +69,46 @@ final class SearchBarContentComponent: Component {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.activateTextInput()
|
||||
}
|
||||
}
|
||||
|
||||
private func activateTextInput() {
|
||||
if self.textField == nil, let textFrame = self.textFrame {
|
||||
let backgroundFrame = self.backgroundLayer.frame
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX - 32.0, height: backgroundFrame.height))
|
||||
|
||||
let textField = SearchTextField(frame: textFieldFrame)
|
||||
textField.clipsToBounds = true
|
||||
textField.autocorrectionType = .no
|
||||
textField.returnKeyType = .search
|
||||
self.textField = textField
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
textField.delegate = self
|
||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||
}
|
||||
|
||||
guard !(self.textField?.isFirstResponder ?? false) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.textField?.becomeFirstResponder()
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
|
||||
let textField = self.textField
|
||||
self.textField = nil
|
||||
|
||||
self.component?.performAction.invoke(.updateSearchActive(false))
|
||||
|
||||
if let textField {
|
||||
textField.resignFirstResponder()
|
||||
textField.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func clearPressed() {
|
||||
guard let textField = self.textField else {
|
||||
return
|
||||
}
|
||||
textField.text = ""
|
||||
self.textFieldChanged(textField)
|
||||
}
|
||||
|
||||
func deactivate() {
|
||||
if let text = self.textField?.text, !text.isEmpty {
|
||||
self.textField?.endEditing(true)
|
||||
} else {
|
||||
self.cancelPressed()
|
||||
}
|
||||
}
|
||||
|
||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
}
|
||||
|
||||
public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
}
|
||||
|
||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.endEditing(true)
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func textFieldChanged(_ textField: UITextField) {
|
||||
let text = textField.text ?? ""
|
||||
|
||||
self.clearIconView.isHidden = text.isEmpty
|
||||
self.clearIconButton.isHidden = text.isEmpty
|
||||
self.placeholderContent.view?.isHidden = !text.isEmpty
|
||||
|
||||
self.queryPromise.set(text)
|
||||
|
||||
if let params = self.params {
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: SearchBarContentComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, transition: transition)
|
||||
self.activateTextInput()
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, transition: ComponentTransition) {
|
||||
let params = Params(
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
size: size
|
||||
)
|
||||
|
||||
if self.params == params {
|
||||
return
|
||||
}
|
||||
|
||||
let isActiveWithText = true
|
||||
|
||||
if self.params?.theme !== theme {
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor
|
||||
self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor
|
||||
}
|
||||
|
||||
self.params = params
|
||||
|
||||
let sideInset: CGFloat = 10.0
|
||||
let inputHeight: CGFloat = 36.0
|
||||
let topInset: CGFloat = (size.height - inputHeight) / 2.0
|
||||
|
||||
let sideTextInset: CGFloat = sideInset + 4.0 + 17.0
|
||||
|
||||
self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor
|
||||
self.backgroundLayer.cornerRadius = 10.5
|
||||
|
||||
let cancelTextSize = self.cancelButtonTitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
text: strings.Common_Cancel,
|
||||
font: Font.regular(17.0),
|
||||
color: theme.rootController.navigationBar.accentTextColor
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 32.0, height: 100.0)
|
||||
)
|
||||
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight))
|
||||
if isActiveWithText {
|
||||
backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing
|
||||
}
|
||||
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)))
|
||||
|
||||
let textX: CGFloat = backgroundFrame.minX + sideTextInset
|
||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||
self.textFrame = textFrame
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + 5.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
}
|
||||
|
||||
let placeholderSize = self.placeholderContent.update(
|
||||
let searchInputSize = self.searchInput.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.Common_Search, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
SearchInputPanelComponent(
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
metrics: .init(widthClass: .compact, heightClass: .compact, orientation: nil),
|
||||
safeInsets: UIEdgeInsets(),
|
||||
placeholder: component.strings.Common_Search,
|
||||
hasEdgeEffect: false,
|
||||
updated: { [weak self] query in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.queryPromise.set(query)
|
||||
},
|
||||
cancel: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.component?.performAction.invoke(.updateSearchActive(false))
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
self.addSubview(placeholderContentView)
|
||||
if let searchInputView = self.searchInput.view as? SearchInputPanelComponent.View {
|
||||
if searchInputView.superview == nil {
|
||||
self.addSubview(searchInputView)
|
||||
|
||||
searchInputView.activateInput()
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
}
|
||||
|
||||
if let image = self.clearIconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.clearIconView, frame: iconFrame)
|
||||
transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0))
|
||||
}
|
||||
|
||||
if let cancelButtonTitleComponentView = self.cancelButtonTitle.view {
|
||||
if cancelButtonTitleComponentView.superview == nil {
|
||||
self.addSubview(cancelButtonTitleComponentView)
|
||||
cancelButtonTitleComponentView.isUserInteractionEnabled = false
|
||||
}
|
||||
transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
|
||||
}
|
||||
|
||||
if let textField = self.textField {
|
||||
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height)))
|
||||
transition.setFrame(view: searchInputView, frame: CGRect(origin: .zero, size: searchInputSize))
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,26 @@ import AccountContext
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import UrlEscaping
|
||||
import GlassBackgroundComponent
|
||||
|
||||
final class TitleBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
|
||||
let theme: PresentationTheme
|
||||
let title: String
|
||||
let readingProgress: CGFloat
|
||||
let loadingProgress: Double?
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
title: String
|
||||
title: String,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.readingProgress = readingProgress
|
||||
self.loadingProgress = loadingProgress
|
||||
}
|
||||
|
||||
static func ==(lhs: TitleBarContentComponent, rhs: TitleBarContentComponent) -> Bool {
|
||||
@@ -31,15 +38,30 @@ final class TitleBarContentComponent: Component {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = GlassBackgroundView()
|
||||
private let clippingView = UIView()
|
||||
private let readingProgressView = UIView()
|
||||
private var titleContent = ComponentView<Empty>()
|
||||
private var component: TitleBarContentComponent?
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.clippingView.clipsToBounds = true
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.contentView.addSubview(self.clippingView)
|
||||
self.clippingView.addSubview(self.readingProgressView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
@@ -48,7 +70,9 @@ final class TitleBarContentComponent: Component {
|
||||
|
||||
func update(component: TitleBarContentComponent, availableSize: CGSize, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
|
||||
let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction
|
||||
|
||||
let titleSize = self.titleContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
@@ -60,7 +84,7 @@ final class TitleBarContentComponent: Component {
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 36.0, height: availableSize.height)
|
||||
containerSize: CGSize(width: availableSize.width - 42.0, height: availableSize.height)
|
||||
)
|
||||
let titleContentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let titleContentView = self.titleContent.view {
|
||||
@@ -71,6 +95,18 @@ final class TitleBarContentComponent: Component {
|
||||
titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size)
|
||||
}
|
||||
|
||||
let expandedBackgroundWidth = availableSize.width - 14.0 * 2.0
|
||||
let collapsedBackgroundWidth = titleSize.width + 32.0
|
||||
let backgroundSize = CGSize(width: expandedBackgroundWidth * (1.0 - collapseFraction) + collapsedBackgroundWidth * collapseFraction, height: 44.0)
|
||||
self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - backgroundSize.width) / 2.0), y: floor((availableSize.height - backgroundSize.height) / 2.0)), size: backgroundSize)
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
transition.setFrame(view: self.clippingView, frame: CGRect(origin: .zero, size: backgroundFrame.size))
|
||||
transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: backgroundFrame.size.height * 0.5)
|
||||
|
||||
self.readingProgressView.backgroundColor = component.theme.rootController.navigationBar.primaryTextColor.withMultipliedAlpha(0.07)
|
||||
self.readingProgressView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundSize.width * component.readingProgress, height: backgroundSize.height))
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,28 +6,24 @@ import BlurredBackgroundComponent
|
||||
import BundleIconComponent
|
||||
import TelegramPresentationData
|
||||
import ContextReferenceButtonComponent
|
||||
import GlassBackgroundComponent
|
||||
import EdgeEffect
|
||||
|
||||
final class BrowserToolbarComponent: CombinedComponent {
|
||||
let backgroundColor: UIColor
|
||||
let separatorColor: UIColor
|
||||
let textColor: UIColor
|
||||
let theme: PresentationTheme
|
||||
let bottomInset: CGFloat
|
||||
let sideInset: CGFloat
|
||||
let item: AnyComponentWithIdentity<Empty>?
|
||||
let collapseFraction: CGFloat
|
||||
|
||||
init(
|
||||
backgroundColor: UIColor,
|
||||
separatorColor: UIColor,
|
||||
textColor: UIColor,
|
||||
theme: PresentationTheme,
|
||||
bottomInset: CGFloat,
|
||||
sideInset: CGFloat,
|
||||
item: AnyComponentWithIdentity<Empty>?,
|
||||
collapseFraction: CGFloat
|
||||
) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.separatorColor = separatorColor
|
||||
self.textColor = textColor
|
||||
self.theme = theme
|
||||
self.bottomInset = bottomInset
|
||||
self.sideInset = sideInset
|
||||
self.item = item
|
||||
@@ -35,13 +31,7 @@ final class BrowserToolbarComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
static func ==(lhs: BrowserToolbarComponent, rhs: BrowserToolbarComponent) -> Bool {
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
if lhs.separatorColor != rhs.separatorColor {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor != rhs.textColor {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.bottomInset != rhs.bottomInset {
|
||||
@@ -60,54 +50,70 @@ final class BrowserToolbarComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let background = Child(BlurredBackgroundComponent.self)
|
||||
let separator = Child(Rectangle.self)
|
||||
let edgeEffect = Child(EdgeEffectComponent.self)
|
||||
let background = Child(GlassBackgroundComponent.self)
|
||||
let centerItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
|
||||
return { context in
|
||||
let contentHeight: CGFloat = 49.0
|
||||
let contentHeight: CGFloat = 56.0
|
||||
let totalHeight = contentHeight + context.component.bottomInset
|
||||
let offset = context.component.collapseFraction * totalHeight
|
||||
let size = CGSize(width: context.availableSize.width, height: totalHeight)
|
||||
|
||||
let background = background.update(
|
||||
component: BlurredBackgroundComponent(color: context.component.backgroundColor),
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
let backgroundHeight: CGFloat = 48.0
|
||||
let edgeEffectHeight = totalHeight
|
||||
let edgeEffect = edgeEffect.update(
|
||||
component: EdgeEffectComponent(
|
||||
color: .clear,
|
||||
blur: true,
|
||||
alpha: 1.0,
|
||||
size: CGSize(width: size.width, height: edgeEffectHeight),
|
||||
edge: .bottom,
|
||||
edgeSize: edgeEffectHeight
|
||||
),
|
||||
availableSize: CGSize(width: size.width, height: edgeEffectHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let separator = separator.update(
|
||||
component: Rectangle(color: context.component.separatorColor, height: UIScreenPixel),
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
transition: context.transition
|
||||
context.add(edgeEffect
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0 + offset))
|
||||
)
|
||||
|
||||
|
||||
let item = context.component.item.flatMap { item in
|
||||
return centerItems[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: context.availableSize.width - context.component.sideInset * 2.0, height: contentHeight),
|
||||
availableSize: CGSize(width: context.availableSize.width - context.component.sideInset * 2.0, height: backgroundHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
}
|
||||
|
||||
let contentWidth = item?.size.width ?? 0.0
|
||||
|
||||
let backgroundSize = CGSize(width: contentWidth, height: backgroundHeight)
|
||||
let background = background.update(
|
||||
component: GlassBackgroundComponent(
|
||||
size: backgroundSize,
|
||||
cornerRadius: backgroundHeight * 0.5,
|
||||
isDark: context.component.theme.overallDarkAppearance,
|
||||
tintColor: .init(kind: .panel, color: UIColor(white: context.component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)),
|
||||
isInteractive: true
|
||||
),
|
||||
availableSize: backgroundSize,
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(background
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0 + offset))
|
||||
.position(CGPoint(x: size.width / 2.0, y: backgroundSize.height / 2.0 + offset))
|
||||
)
|
||||
|
||||
context.add(separator
|
||||
.position(CGPoint(x: size.width / 2.0, y: 0.0 + offset))
|
||||
)
|
||||
|
||||
if let centerItem = item {
|
||||
context.add(centerItem
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight / 2.0 + offset))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: backgroundSize.height / 2.0 + offset))
|
||||
.appear(ComponentTransition.Appear({ _, view, transition in
|
||||
transition.animatePosition(view: view, from: CGPoint(x: 0.0, y: size.height), to: .zero, additive: true)
|
||||
transition.animateBlur(layer: view.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
transition.animateAlpha(view: view, from: 0.0, to: 1.0)
|
||||
}))
|
||||
.disappear(ComponentTransition.Disappear({ view, transition, completion in
|
||||
let from = view.center
|
||||
view.center = from.offsetBy(dx: 0.0, dy: size.height)
|
||||
transition.animatePosition(view: view, from: from, to: view.center, completion: { _ in
|
||||
transition.animateBlur(layer: view.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
transition.setAlpha(view: view, alpha: 0.0, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}))
|
||||
@@ -120,8 +126,7 @@ final class BrowserToolbarComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
let accentColor: UIColor
|
||||
let textColor: UIColor
|
||||
let theme: PresentationTheme
|
||||
let canGoBack: Bool
|
||||
let canGoForward: Bool
|
||||
let canOpenIn: Bool
|
||||
@@ -131,8 +136,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
let performHoldAction: (UIView, ContextGesture?, BrowserScreen.Action) -> Void
|
||||
|
||||
init(
|
||||
accentColor: UIColor,
|
||||
textColor: UIColor,
|
||||
theme: PresentationTheme,
|
||||
canGoBack: Bool,
|
||||
canGoForward: Bool,
|
||||
canOpenIn: Bool,
|
||||
@@ -141,8 +145,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
performAction: ActionSlot<BrowserScreen.Action>,
|
||||
performHoldAction: @escaping (UIView, ContextGesture?, BrowserScreen.Action) -> Void
|
||||
) {
|
||||
self.accentColor = accentColor
|
||||
self.textColor = textColor
|
||||
self.theme = theme
|
||||
self.canGoBack = canGoBack
|
||||
self.canGoForward = canGoForward
|
||||
self.canOpenIn = canOpenIn
|
||||
@@ -153,10 +156,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
static func ==(lhs: NavigationToolbarContentComponent, rhs: NavigationToolbarContentComponent) -> Bool {
|
||||
if lhs.accentColor != rhs.accentColor {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor != rhs.textColor {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.canGoBack != rhs.canGoBack {
|
||||
@@ -191,26 +191,20 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
let performAction = context.component.performAction
|
||||
let performHoldAction = context.component.performHoldAction
|
||||
|
||||
let sideInset: CGFloat = 5.0
|
||||
let buttonSize = CGSize(width: 50.0, height: availableSize.height)
|
||||
var size = CGSize(width: 0.0, height: 48.0)
|
||||
let buttonSize = CGSize(width: 50.0, height: size.height)
|
||||
|
||||
var buttonCount = 3
|
||||
if context.component.canShare {
|
||||
buttonCount += 1
|
||||
}
|
||||
if context.component.canOpenIn {
|
||||
buttonCount += 1
|
||||
}
|
||||
|
||||
let spacing = (availableSize.width - buttonSize.width * CGFloat(buttonCount) - sideInset * 2.0) / CGFloat(buttonCount - 1)
|
||||
let sideInset: CGFloat = 34.0
|
||||
let spacing: CGFloat = 66.0
|
||||
let textColor = context.component.theme.rootController.navigationBar.primaryTextColor
|
||||
|
||||
let canShare = context.component.canShare
|
||||
let share = share.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat List/NavigationShare",
|
||||
tintColor: context.component.accentColor
|
||||
name: "Instant View/Share",
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@@ -224,22 +218,14 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
)
|
||||
|
||||
if context.component.isDocument {
|
||||
if !context.component.canShare {
|
||||
context.add(share
|
||||
.position(CGPoint(x: availableSize.width / 2.0, y: 10000.0))
|
||||
)
|
||||
} else {
|
||||
context.add(share
|
||||
.position(CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
var originX: CGFloat = sideInset
|
||||
|
||||
let search = search.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat List/SearchIcon",
|
||||
tintColor: context.component.accentColor
|
||||
name: "Instant View/Search",
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@@ -250,15 +236,27 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(search
|
||||
.position(CGPoint(x: sideInset + search.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
originX += spacing
|
||||
|
||||
if !context.component.canShare {
|
||||
context.add(share
|
||||
.position(CGPoint(x: availableSize.width / 2.0, y: 10000.0))
|
||||
)
|
||||
} else {
|
||||
context.add(share
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
originX += spacing
|
||||
}
|
||||
|
||||
let quickLook = quickLook.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/OpenDocument",
|
||||
tintColor: context.component.accentColor
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@@ -269,16 +267,19 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(quickLook
|
||||
.position(CGPoint(x: context.availableSize.width - sideInset - quickLook.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
size.width = originX + sideInset
|
||||
} else {
|
||||
var originX: CGFloat = sideInset
|
||||
|
||||
let canGoBack = context.component.canGoBack
|
||||
let back = back.update(
|
||||
component: ContextReferenceButtonComponent(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Back",
|
||||
tintColor: canGoBack ? context.component.accentColor : context.component.accentColor.withAlphaComponent(0.4)
|
||||
tintColor: canGoBack ? textColor : textColor.withAlphaComponent(0.4)
|
||||
)
|
||||
),
|
||||
minSize: buttonSize,
|
||||
@@ -297,8 +298,9 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(back
|
||||
.position(CGPoint(x: sideInset + back.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: sideInset, y: availableSize.height / 2.0))
|
||||
)
|
||||
originX += spacing
|
||||
|
||||
let canGoForward = context.component.canGoForward
|
||||
let forward = forward.update(
|
||||
@@ -306,7 +308,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Forward",
|
||||
tintColor: canGoForward ? context.component.accentColor : context.component.accentColor.withAlphaComponent(0.4)
|
||||
tintColor: canGoForward ? textColor : textColor.withAlphaComponent(0.4)
|
||||
)
|
||||
),
|
||||
minSize: buttonSize,
|
||||
@@ -325,19 +327,21 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(forward
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
originX += spacing
|
||||
|
||||
context.add(share
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
originX += spacing
|
||||
|
||||
let bookmark = bookmark.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Bookmark",
|
||||
tintColor: context.component.accentColor
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@@ -348,16 +352,18 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(bookmark
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if context.component.canOpenIn {
|
||||
originX += spacing
|
||||
|
||||
let openIn = openIn.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Browser",
|
||||
tintColor: context.component.accentColor
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@@ -368,34 +374,36 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(openIn
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
.position(CGPoint(x: originX, y: availableSize.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
size.width = originX + sideInset
|
||||
}
|
||||
|
||||
return availableSize
|
||||
return size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class SearchToolbarContentComponent: CombinedComponent {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let textColor: UIColor
|
||||
let index: Int
|
||||
let count: Int
|
||||
let isEmpty: Bool
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
textColor: UIColor,
|
||||
index: Int,
|
||||
count: Int,
|
||||
isEmpty: Bool,
|
||||
performAction: ActionSlot<BrowserScreen.Action>
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.textColor = textColor
|
||||
self.index = index
|
||||
self.count = count
|
||||
self.isEmpty = isEmpty
|
||||
@@ -403,10 +411,10 @@ final class SearchToolbarContentComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
static func ==(lhs: SearchToolbarContentComponent, rhs: SearchToolbarContentComponent) -> Bool {
|
||||
if lhs.strings !== rhs.strings {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor != rhs.textColor {
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.index != rhs.index {
|
||||
@@ -430,15 +438,17 @@ final class SearchToolbarContentComponent: CombinedComponent {
|
||||
let availableSize = context.availableSize
|
||||
let performAction = context.component.performAction
|
||||
|
||||
let sideInset: CGFloat = 3.0
|
||||
let sideInset: CGFloat = 60.0
|
||||
let buttonSize = CGSize(width: 50.0, height: availableSize.height)
|
||||
|
||||
let textColor = context.component.theme.rootController.navigationBar.primaryTextColor
|
||||
|
||||
let down = down.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat/Input/Search/DownButton",
|
||||
tintColor: context.component.textColor
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
isEnabled: context.component.count > 0,
|
||||
@@ -458,7 +468,7 @@ final class SearchToolbarContentComponent: CombinedComponent {
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat/Input/Search/UpButton",
|
||||
tintColor: context.component.textColor
|
||||
tintColor: textColor
|
||||
)
|
||||
),
|
||||
isEnabled: context.component.count > 0,
|
||||
@@ -486,7 +496,7 @@ final class SearchToolbarContentComponent: CombinedComponent {
|
||||
component: Text(
|
||||
text: currentText,
|
||||
font: Font.regular(15.0),
|
||||
color: context.component.textColor
|
||||
color: textColor
|
||||
),
|
||||
availableSize: availableSize,
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
@@ -495,7 +505,7 @@ final class SearchToolbarContentComponent: CombinedComponent {
|
||||
.position(CGPoint(x: availableSize.width - sideInset - down.size.width - up.size.width - text.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
return availableSize
|
||||
return CGSize(width: availableSize.width - 60.0, height: 48.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import SaveProgressScreen
|
||||
import DeviceModel
|
||||
import LegacyMediaPickerUI
|
||||
import PassKit
|
||||
import AlertComponent
|
||||
|
||||
private final class TonSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
private final class PendingTask {
|
||||
@@ -1258,12 +1259,20 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
||||
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var completed = false
|
||||
let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler()
|
||||
}
|
||||
})])
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: message,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_OK, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler()
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
@@ -1278,17 +1287,26 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
||||
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @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: message, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(false)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(true)
|
||||
}
|
||||
})])
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: message,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(false)
|
||||
}
|
||||
}),
|
||||
.init(title: presentationData.strings.Common_OK, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(true)
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
@@ -1302,24 +1320,28 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
||||
|
||||
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
|
||||
var completed = false
|
||||
let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: prompt, value: defaultText, apply: { value in
|
||||
if !completed {
|
||||
completed = true
|
||||
if let value = value {
|
||||
completionHandler(value)
|
||||
} else {
|
||||
completionHandler(nil)
|
||||
let promptController = promptController(
|
||||
context: self.context,
|
||||
updatedPresentationData: nil,
|
||||
text: prompt,
|
||||
value: defaultText,
|
||||
apply: { value in
|
||||
if !completed {
|
||||
completed = true
|
||||
if let value = value {
|
||||
completionHandler(value)
|
||||
} else {
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
promptController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
},
|
||||
dismissed: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.present(promptController, nil)
|
||||
}
|
||||
|
||||
@@ -1356,17 +1378,25 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
||||
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)
|
||||
}
|
||||
})])
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(false)
|
||||
}
|
||||
}),
|
||||
.init(title: presentationData.strings.WebBrowser_Download_Download, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(true)
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
|
||||
Reference in New Issue
Block a user