diff --git a/submodules/BrowserUI/Sources/BrowserContent.swift b/submodules/BrowserUI/Sources/BrowserContent.swift index b3b85aa900..567ec9e08c 100644 --- a/submodules/BrowserUI/Sources/BrowserContent.swift +++ b/submodules/BrowserUI/Sources/BrowserContent.swift @@ -28,7 +28,7 @@ final class BrowserContentState: Equatable { self.url = webItem.url.absoluteString self.title = webItem.title ?? "" self.uuid = nil - self.webItem = nil + self.webItem = webItem } } @@ -162,8 +162,7 @@ protocol BrowserContent: UIView { func navigateForward() func navigateTo(historyItem: BrowserContentState.HistoryItem) - func setFontSize(_ fontSize: CGFloat) - func setForceSerif(_ force: Bool) + func updateFontState(_ state: BrowserPresentationState.FontState) func setSearch(_ query: String?, completion: ((Int) -> Void)?) func scrollToPreviousSearchResult(completion: ((Int, Int) -> Void)?) diff --git a/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift b/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift index 25a950e245..d686a615db 100644 --- a/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift +++ b/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift @@ -311,14 +311,10 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg } - func setFontSize(_ fontSize: CGFloat) { + func updateFontState(_ state: BrowserPresentationState.FontState) { } - - func setForceSerif(_ force: Bool) { - } - func setSearch(_ query: String?, completion: ((Int) -> Void)?) { } diff --git a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift index 78f8778de2..3921ea991a 100644 --- a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift @@ -100,7 +100,7 @@ final class BrowserNavigationBarComponent: CombinedComponent { } static var body: Body { - let background = Child(BlurredBackgroundComponent.self) + let background = Child(Rectangle.self) let readingProgress = Child(Rectangle.self) let separator = Child(Rectangle.self) let loadingProgress = Child(LoadingProgressComponent.self) @@ -118,7 +118,7 @@ final class BrowserNavigationBarComponent: CombinedComponent { let size = CGSize(width: context.availableSize.width, height: context.component.topInset + contentHeight) let background = background.update( - component: BlurredBackgroundComponent(color: context.component.backgroundColor), + component: Rectangle(color: context.component.backgroundColor.withAlphaComponent(1.0)), availableSize: CGSize(width: size.width, height: size.height), transition: context.transition ) diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index b8b9cc08f6..fa13fd889f 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -248,8 +248,11 @@ private final class BrowserScreenComponent: CombinedComponent { } struct BrowserPresentationState: Equatable { - var fontSize: Int32 - var fontIsSerif: Bool + struct FontState: Equatable { + var size: Int32 + var isSerif: Bool + } + var fontState: FontState var isSearching: Bool var searchResultIndex: Int var searchResultCount: Int @@ -295,6 +298,7 @@ public class BrowserScreen: ViewController, MinimizableController { fileprivate let componentHost = ComponentView() private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? private var validLayout: (ContainerViewLayout, CGFloat)? init(controller: BrowserScreen) { @@ -302,7 +306,10 @@ public class BrowserScreen: ViewController, MinimizableController { self.controller = controller self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - self.presentationState = BrowserPresentationState(fontSize: 100, fontIsSerif: false, isSearching: false, searchResultIndex: 0, searchResultCount: 0, searchQueryIsEmpty: true) + self.presentationState = BrowserPresentationState( + fontState: BrowserPresentationState.FontState(size: 100, isSerif: false), + isSearching: false, searchResultIndex: 0, searchResultCount: 0, searchQueryIsEmpty: true + ) super.init() @@ -381,66 +388,76 @@ public class BrowserScreen: ViewController, MinimizableController { case .decreaseFontSize: self.updatePresentationState({ state in var updatedState = state - switch state.fontSize { + switch state.fontState.size { case 150: - updatedState.fontSize = 125 + updatedState.fontState.size = 125 case 125: - updatedState.fontSize = 115 + updatedState.fontState.size = 115 case 115: - updatedState.fontSize = 100 + updatedState.fontState.size = 100 case 100: - updatedState.fontSize = 85 + updatedState.fontState.size = 85 case 85: - updatedState.fontSize = 75 + updatedState.fontState.size = 75 case 75: - updatedState.fontSize = 50 + updatedState.fontState.size = 50 default: - updatedState.fontSize = 50 + updatedState.fontState.size = 50 } return updatedState }) - content.setFontSize(CGFloat(self.presentationState.fontSize) / 100.0) + content.updateFontState(self.presentationState.fontState) case .increaseFontSize: self.updatePresentationState({ state in var updatedState = state - switch state.fontSize { + switch state.fontState.size { case 125: - updatedState.fontSize = 150 + updatedState.fontState.size = 150 case 115: - updatedState.fontSize = 125 + updatedState.fontState.size = 125 case 100: - updatedState.fontSize = 115 + updatedState.fontState.size = 115 case 85: - updatedState.fontSize = 100 + updatedState.fontState.size = 100 case 75: - updatedState.fontSize = 85 + updatedState.fontState.size = 85 case 50: - updatedState.fontSize = 75 + updatedState.fontState.size = 75 default: - updatedState.fontSize = 150 + updatedState.fontState.size = 150 } return updatedState }) - content.setFontSize(CGFloat(self.presentationState.fontSize) / 100.0) + content.updateFontState(self.presentationState.fontState) case .resetFontSize: self.updatePresentationState({ state in var updatedState = state - updatedState.fontSize = 100 + updatedState.fontState.size = 100 return updatedState }) - content.setFontSize(CGFloat(self.presentationState.fontSize) / 100.0) + content.updateFontState(self.presentationState.fontState) case let .updateFontIsSerif(value): self.updatePresentationState({ state in var updatedState = state - updatedState.fontIsSerif = value + updatedState.fontState.isSerif = value return updatedState }) - content.setForceSerif(value) + content.updateFontState(self.presentationState.fontState) } } + + self.presentationDataDisposable = (controller.context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + guard let self else { + return + } + self.presentationData = presentationData + self.requestLayout(transition: .immediate) + }) } deinit { + self.presentationDataDisposable?.dispose() self.contentStateDisposable.dispose() } @@ -575,11 +592,11 @@ public class BrowserScreen: ViewController, MinimizableController { } } - func minimize() { + func minimize(topEdgeOffset: CGFloat? = nil, damping: CGFloat? = nil, initialVelocity: CGFloat? = nil) { guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { return } - navigationController.minimizeViewController(controller, damping: nil, beforeMaximize: { _, completion in + navigationController.minimizeViewController(controller, topEdgeOffset: topEdgeOffset, damping: damping, velocity: initialVelocity, beforeMaximize: { _, completion in completion() }, setupContainer: { [weak self] current in let minimizedContainer: MinimizedContainerImpl? @@ -626,20 +643,20 @@ public class BrowserScreen: ViewController, MinimizableController { let performAction = self.performAction - let forceIsSerif = self.presentationState.fontIsSerif + let forceIsSerif = self.presentationState.fontState.isSerif let fontItem = BrowserFontSizeContextMenuItem( - value: self.presentationState.fontSize, + value: self.presentationState.fontState.size, decrease: { [weak self] in performAction.invoke(.decreaseFontSize) if let self { - return self.presentationState.fontSize + return self.presentationState.fontState.size } else { return 100 } }, increase: { [weak self] in performAction.invoke(.increaseFontSize) if let self { - return self.presentationState.fontSize + return self.presentationState.fontState.size } else { return 100 } @@ -953,6 +970,10 @@ public class BrowserScreen: ViewController, MinimizableController { self.node.containerLayoutUpdated(layout: layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.height, transition: ComponentTransition(transition)) } + public func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) { + self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity) + } + public var isMinimized = false public var isMinimizable = true diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 6eb5aa1674..f67a5cc45e 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -170,12 +170,26 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU self.faviconDisposable.dispose() } - func setFontSize(_ fontSize: CGFloat) { - let js = "document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust='\(Int(fontSize * 100.0))%'" + var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false) + func updateFontState(_ state: BrowserPresentationState.FontState) { + self.updateFontState(state, force: false) + } + func updateFontState(_ state: BrowserPresentationState.FontState, force: Bool) { + if self.currentFontState.size != state.size || (force && self.currentFontState.size != 100) { + self.setFontSize(state.size) + } + if self.currentFontState.isSerif != state.isSerif || (force && self.currentFontState.isSerif) { + self.setFontSerif(state.isSerif) + } + self.currentFontState = state + } + + private func setFontSize(_ fontSize: Int32) { + let js = "document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust='\(fontSize)%'" self.webView.evaluateJavaScript(js, completionHandler: nil) } - func setForceSerif(_ force: Bool) { + private func setFontSerif(_ force: Bool) { let js: String if force { js = "document.getElementsByTagName(\'body\')[0].style.fontFamily = 'Georgia, serif';" @@ -388,13 +402,16 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU } } + func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { + self.updateFontState(self.currentFontState, force: true) + } + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.updateState { $0 .withUpdatedBackList(webView.backForwardList.backList.map { BrowserContentState.HistoryItem(webItem: $0) }) .withUpdatedForwardList(webView.backForwardList.forwardList.map { BrowserContentState.HistoryItem(webItem: $0) }) - } - + } self.parseFavicon() } diff --git a/submodules/Display/Source/Navigation/MinimizedContainer.swift b/submodules/Display/Source/Navigation/MinimizedContainer.swift index 625f95a752..bc5cb3941b 100644 --- a/submodules/Display/Source/Navigation/MinimizedContainer.swift +++ b/submodules/Display/Source/Navigation/MinimizedContainer.swift @@ -11,7 +11,7 @@ public protocol MinimizedContainer: ASDisplayNode { var statusBarStyle: StatusBarStyle { get } var statusBarStyleUpdated: (() -> Void)? { get set } - func addController(_ viewController: MinimizableController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition) + func addController(_ viewController: MinimizableController, topEdgeOffset: CGFloat?, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition) func maximizeController(_ viewController: MinimizableController, animated: Bool, completion: @escaping (Bool) -> Void) func collapse() func dismissAll(completion: @escaping () -> Void) @@ -28,6 +28,7 @@ public protocol MinimizableController: ViewController { var minimizedIcon: UIImage? { get } var minimizedProgress: Float? { get } + func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) func makeContentSnapshotView() -> UIView? func shouldDismissImmediately() -> Bool } @@ -57,6 +58,10 @@ public extension MinimizableController { return nil } + func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) { + + } + func makeContentSnapshotView() -> UIView? { return self.displayNode.view.snapshotView(afterScreenUpdates: false) } diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index dc65ebe8e7..7f7156e25d 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -1593,7 +1593,7 @@ open class NavigationController: UINavigationController, ContainableController, self._viewControllersPromise.set(self.viewControllers) } - public func minimizeViewController(_ viewController: MinimizableController, damping: CGFloat? = nil, velocity: CGFloat? = nil, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, setupContainer: (MinimizedContainer?) -> MinimizedContainer?, animated: Bool) { + public func minimizeViewController(_ viewController: MinimizableController, topEdgeOffset: CGFloat? = nil, damping: CGFloat? = nil, velocity: CGFloat? = nil, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, setupContainer: (MinimizedContainer?) -> MinimizedContainer?, animated: Bool) { let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .customSpring(damping: damping ?? 124.0, initialVelocity: velocity ?? 0.0)) : .immediate let minimizedContainer = setupContainer(self.minimizedContainer) @@ -1605,7 +1605,7 @@ open class NavigationController: UINavigationController, ContainableController, } viewController.isMinimized = true self.filterController(viewController, animated: true) - minimizedContainer?.addController(viewController, beforeMaximize: beforeMaximize, transition: transition) + minimizedContainer?.addController(viewController, topEdgeOffset: topEdgeOffset, beforeMaximize: beforeMaximize, transition: transition) } private var isMaximizing = false diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift index ac605fe4e2..5ead1f9391 100644 --- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift @@ -245,6 +245,8 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes self.view.endEditing(true) } + private var isDraggingHeader = false + func scrollViewDidScroll(_ scrollView: UIScrollView) { if self.ignoreScrolling || self.isDismissed { return @@ -253,6 +255,9 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes progress = max(0.0, min(1.0, progress)) self.dismissProgress = progress self.applyDismissProgress(transition: .immediate, completion: {}) + + let location = scrollView.panGestureRecognizer.location(in: scrollView).offsetBy(dx: 0.0, dy: -self.container.frame.minY) + self.isDraggingHeader = location.y < 66.0 } private func applyDismissProgress(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { @@ -277,10 +282,20 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes let transition: ContainedViewLayoutTransition let dismissProgress: CGFloat if (velocity.y < -0.5 || progress >= 0.5) && self.checkInteractiveDismissWithControllers() { - dismissProgress = 1.0 - targetOffset = 0.0 - transition = .animated(duration: duration, curve: .easeInOut) - self.isDismissed = true + if self.isDraggingHeader, let controller = self.container.controllers.last as? MinimizableController { + dismissProgress = 0.0 + targetOffset = 0.0 + transition = .immediate + + let topEdgeOffset = self.container.view.convert(self.container.bounds, to: self.view).minY + controller.requestMinimize(topEdgeOffset: topEdgeOffset, initialVelocity: velocity.y) + self.dim.removeFromSupernode() + } else { + dismissProgress = 1.0 + targetOffset = 0.0 + transition = .animated(duration: duration, curve: .easeInOut) + self.isDismissed = true + } } else { dismissProgress = 0.0 targetOffset = self.bounds.height diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Utils.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Utils.swift new file mode 100644 index 0000000000..a3bd79bbf6 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Utils.swift @@ -0,0 +1,70 @@ +import Foundation + +func emojiFor(for meteocode: Int, timestamp: Int32) -> String { + var emoji = weatherEmoji(for: meteocode) + if ["☀️", "🌤️"].contains(emoji) { + emoji = moonPhaseEmoji(for: timestamp) + } + return emoji +} + +func moonPhaseEmoji(for timestamp: Int32) -> String { + let newMoonDate = Date(timeIntervalSince1970: 1612137600) + let date = Date(timeIntervalSince1970: Double(timestamp)) + let lunarMonth: TimeInterval = 29.53058867 * 24 * 60 * 60 + + let daysSinceNewMoon = date.timeIntervalSince(newMoonDate) / (24 * 60 * 60) + let currentMoonPhase = daysSinceNewMoon.truncatingRemainder(dividingBy: lunarMonth) / lunarMonth + + switch currentMoonPhase { + case 0..<0.03: + return "🌑" + case 0.03..<0.22: + return "🌒" + case 0.22..<0.28: + return "🌓" + case 0.28..<0.47: + return "🌔" + case 0.47..<0.53: + return "🌕" + case 0.53..<0.72: + return "🌖" + case 0.72..<0.78: + return "🌗" + case 0.78..<0.97: + return "🌘" + default: + return "🌑" + } +} + +func weatherEmoji(for meteocode: Int) -> String { + switch meteocode { + case 0: + return "☀️" + case 1, 2, 3: + return "🌤️" + case 45, 48: + return "🌫️" + case 51, 53, 55: + return "🌧️" // Drizzle: Light, moderate, and dense intensity + case 56, 57: + return "🌧️" // Freezing Drizzle: Light and dense intensity + case 61, 63, 65: + return "🌧️" // Rain: Slight, moderate, and heavy intensity + case 66, 67: + return "🌧️" // Freezing Rain: Light and heavy intensity + case 71, 73, 75: + return "🌨️" // Snow fall: Slight, moderate, and heavy intensity + case 77: + return "🌨️" // Snow grains + case 80, 81, 82: + return "🌦️" // Rain showers: Slight, moderate, and violent + case 85, 86: + return "🌨️" + case 95, 96, 99: + return "⛈️" // Thunderstorm: Slight or moderate + default: + return "❓" + } +} diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift index 41b968e987..b6d1b4b977 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift @@ -28,11 +28,18 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll let id: AnyHashable let controller: MinimizableController let beforeMaximize: (NavigationController, @escaping () -> Void) -> Void + let topEdgeOffset: CGFloat? - init(id: AnyHashable, controller: MinimizableController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void) { + init( + id: AnyHashable, + controller: MinimizableController, + beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, + topEdgeOffset: CGFloat? + ) { self.id = id self.controller = controller self.beforeMaximize = beforeMaximize + self.topEdgeOffset = topEdgeOffset } } @@ -538,11 +545,12 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll return result } - public func addController(_ viewController: MinimizableController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition) { + public func addController(_ viewController: MinimizableController, topEdgeOffset: CGFloat?, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition) { let item = Item( id: AnyHashable(Int64.random(in: Int64.min ... Int64.max)), controller: viewController, - beforeMaximize: beforeMaximize + beforeMaximize: beforeMaximize, + topEdgeOffset: topEdgeOffset ) self.items.append(item) @@ -986,14 +994,19 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll itemNode.animateIn() var initialOffset = insets.top - if let minimizedTopEdgeOffset = itemNode.item.controller.minimizedTopEdgeOffset { - initialOffset += minimizedTopEdgeOffset - } - if layout.size.width < layout.size.height { - initialOffset += 10.0 - } - if let minimizedBounds = itemNode.item.controller.minimizedBounds { - initialOffset += -minimizedBounds.minY + if let topEdgeOffset = itemNode.item.topEdgeOffset { + initialOffset += topEdgeOffset + dimView.removeFromSuperview() + } else { + if let minimizedTopEdgeOffset = itemNode.item.controller.minimizedTopEdgeOffset { + initialOffset += minimizedTopEdgeOffset + } + if layout.size.width < layout.size.height { + initialOffset += 10.0 + } + if let minimizedBounds = itemNode.item.controller.minimizedBounds { + initialOffset += -minimizedBounds.minY + } } transition.animatePosition(node: itemNode, from: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + initialOffset), completion: { _ in @@ -1075,6 +1088,14 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll itemNode.animateOut() transition.updateTransform(node: itemNode, transform: CATransform3DIdentity) + + if let _ = itemNode.snapshotView { + if itemNode.item.controller.minimizedTopEdgeOffset == nil, let snapshotView = itemNode.snapshotView, snapshotView.frame.origin.y == -12.0 { + let snapshotFrame = snapshotView.frame.offsetBy(dx: 0.0, dy: 12.0) + transition.updateFrame(view: snapshotView, frame: snapshotFrame) + } + } + transition.updatePosition(node: itemNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + topInset + self.scrollView.contentOffset.y), completion: { _ in self.isApplyingTransition = false if self.currentTransition == currentTransition {