diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 07271caf62..f4d5615c43 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -28,6 +28,29 @@ public enum AttachmentButtonType: Equatable { case gift case standalone + public var key: String { + switch self { + case .gallery: + return "gallery" + case .file: + return "file" + case .location: + return "location" + case .quickReply: + return "quickReply" + case .contact: + return "contact" + case .poll: + return "poll" + case let .app(bot): + return "app_\(bot.shortName)" + case .gift: + return "gift" + case .standalone: + return "standalone" + } + } + public static func ==(lhs: AttachmentButtonType, rhs: AttachmentButtonType) -> Bool { switch lhs { case .gallery: @@ -207,10 +230,10 @@ public class AttachmentController: ViewController { private let updatedPresentationData: (initial: PresentationData, signal: Signal)? private let chatLocation: ChatLocation? private let isScheduledMessages: Bool - private let buttons: [AttachmentButtonType] + private var buttons: [AttachmentButtonType] private let initialButton: AttachmentButtonType private let fromMenu: Bool - private let hasTextInput: Bool + private var hasTextInput: Bool private let makeEntityInputView: () -> AttachmentTextInputPanelInputView? public var animateAppearance: Bool = false @@ -239,7 +262,7 @@ public class AttachmentController: ViewController { private let makeEntityInputView: () -> AttachmentTextInputPanelInputView? let panel: AttachmentPanel - private var currentType: AttachmentButtonType? + fileprivate var currentType: AttachmentButtonType? private var currentControllers: [AttachmentContainable] = [] private var validLayout: ContainerViewLayout? @@ -368,7 +391,7 @@ public class AttachmentController: ViewController { self.container.interactivelyDismissed = { [weak self] velocity in if let strongSelf = self, let layout = strongSelf.validLayout { - if let controller = strongSelf.controller, controller.shouldMinimizeOnSwipe?() == true, let navigationController = controller.navigationController as? NavigationController { + if let controller = strongSelf.controller, controller.shouldMinimizeOnSwipe?(strongSelf.currentType) == true, let navigationController = controller.navigationController as? NavigationController { let delta = layout.size.height - controller.minimizedTopEdgeOffset let damping: CGFloat = 180 @@ -390,7 +413,6 @@ public class AttachmentController: ViewController { strongSelf.isMinimizing = true strongSelf.container.update(isExpanded: true, force: true, transition: .immediate) -// strongSelf.container.update(isExpanded: true, force: true, transition: .animated(duration: 0.4, curve: .customSpring(damping: 180.0, initialVelocity: initialVelocity))) strongSelf.isMinimizing = false Queue.mainQueue().after(0.45, { @@ -1025,7 +1047,7 @@ public class AttachmentController: ViewController { public var getSourceRect: (() -> CGRect?)? - public var shouldMinimizeOnSwipe: (() -> Bool)? + public var shouldMinimizeOnSwipe: ((AttachmentButtonType?) -> Bool)? public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation?, isScheduledMessages: Bool = false, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { self.context = context @@ -1061,31 +1083,18 @@ public class AttachmentController: ViewController { return self.buttons.contains(.standalone) } -// private var snapshotView: UIView? -// public override var isMinimized: Bool { -// didSet { -// guard self.isMinimized != oldValue else { -// return -// } -// if self.isMinimized { -// if self.snapshotView == nil, let lastController = self.node.container.container.controllers.last, let snapshotView = lastController.view.snapshotView(afterScreenUpdates: false) { -// snapshotView.isUserInteractionEnabled = false -// self.snapshotView = snapshotView -// lastController.view.addSubview(snapshotView) -// } -// } else { -// if let snapshotView = self.snapshotView { -// self.snapshotView = nil -// Queue.mainQueue().after(0.15) { -// snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in -// snapshotView.removeFromSuperview() -// }) -// } -// } -// } -// } -// } - + public func convertToStandalone() { + guard self.buttons != [.standalone] else { + return + } + if case let .app(bot) = self.node.currentType { + self.title = bot.peer.compactDisplayTitle + } + self.buttons = [.standalone] + self.hasTextInput = false + self.requestLayout(transition: .immediate) + } + public func updateSelectionCount(_ count: Int) { self.node.updateSelectionCount(count, animated: false) } diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 920159407f..6d9605d02d 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -701,7 +701,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { private let backgroundNode: NavigationBackgroundNode private let scrollNode: ASScrollNode private let separatorNode: ASDisplayNode - private var buttonViews: [Int: ComponentHostView] = [:] + private var buttonViews: [AnyHashable: ComponentHostView] = [:] private var textInputPanelNode: AttachmentTextInputPanelNode? private var progressNode: LoadingProgressNode? @@ -1169,7 +1169,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { } let visibleRect = self.scrollNode.bounds.insetBy(dx: -180.0, dy: 0.0) - var validButtons = Set() var distanceBetweenNodes = layout.size.width / CGFloat(self.buttons.count) let internalWidth = distanceBetweenNodes * CGFloat(self.buttons.count - 1) @@ -1182,26 +1181,29 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { leftNodeOriginX = layout.safeInsets.left + sideInset + buttonWidth / 2.0 } + var validIds = Set() + for i in 0 ..< self.buttons.count { let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0) let buttonFrame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: CGSize(width: buttonWidth, height: buttonSize.height)) if !visibleRect.intersects(buttonFrame) { continue } - validButtons.insert(i) + + let type = self.buttons[i] + let _ = validIds.insert(type.key) var buttonTransition = transition let buttonView: ComponentHostView - if let current = self.buttonViews[i] { + if let current = self.buttonViews[type.key] { buttonView = current } else { buttonTransition = .immediate buttonView = ComponentHostView() - self.buttonViews[i] = buttonView + self.buttonViews[type.key] = buttonView self.scrollNode.view.addSubview(buttonView) } - let type = self.buttons[i] if case let .app(bot) = type { for (name, file) in bot.icons { if [.default, .iOSAnimated, .iOSSettingsStatic, .placeholder].contains(name) { @@ -1285,6 +1287,16 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { buttonView.accessibilityLabel = accessibilityTitle buttonView.accessibilityTraits = [.button] } + var removeIds: [AnyHashable] = [] + for (id, itemView) in self.buttonViews { + if !validIds.contains(id) { + removeIds.append(id) + itemView.removeFromSuperview() + } + } + for id in removeIds { + self.buttonViews.removeValue(forKey: id) + } } private func updateScrollLayoutIfNeeded(force: Bool, transition: ContainedViewLayoutTransition) -> Bool { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 80af3330b0..d3f44e84d0 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -638,6 +638,7 @@ final class MediaPickerGridItemNode: GridItemNode { self.updateHiddenMedia() } + private var currentPrice: Int64? private var didSetupSpoiler = false private func updateHasSpoiler(_ hasSpoiler: Bool, price: Int64?, isSingle: Bool) { var animated = true @@ -645,7 +646,8 @@ final class MediaPickerGridItemNode: GridItemNode { animated = false self.didSetupSpoiler = true } - + self.currentPrice = isSingle ? price : nil + if hasSpoiler || price != nil { if self.spoilerNode == nil { let spoilerNode = SpoilerOverlayNode(enableAnimations: self.enableAnimations) @@ -724,6 +726,11 @@ final class MediaPickerGridItemNode: GridItemNode { spoilerNode.update(size: self.bounds.size, transition: .immediate) } + if let priceNode = self.priceNode, self.bounds.width > 0.0 { + priceNode.frame = self.bounds + priceNode.update(size: self.bounds.size, price: self.currentPrice, small: true, transition: .immediate) + } + let statusSize = CGSize(width: 40.0, height: 40.0) if let statusNode = self.statusNode { statusNode.view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - statusSize.width) / 2.0), y: floorToScreenPixels((self.bounds.height - statusSize.height) / 2.0)), size: statusSize) diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 1c40323a74..eec5c35f63 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -2369,6 +2369,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD withdrawalDisposable.set((context.engine.peers.checkStarsRevenueWithdrawalAvailability() |> deliverOnMainQueue).start(error: { error in switch error { + case .serverProvided: + return case .requestPassword: let _ = (starsContext.state |> take(1) diff --git a/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift b/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift index f3da914f14..09cf65e4c7 100644 --- a/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift +++ b/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift @@ -234,6 +234,7 @@ public enum RequestStarsRevenueWithdrawalError : Equatable { case limitExceeded case requestPassword case invalidPassword + case serverProvided(text: String) } func _internal_checkStarsRevenueWithdrawalAvailability(account: Account) -> Signal { @@ -292,7 +293,9 @@ func _internal_requestStarsRevenueWithdrawalUrl(account: Account, peerId: PeerId |> mapToSignal { password -> Signal in return account.network.request(Api.functions.payments.getStarsRevenueWithdrawalUrl(peer: inputPeer, stars: amount, password: password), automaticFloodWait: false) |> mapError { error -> RequestStarsRevenueWithdrawalError in - if error.errorDescription.hasPrefix("FLOOD_WAIT") { + if error.errorCode == 406 { + return .serverProvided(text: error.errorDescription) + } else if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded } else if error.errorDescription == "PASSWORD_HASH_INVALID" { return .invalidPassword diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift index d066796100..29cd7374c2 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift @@ -232,7 +232,6 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll private var highlightedItemId: AnyHashable? - private var dismissGestureRecognizer: UIPanGestureRecognizer? private var dismissingItemId: AnyHashable? private var dismissingItemOffset: CGFloat? @@ -297,11 +296,10 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll self.scrollView.showsVerticalScrollIndicator = false self.scrollView.showsHorizontalScrollIndicator = false - let dismissGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.dismissPan(_:))) - dismissGestureRecognizer.delegate = self.wrappedGestureRecognizerDelegate - dismissGestureRecognizer.delaysTouchesBegan = true - self.scrollView.addGestureRecognizer(dismissGestureRecognizer) - self.dismissGestureRecognizer = dismissGestureRecognizer + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) + panGestureRecognizer.delegate = self.wrappedGestureRecognizerDelegate + panGestureRecognizer.delaysTouchesBegan = true + self.scrollView.addGestureRecognizer(panGestureRecognizer) } func item(at y: CGFloat) -> Int? { @@ -323,13 +321,35 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll let location = panGesture.location(in: gestureRecognizer.view) let velocity = panGesture.velocity(in: gestureRecognizer.view) - if abs(velocity.x) > abs(velocity.y), let _ = self.item(at: location.y) { - return true + if let _ = self.item(at: location.y) { + if self.isExpanded { + return abs(velocity.x) > abs(velocity.y) + } else { + return abs(velocity.y) > abs(velocity.x) + } } return false } - @objc func dismissPan(_ gestureRecognizer: UIPanGestureRecognizer) { + @objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) { + if self.isExpanded { + self.dismissPanGesture(gestureRecognizer) + } else { + self.expandPanGesture(gestureRecognizer) + } + } + + @objc func expandPanGesture(_ gestureRecognizer: UIPanGestureRecognizer) { + let translation = gestureRecognizer.translation(in: self.view) + if translation.y < -10.0 { + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + + self.expand() + } + } + + @objc func dismissPanGesture(_ gestureRecognizer: UIPanGestureRecognizer) { let scrollView = self.scrollView switch gestureRecognizer.state { @@ -357,7 +377,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll var needsLayout = true if let itemId = self.dismissingItemId { if let offset = self.dismissingItemOffset { - if offset < -self.frame.width / 4.0 { + let velocity = gestureRecognizer.velocity(in: self.view) + if offset < -self.frame.width / 3.0 || velocity.x < -300.0 { self.currentTransition = .dismiss(itemId: itemId) self.items.removeAll(where: { $0.id == itemId }) @@ -449,6 +470,18 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll completion() }) } + + public func expand() { + guard !self.items.isEmpty && !self.isExpanded else { + return + } + if self.items.count == 1, let item = self.items.first { + self.navigationController?.maximizeViewController(item.controller, animated: true) + } else { + self.isExpanded = true + self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring)) + } + } public func scrollViewDidScroll(_ scrollView: UIScrollView) { guard self.isExpanded else { @@ -569,12 +602,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll if self.isExpanded { self.navigationController?.maximizeViewController(item.controller, animated: true) } else { - if self.items.count == 1 { - self.navigationController?.maximizeViewController(item.controller, animated: true) - } else { - self.isExpanded = true - self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring)) - } + self.expand() } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index 9ca2d2e662..de36ea1c50 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -679,6 +679,8 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { return } switch error { + case .serverProvided: + return case .requestPassword: let _ = (revenueContext.state |> take(1) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 55e64ac69a..90f223fdba 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3754,7 +3754,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let openWebView = { if source == .menu { strongSelf.updateChatPresentationInterfaceState(interactive: false) { state in - return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true) + return state.updatedForceInputCommandsHidden(true) +// return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true) } let context = strongSelf.context diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 1288fac634..0ac342390c 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1714,13 +1714,19 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updateFrame(node: backgroundEffectNode, frame: CGRect(origin: CGPoint(), size: layout.size)) } - let wallpaperBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height) + var wallpaperBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height) transition.updateFrame(node: self.backgroundNode, frame: wallpaperBounds) var displayMode: WallpaperDisplayMode = .aspectFill if case .regular = layout.metrics.widthClass, layout.size.height == layout.deviceMetrics.screenSize.width { displayMode = .aspectFit + } else if case .compact = layout.metrics.widthClass { + if layout.size.width < layout.size.height && layout.size.height < layout.deviceMetrics.screenSize.height { + wallpaperBounds.size.height = layout.deviceMetrics.screenSize.height + } else if layout.size.width > layout.size.height && layout.size.height < layout.deviceMetrics.screenSize.width { + wallpaperBounds.size.height = layout.deviceMetrics.screenSize.width + } } self.backgroundNode.updateLayout(size: wallpaperBounds.size, displayMode: displayMode, transition: transition) diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 4d69df2e70..2dd53831fb 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -277,6 +277,13 @@ extension ChatControllerImpl { } return EntityInputView(context: strongSelf.context, isDark: false, areCustomEmojiEnabled: strongSelf.presentationInterfaceState.customEmojiAvailable) }) + attachmentController.shouldMinimizeOnSwipe = { [weak attachmentController] button in + if case .app = button { + attachmentController?.convertToStandalone() + return true + } + return false + } attachmentController.didDismiss = { [weak self] in self?.attachmentController = nil self?.canReadHistory.set(true) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 2b4cfbfc8f..08a9f1aab0 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1743,7 +1743,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.menuButtonIconNode.enqueueState(.close, animated: false) } else if case .webView = interfaceState.botMenuButton, let previousShowWebView = previousState?.showWebView, previousShowWebView != interfaceState.showWebView { if interfaceState.showWebView { - self.menuButtonIconNode.enqueueState(.close, animated: true) +// self.menuButtonIconNode.enqueueState(.close, animated: true) } else { self.menuButtonIconNode.enqueueState(.app, animated: true) } @@ -4536,7 +4536,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } } else if case let .webView(title, url) = presentationInterfaceState.botMenuButton { let willShow = !(self.presentationInterfaceState?.showWebView ?? false) - if willShow { + if willShow || "".isEmpty { self.interfaceInteraction?.openWebView(title, url, false, .menu) } else { self.interfaceInteraction?.updateShowWebView { _ in diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index d6b2115d71..31cbfc89ed 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -2182,7 +2182,7 @@ public func standaloneWebAppController( let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, makeEntityInputView: { return nil }) - controller.getInputContainerNode = getInputContainerNode +// controller.getInputContainerNode = getInputContainerNode controller.requestController = { _, present in let webAppController = WebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, replyToMessageId: nil, threadId: threadId) webAppController.openUrl = openUrl @@ -2195,12 +2195,8 @@ public func standaloneWebAppController( controller.didDismiss = didDismiss controller.getSourceRect = getSourceRect controller.title = params.botName - controller.shouldMinimizeOnSwipe = { - if params.source != .menu { - return true - } else { - return false - } + controller.shouldMinimizeOnSwipe = { _ in + return true } return controller }