Various fixes

This commit is contained in:
Ilya Laktyushin
2024-07-26 12:26:37 +02:00
parent c5179bc4a6
commit cd810b940a
12 changed files with 337 additions and 85 deletions

View File

@@ -8,26 +8,36 @@ import Postbox
import TelegramCore
import AccountContext
import TelegramPresentationData
import ContextUI
final class BrowserAddressListComponent: Component {
let context: AccountContext
let theme: PresentationTheme
let strings: PresentationStrings
let insets: UIEdgeInsets
let navigateTo: (String) -> Void
let metrics: LayoutMetrics
let addressBarFrame: CGRect
let performAction: ActionSlot<BrowserScreen.Action>
let presentInGlobalOverlay: (ViewController) -> Void
init(
context: AccountContext,
theme: PresentationTheme,
strings: PresentationStrings,
insets: UIEdgeInsets,
navigateTo: @escaping (String) -> Void
metrics: LayoutMetrics,
addressBarFrame: CGRect,
performAction: ActionSlot<BrowserScreen.Action>,
presentInGlobalOverlay: @escaping (ViewController) -> Void
) {
self.context = context
self.theme = theme
self.strings = strings
self.insets = insets
self.navigateTo = navigateTo
self.metrics = metrics
self.addressBarFrame = addressBarFrame
self.performAction = performAction
self.presentInGlobalOverlay = presentInGlobalOverlay
}
static func ==(lhs: BrowserAddressListComponent, rhs: BrowserAddressListComponent) -> Bool {
@@ -43,6 +53,12 @@ final class BrowserAddressListComponent: Component {
if lhs.insets != rhs.insets {
return false
}
if lhs.metrics != rhs.metrics {
return false
}
if lhs.addressBarFrame != rhs.addressBarFrame {
return false
}
return true
}
@@ -109,6 +125,8 @@ final class BrowserAddressListComponent: Component {
let bookmarks: [Message]
}
private let outerView = UIButton()
private let shadowView = UIImageView()
private let backgroundView = UIView()
private let scrollView = ScrollView()
private let itemContainerView = UIView()
@@ -130,13 +148,19 @@ final class BrowserAddressListComponent: Component {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundView.clipsToBounds = true
self.scrollView.alwaysBounceVertical = true
self.scrollView.delegate = self
self.scrollView.showsVerticalScrollIndicator = false
self.addSubview(self.outerView)
self.addSubview(self.shadowView)
self.addSubview(self.backgroundView)
self.addSubview(self.scrollView)
self.backgroundView.addSubview(self.scrollView)
self.scrollView.addSubview(self.itemContainerView)
self.outerView.addTarget(self, action: #selector(self.outerPressed), for: .touchUpInside)
}
required init?(coder: NSCoder) {
@@ -147,6 +171,10 @@ final class BrowserAddressListComponent: Component {
self.stateDisposable?.dispose()
}
@objc private func outerPressed() {
self.component?.performAction.invoke(.closeAddressBar)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !self.ignoreScrolling {
self.updateScrolling(transition: .immediate)
@@ -155,6 +183,8 @@ final class BrowserAddressListComponent: Component {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.window?.endEditing(true)
cancelContextGestures(view: scrollView)
}
private func updateScrolling(transition: ComponentTransition) {
@@ -230,7 +260,7 @@ final class BrowserAddressListComponent: Component {
)
if let sectionHeaderView = sectionHeader.view {
if sectionHeaderView.superview == nil {
self.addSubview(sectionHeaderView)
self.backgroundView.addSubview(sectionHeaderView)
if !transition.animation.isImmediate {
sectionHeaderView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
@@ -289,7 +319,7 @@ final class BrowserAddressListComponent: Component {
}
}
let navigateTo = component.navigateTo
let performAction = component.performAction
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(
@@ -302,8 +332,49 @@ final class BrowserAddressListComponent: Component {
insets: component.insets,
action: {
if let url = webPage?.content.url {
navigateTo(url)
performAction.invoke(.navigateTo(url))
}
},
contextAction: { [weak self] webPage, message, sourceView, gesture in
guard let self, let component = self.component else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
var itemList: [ContextMenuItem] = []
if let message {
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
if let self, let component = self.component {
let _ = component.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone()
}
})))
} else {
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_RemoveRecent, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
if let self, let component = self.component, let url = webPage.content.url {
let _ = removeRecentlyVisitedLink(engine: component.context.engine, url: url).startStandalone()
}
})))
}
let items = ContextController.Items(content: .list(itemList))
let controller = ContextController(
presentationData: presentationData,
source: .extracted(BrowserAddressListContextExtractedContentSource(contentView: sourceView)),
items: .single(items),
recognizer: nil,
gesture: gesture
)
component.presentInGlobalOverlay(controller)
})
),
environment: {},
@@ -387,7 +458,22 @@ final class BrowserAddressListComponent: Component {
self.component = component
self.state = state
let resetScrolling = self.scrollView.bounds.width != availableSize.width
self.outerView.isHidden = !component.metrics.isTablet
self.outerView.frame = CGRect(origin: .zero, size: availableSize)
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)
self.backgroundView.layer.cornerRadius = 10.0
} else {
containerFrame = CGRect(origin: .zero, size: availableSize)
self.backgroundView.layer.cornerRadius = 0.0
}
let resetScrolling = self.scrollView.bounds.width != containerFrame.width
if themeUpdated {
self.backgroundView.backgroundColor = component.theme.list.plainBackgroundColor
}
@@ -402,16 +488,13 @@ final class BrowserAddressListComponent: Component {
message: nil,
hasNext: true,
insets: .zero,
action: {}
action: {},
contextAction: nil
)),
environment: {},
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
)
let _ = resetScrolling
let _ = addressItemSize
var sections: [ItemLayout.Section] = []
if let state = self.stateValue {
if !state.recent.isEmpty {
@@ -432,37 +515,50 @@ final class BrowserAddressListComponent: Component {
}
}
let itemLayout = ItemLayout(containerSize: availableSize, insets: .zero, sections: sections)
let itemLayout = ItemLayout(containerSize: containerFrame.size, insets: .zero, sections: sections)
self.itemLayout = itemLayout
let containerWidth = availableSize.width
let scrollContentHeight = max(itemLayout.contentHeight, availableSize.height)
let containerWidth = containerFrame.size.width
let scrollContentHeight = max(itemLayout.contentHeight, containerFrame.size.height)
self.ignoreScrolling = true
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: availableSize.height)))
transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: containerFrame.size))
let contentSize = CGSize(width: containerWidth, height: scrollContentHeight)
if contentSize != self.scrollView.contentSize {
self.scrollView.contentSize = contentSize
}
// let contentInset: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomPanelHeight + bottomPanelInset, right: 0.0)
// let indicatorInset = UIEdgeInsets(top: max(itemLayout.containerInset, environment.safeInsets.top + navigationHeight), left: 0.0, bottom: contentInset.bottom, right: 0.0)
// if indicatorInset != self.scrollView.scrollIndicatorInsets {
// self.scrollView.scrollIndicatorInsets = indicatorInset
// }
// if contentInset != self.scrollView.contentInset {
// self.scrollView.contentInset = contentInset
// }
if resetScrolling {
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: availableSize.height))
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: containerFrame.size.height))
}
self.ignoreScrolling = false
self.updateScrolling(transition: transition)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: availableSize))
transition.setFrame(view: self.backgroundView, frame: containerFrame)
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: scrollContentHeight)))
if component.metrics.isTablet {
transition.setFrame(view: self.shadowView, frame: containerFrame.insetBy(dx: -60.0, dy: -60.0))
self.shadowView.isHidden = false
if self.shadowView.image == nil {
self.shadowView.image = generateShadowImage()
}
} else {
self.shadowView.isHidden = true
}
return availableSize
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if let component = self.component, component.metrics.isTablet {
let addressFrame = CGRect(origin: CGPoint(x: self.backgroundView.frame.minX, y: self.backgroundView.frame.minY - 48.0), size: CGSize(width: self.backgroundView.frame.width, height: 48.0))
if addressFrame.contains(point) {
return nil
}
}
return result
}
}
func makeView() -> View {
@@ -473,3 +569,55 @@ final class BrowserAddressListComponent: Component {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
private func generateShadowImage() -> UIImage? {
return generateImage(CGSize(width: 140.0, height: 140.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.saveGState()
context.setShadow(offset: CGSize(), blur: 60.0, color: UIColor(white: 0.0, alpha: 0.4).cgColor)
let path = UIBezierPath(roundedRect: CGRect(x: 60.0, y: 60.0, width: 20.0, height: 20.0), cornerRadius: 10.0).cgPath
context.addPath(path)
context.fillPath()
context.restoreGState()
context.setBlendMode(.clear)
context.addPath(path)
context.fillPath()
})?.stretchableImage(withLeftCapWidth: 70, topCapHeight: 70)
}
private final class BrowserAddressListContextExtractedContentSource: ContextExtractedContentSource {
let keepInPlace: Bool = false
let ignoreContentTouches: Bool = false
let blurBackground: Bool = true
private let contentView: ContextExtractedContentContainingView
init(contentView: ContextExtractedContentContainingView) {
self.contentView = contentView
}
func takeView() -> ContextControllerTakeViewInfo? {
return ContextControllerTakeViewInfo(containingItem: .view(self.contentView), contentAreaInScreenSpace: UIScreen.main.bounds)
}
func putBack() -> ContextControllerPutBackViewInfo? {
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
private func cancelContextGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
for gesture in gestureRecognizers {
if let gesture = gesture as? ContextGesture {
gesture.cancel()
}
}
}
for subview in view.subviews {
cancelContextGestures(view: subview)
}
}