Web app improvements

This commit is contained in:
Ilya Laktyushin 2024-08-25 16:15:35 +04:00
parent 5941becc33
commit 0f597d457e
9 changed files with 291 additions and 80 deletions

View File

@ -5,6 +5,13 @@ public struct AttachmentMainButtonState {
public enum Background {
case color(UIColor)
case premium
public var colorValue: UIColor? {
if case let .color(color) = self {
return color
}
return nil
}
}
public enum Progress: Equatable {
@ -18,6 +25,13 @@ public struct AttachmentMainButtonState {
case bold
}
public enum Position: String, Equatable {
case top
case bottom
case left
case right
}
public let text: String?
public let font: Font
public let background: Background
@ -25,6 +39,8 @@ public struct AttachmentMainButtonState {
public let isVisible: Bool
public let progress: Progress
public let isEnabled: Bool
public let hasShimmer: Bool
public let position: Position?
public init(
text: String?,
@ -33,7 +49,9 @@ public struct AttachmentMainButtonState {
textColor: UIColor,
isVisible: Bool,
progress: Progress,
isEnabled: Bool
isEnabled: Bool,
hasShimmer: Bool,
position: Position? = nil
) {
self.text = text
self.font = font
@ -42,9 +60,11 @@ public struct AttachmentMainButtonState {
self.isVisible = isVisible
self.progress = progress
self.isEnabled = isEnabled
self.hasShimmer = hasShimmer
self.position = position
}
public static var initial: AttachmentMainButtonState {
return AttachmentMainButtonState(text: nil, font: .bold, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false)
return AttachmentMainButtonState(text: nil, font: .bold, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false, hasShimmer: false)
}
}

View File

@ -215,8 +215,11 @@ public protocol AttachmentMediaPickerContext {
var loadingProgress: Signal<CGFloat?, NoError> { get }
var mainButtonState: Signal<AttachmentMainButtonState?, NoError> { get }
var secondaryButtonState: Signal<AttachmentMainButtonState?, NoError> { get }
var bottomPanelBackgroundColor: Signal<UIColor?, NoError> { get }
func mainButtonAction()
func secondaryButtonAction()
func setCaption(_ caption: NSAttributedString)
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?)
@ -261,6 +264,14 @@ public extension AttachmentMediaPickerContext {
var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
return .single(nil)
}
var secondaryButtonState: Signal<AttachmentMainButtonState?, NoError> {
return .single(nil)
}
var bottomPanelBackgroundColor: Signal<UIColor?, NoError> {
return .single(nil)
}
func setCaption(_ caption: NSAttributedString) {
}
@ -273,6 +284,9 @@ public extension AttachmentMediaPickerContext {
func mainButtonAction() {
}
func secondaryButtonAction() {
}
}
private func generateShadowImage() -> UIImage? {
@ -364,6 +378,8 @@ public class AttachmentController: ViewController, MinimizableController {
private let loadingProgressDisposable = MetaDisposable()
private let mainButtonStateDisposable = MetaDisposable()
private let secondaryButtonStateDisposable = MetaDisposable()
private let bottomPanelBackgroundColorDisposable = MetaDisposable()
private var selectionCount: Int = 0
@ -408,11 +424,44 @@ public class AttachmentController: ViewController, MinimizableController {
})
}
}))
self.secondaryButtonStateDisposable.set((mediaPickerContext.secondaryButtonState
|> deliverOnMainQueue).startStrict(next: { [weak self] mainButtonState in
if let strongSelf = self {
let _ = (strongSelf.panel.animatingTransitionPromise.get()
|> filter { value in
return !value
}
|> take(1)).startStandalone(next: { [weak self] _ in
if let strongSelf = self {
strongSelf.panel.updateSecondaryButtonState(mainButtonState)
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
}
}
})
}
}))
self.bottomPanelBackgroundColorDisposable.set((mediaPickerContext.bottomPanelBackgroundColor
|> deliverOnMainQueue).startStrict(next: { [weak self] color in
if let strongSelf = self {
let _ = (strongSelf.panel.animatingTransitionPromise.get()
|> filter { value in
return !value
}
|> take(1)).startStandalone(next: { [weak self] _ in
if let strongSelf = self {
strongSelf.panel.updateCustomBottomPanelBackgroundColor(color)
}
})
}
}))
} else {
self.updateSelectionCount(0)
self.mediaSelectionCountDisposable.set(nil)
self.loadingProgressDisposable.set(nil)
self.mainButtonStateDisposable.set(nil)
self.secondaryButtonStateDisposable.set(nil)
self.bottomPanelBackgroundColorDisposable.set(nil)
}
}
}
@ -590,12 +639,18 @@ public class AttachmentController: ViewController, MinimizableController {
}
}
self.panel.mainButtonPressed = { [weak self] in
self.panel.onMainButtonPressed = { [weak self] in
if let strongSelf = self {
strongSelf.mediaPickerContext?.mainButtonAction()
}
}
self.panel.onSecondaryButtonPressed = { [weak self] in
if let strongSelf = self {
strongSelf.mediaPickerContext?.secondaryButtonAction()
}
}
self.panel.requestLayout = { [weak self] in
if let strongSelf = self, let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.2, curve: .easeInOut))
@ -628,6 +683,8 @@ public class AttachmentController: ViewController, MinimizableController {
self.mediaSelectionCountDisposable.dispose()
self.loadingProgressDisposable.dispose()
self.mainButtonStateDisposable.dispose()
self.secondaryButtonStateDisposable.dispose()
self.bottomPanelBackgroundColorDisposable.dispose()
}
private var inputContainerHeight: CGFloat?
@ -985,10 +1042,7 @@ public class AttachmentController: ViewController, MinimizableController {
var containerLayout = layout
let containerRect: CGRect
var isCompact = true
if case .regular = layout.metrics.widthClass {
isCompact = false
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
@ -1055,7 +1109,7 @@ public class AttachmentController: ViewController, MinimizableController {
var containerInsets = containerLayout.intrinsicInsets
var hasPanel = false
let previousHasButton = self.hasButton
// let previousHasButton = self.hasButton
let hasButton = self.panel.isButtonVisible && !self.isDismissing
self.hasButton = hasButton
if let controller = self.controller, controller.buttons.count > 1 || controller.hasTextInput {
@ -1066,53 +1120,21 @@ public class AttachmentController: ViewController, MinimizableController {
}
let isEffecitvelyCollapsedUpdated = (self.selectionCount > 0) != (self.panel.isSelecting)
var panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isSelecting: self.selectionCount > 0, elevateProgress: !hasPanel && !hasButton, transition: transition)
if fromMenu && !hasButton, let inputContainerHeight = self.inputContainerHeight {
panelHeight = inputContainerHeight
}
let panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isSelecting: self.selectionCount > 0, elevateProgress: !hasPanel && !hasButton, transition: transition)
if hasPanel || hasButton {
containerInsets.bottom = panelHeight
}
var transitioning = false
if fromMenu && previousHasButton != hasButton, let (_, _, getTransition) = controller.getInputContainerNode(), let inputTransition = getTransition() {
if hasButton {
self.panel.animateTransitionIn(inputTransition: inputTransition, transition: transition)
} else {
self.panel.animateTransitionOut(inputTransition: inputTransition, dismissed: false, transition: transition)
}
transitioning = true
}
var panelTransition = transition
if isEffecitvelyCollapsedUpdated {
panelTransition = .animated(duration: 0.25, curve: .easeInOut)
}
var panelY = containerRect.height - panelHeight
if fromMenu && isCompact {
panelY = layout.size.height - panelHeight
} else if !hasPanel && !hasButton {
if !hasPanel && !hasButton {
panelY = containerRect.height
}
if fromMenu && isCompact {
if hasButton {
self.panel.isHidden = false
self.inputContainerNode?.isHidden = true
} else if !transitioning {
if !self.panel.animatingTransition {
self.panel.isHidden = true
self.inputContainerNode?.isHidden = false
}
}
}
panelTransition.updateFrame(node: self.panel, frame: CGRect(origin: CGPoint(x: 0.0, y: panelY), size: CGSize(width: containerRect.width, height: panelHeight)), completion: { [weak self] finished in
if transitioning && finished, isCompact {
self?.panel.isHidden = !hasButton
self?.inputContainerNode?.isHidden = hasButton
}
})
panelTransition.updateFrame(node: self.panel, frame: CGRect(origin: CGPoint(x: 0.0, y: panelY), size: CGSize(width: containerRect.width, height: panelHeight)))
var shadowFrame = containerRect.insetBy(dx: -60.0, dy: -60.0)
shadowFrame.size.height -= 12.0

View File

@ -463,7 +463,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
let diameter: CGFloat = size.height - 22.0
let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(buttonOffset + (buttonWidth - diameter) / 2.0), y: floorToScreenPixels((size.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter))
progressNode.frame = progressFrame
progressNode.image = generateIndefiniteActivityIndicatorImage(color: .white, diameter: diameter, lineWidth: 3.0)
progressNode.image = generateIndefiniteActivityIndicatorImage(color: self.state.textColor, diameter: diameter, lineWidth: 3.0)
self.addSubnode(progressNode)
@ -499,7 +499,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
}
private func setupShimmering() {
if case .premium = self.state.background {
if self.state.hasShimmer {
if self.shimmerView == nil {
let shimmerView = ShimmerEffectForegroundView()
shimmerView.isUserInteractionEnabled = false
@ -601,7 +601,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
}
}
func updateLayout(size: CGSize, state: AttachmentMainButtonState, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, state: AttachmentMainButtonState, animateBackground: Bool = false, transition: ContainedViewLayoutTransition) {
let previousState = self.state
self.state = state
self.size = size
@ -621,13 +621,23 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: state.textColor)
let textSize = self.textNode.updateLayout(size)
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
if self.textNode.frame.width.isZero {
self.textNode.frame = textFrame
} else {
self.textNode.bounds = CGRect(origin: .zero, size: textSize)
transition.updatePosition(node: self.textNode, position: textFrame.center)
}
switch state.background {
case let .color(backgroundColor):
self.backgroundAnimationNode.image = nil
self.backgroundAnimationNode.layer.removeAllAnimations()
self.backgroundColor = backgroundColor
if animateBackground {
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear).updateBackgroundColor(node: self, color: backgroundColor)
} else {
self.backgroundColor = backgroundColor
}
case .premium:
if self.backgroundAnimationNode.image == nil {
let backgroundColors = [
@ -706,9 +716,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
private var textInputPanelNode: AttachmentTextInputPanelNode?
private var progressNode: LoadingProgressNode?
private var mainButtonNode: MainButtonNode
private var secondaryButtonNode: MainButtonNode
private var loadingProgress: CGFloat?
private var mainButtonState: AttachmentMainButtonState = .initial
private var secondaryButtonState: AttachmentMainButtonState = .initial
private var customBottomPanelBackgroundColor: UIColor?
private var elevateProgress: Bool = false
private var buttons: [AttachmentButtonType] = []
@ -716,7 +729,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
private(set) var isSelecting: Bool = false
private var _isButtonVisible: Bool = false
var isButtonVisible: Bool {
return self.mainButtonState.isVisible
return self.mainButtonState.isVisible || self.secondaryButtonState.isVisible
}
private var validLayout: ContainerViewLayout?
@ -737,7 +750,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
var getCurrentSendMessageContextMediaPreview: (() -> ChatSendMessageContextScreenMediaPreview?)?
var mainButtonPressed: () -> Void = { }
var onMainButtonPressed: () -> Void = { }
var onSecondaryButtonPressed: () -> Void = { }
init(controller: AttachmentController, context: AccountContext, chatLocation: ChatLocation?, isScheduledMessages: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
self.controller = controller
@ -760,6 +774,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
self.separatorNode.backgroundColor = self.presentationData.theme.rootController.tabBar.separatorColor
self.mainButtonNode = MainButtonNode()
self.secondaryButtonNode = MainButtonNode()
super.init()
@ -768,9 +783,11 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
self.containerNode.addSubnode(self.separatorNode)
self.containerNode.addSubnode(self.scrollNode)
self.addSubnode(self.secondaryButtonNode)
self.addSubnode(self.mainButtonNode)
self.mainButtonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.mainButtonNode.addTarget(self, action: #selector(self.mainButtonPressed), forControlEvents: .touchUpInside)
self.secondaryButtonNode.addTarget(self, action: #selector(self.secondaryButtonPressed), forControlEvents: .touchUpInside)
self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in
}, setupEditMessage: { _, _ in
@ -1105,7 +1122,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
if let strongSelf = self {
strongSelf.presentationData = presentationData
strongSelf.backgroundNode.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
strongSelf.backgroundNode.updateColor(color: strongSelf.customBottomPanelBackgroundColor ?? presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
strongSelf.separatorNode.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor
strongSelf.updateChatPresentationInterfaceState({ $0.updatedTheme(presentationData.theme) })
@ -1137,8 +1154,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
self.view.accessibilityTraits = .tabBar
}
@objc private func buttonPressed() {
self.mainButtonPressed()
@objc private func mainButtonPressed() {
self.onMainButtonPressed()
}
@objc private func secondaryButtonPressed() {
self.onSecondaryButtonPressed()
}
func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
@ -1372,11 +1393,24 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
func updateMainButtonState(_ mainButtonState: AttachmentMainButtonState?) {
var currentButtonState = self.mainButtonState
if mainButtonState == nil {
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, font: currentButtonState.font, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled)
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, font: currentButtonState.font, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled, hasShimmer: currentButtonState.hasShimmer)
}
self.mainButtonState = mainButtonState ?? currentButtonState
}
func updateSecondaryButtonState(_ secondaryButtonState: AttachmentMainButtonState?) {
var currentButtonState = self.secondaryButtonState
if secondaryButtonState == nil {
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, font: currentButtonState.font, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled, hasShimmer: currentButtonState.hasShimmer)
}
self.secondaryButtonState = secondaryButtonState ?? currentButtonState
}
func updateCustomBottomPanelBackgroundColor(_ color: UIColor?) {
self.customBottomPanelBackgroundColor = color
self.backgroundNode.updateColor(color: self.customBottomPanelBackgroundColor ?? presentationData.theme.rootController.tabBar.backgroundColor, transition: .animated(duration: 0.2, curve: .linear))
}
let animatingTransitionPromise = ValuePromise<Bool>(false)
private(set) var animatingTransition = false {
didSet {
@ -1521,11 +1555,14 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
self.scrollNode.isUserInteractionEnabled = !isSelecting
let isButtonVisible = self.mainButtonState.isVisible
let isNarrowButton = isButtonVisible && self.mainButtonState.font == .regular
let isAnyButtonVisible = self.mainButtonState.isVisible || self.secondaryButtonState.isVisible
let isNarrowButton = isAnyButtonVisible && self.mainButtonState.font == .regular
let isTwoVerticalButtons = self.mainButtonState.isVisible && self.secondaryButtonState.isVisible && [.top, .bottom].contains(self.secondaryButtonState.position)
let isTwoHorizontalButtons = self.mainButtonState.isVisible && self.secondaryButtonState.isVisible && [.left, .right].contains(self.secondaryButtonState.position)
var insets = layout.insets(options: [])
if let inputHeight = layout.inputHeight, inputHeight > 0.0 && (isSelecting || isButtonVisible) {
if let inputHeight = layout.inputHeight, inputHeight > 0.0 && (isSelecting || isAnyButtonVisible) {
insets.bottom = inputHeight
} else if layout.intrinsicInsets.bottom > 0.0 {
insets.bottom = layout.intrinsicInsets.bottom
@ -1560,7 +1597,11 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: buttonSize.height + insets.bottom))
var containerTransition: ContainedViewLayoutTransition
let containerFrame: CGRect
if isButtonVisible {
let sideInset: CGFloat = 16.0
let buttonHeight: CGFloat = 50.0
if isAnyButtonVisible {
var height: CGFloat
if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero {
height = bounds.height
@ -1577,6 +1618,9 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
if !isNarrowButton {
height += 9.0
}
if isTwoVerticalButtons {
height += buttonHeight + sideInset
}
containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: height))
} else if isSelecting {
containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: textPanelHeight + insets.bottom))
@ -1589,8 +1633,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
} else {
containerTransition = transition
}
containerTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting || isButtonVisible ? 0.0 : 1.0)
containerTransition.updateTransformScale(node: self.scrollNode, scale: isSelecting || isButtonVisible ? 0.85 : 1.0)
containerTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
containerTransition.updateTransformScale(node: self.scrollNode, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
if isSelectingUpdated {
if isSelecting {
@ -1642,16 +1686,68 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
progressNode?.removeFromSupernode()
})
}
let sideInset: CGFloat = 16.0
let buttonSize = CGSize(width: layout.size.width - (sideInset + layout.safeInsets.left) * 2.0, height: 50.0)
let buttonTopInset: CGFloat = isNarrowButton ? 2.0 : 8.0
if !self.dismissed {
self.mainButtonNode.updateLayout(size: buttonSize, state: self.mainButtonState, transition: transition)
var buttonSize = CGSize(width: layout.size.width - (sideInset + layout.safeInsets.left) * 2.0, height: buttonHeight)
if isTwoHorizontalButtons {
buttonSize = CGSize(width: (buttonSize.width - sideInset) / 2.0, height: buttonSize.height)
}
let buttonTopInset: CGFloat = isNarrowButton ? 2.0 : 8.0
if !self.animatingTransition {
transition.updateFrame(node: self.mainButtonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + sideInset, y: isButtonVisible || self.fromMenu ? buttonTopInset : containerFrame.height), size: buttonSize))
let buttonOriginX = layout.safeInsets.left + sideInset
let buttonOriginY = isAnyButtonVisible || self.fromMenu ? buttonTopInset : containerFrame.height
var mainButtonFrame: CGRect?
var secondaryButtonFrame: CGRect?
if self.secondaryButtonState.isVisible && self.mainButtonState.isVisible, let position = self.secondaryButtonState.position {
switch position {
case .top:
secondaryButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
mainButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY + sideInset + buttonSize.height), size: buttonSize)
case .bottom:
mainButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
secondaryButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY + sideInset + buttonSize.height), size: buttonSize)
case .left:
secondaryButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
mainButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX + buttonSize.width + sideInset, y: buttonOriginY), size: buttonSize)
case .right:
mainButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
secondaryButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX + buttonSize.width + sideInset, y: buttonOriginY), size: buttonSize)
}
} else {
if self.mainButtonState.isVisible {
mainButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
}
if self.secondaryButtonState.isVisible {
secondaryButtonFrame = CGRect(origin: CGPoint(x: buttonOriginX, y: buttonOriginY), size: buttonSize)
}
}
if let mainButtonFrame {
if !self.dismissed {
self.mainButtonNode.updateLayout(size: buttonSize, state: self.mainButtonState, animateBackground: self.mainButtonState.background.colorValue == self.backgroundNode.color && transition.isAnimated, transition: transition)
}
if self.mainButtonNode.frame.width.isZero {
self.mainButtonNode.frame = mainButtonFrame
} else {
transition.updateFrame(node: self.mainButtonNode, frame: mainButtonFrame)
}
transition.updateAlpha(node: self.mainButtonNode, alpha: 1.0)
} else {
transition.updateAlpha(node: self.mainButtonNode, alpha: 0.0)
}
if let secondaryButtonFrame {
if !self.dismissed {
self.secondaryButtonNode.updateLayout(size: buttonSize, state: self.secondaryButtonState, animateBackground: self.secondaryButtonState.background.colorValue == self.backgroundNode.color && transition.isAnimated, transition: transition)
}
if self.secondaryButtonNode.frame.width.isZero {
self.secondaryButtonNode.frame = secondaryButtonFrame
} else {
transition.updateFrame(node: self.secondaryButtonNode, frame: secondaryButtonFrame)
}
transition.updateAlpha(node: self.secondaryButtonNode, alpha: 1.0)
} else {
transition.updateAlpha(node: self.secondaryButtonNode, alpha: 0.0)
}
}
return containerFrame.height

View File

@ -140,6 +140,10 @@ private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTranspar
public final class NavigationBackgroundNode: ASDisplayNode {
private var _color: UIColor
public var color: UIColor {
return self._color
}
private var enableBlur: Bool
private var enableSaturation: Bool

View File

@ -2949,7 +2949,7 @@ public func wallpaperMediaPickerController(
controller.animateAppearance = animateAppearance
controller.requestController = { [weak controller] _, present in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_SetColorWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true), mainButtonAction: {
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_SetColorWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false), mainButtonAction: {
controller?.dismiss(animated: true)
openColors()
})

View File

@ -879,7 +879,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
price = nil
}
let buttonText = presentationData.strings.Premium_Gift_GiftSubscription(price ?? "").string
self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, font: .bold, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true, hasShimmer: false)))
self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, font: .bold, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true, hasShimmer: true)))
}
func buy() {

View File

@ -194,7 +194,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
self?.push(controller)
}
self.mainButtonState = AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true)
self.mainButtonState = AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
}
required public init(coder aDecoder: NSCoder) {

View File

@ -645,8 +645,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let giveawayMessageId {
tableItems.append(.init(
id: "gift",
title: "Gift",
id: "prize",
title: "Prize",
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: "\(count) Stars", font: tableFont, textColor: tableTextColor)))
)

View File

@ -104,6 +104,7 @@ public func generateWebAppThemeParams(_ theme: PresentationTheme) -> [String: An
"button_color": Int32(bitPattern: theme.list.itemCheckColors.fillColor.rgb),
"button_text_color": Int32(bitPattern: theme.list.itemCheckColors.foregroundColor.rgb),
"header_bg_color": Int32(bitPattern: theme.rootController.navigationBar.opaqueBackgroundColor.rgb),
"bottom_bar_bg_color": Int32(bitPattern: theme.rootController.tabBar.backgroundColor.rgb),
"accent_text_color": Int32(bitPattern: theme.list.itemAccentColor.rgb),
"section_bg_color": Int32(bitPattern: theme.list.itemBlocksBackgroundColor.rgb),
"section_header_text_color": Int32(bitPattern: theme.list.freeTextColor.rgb),
@ -145,6 +146,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
fileprivate let mainButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
fileprivate var secondaryButtonState: AttachmentMainButtonState? {
didSet {
self.secondaryButtonStatePromise.set(.single(self.secondaryButtonState))
}
}
fileprivate let secondaryButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
private let context: AccountContext
var presentationData: PresentationData
private var queryId: Int64?
@ -192,9 +200,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
self?.handleScriptMessage(message)
}
webView.onFirstTouch = { [weak self] in
if let strongSelf = self, let delayedScriptMessage = strongSelf.delayedScriptMessage {
strongSelf.delayedScriptMessage = nil
strongSelf.handleScriptMessage(delayedScriptMessage)
if let self, !self.delayedScriptMessages.isEmpty {
let delayedScriptMessages = self.delayedScriptMessages
self.delayedScriptMessages.removeAll()
for message in delayedScriptMessages {
self.handleScriptMessage(message)
}
}
}
if #available(iOS 13.0, *) {
@ -414,6 +425,14 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.webView?.sendEvent(name: "main_button_pressed", data: nil)
}
@objc fileprivate func secondaryButtonPressed() {
if let secondaryButtonState = self.secondaryButtonState, !secondaryButtonState.isVisible || !secondaryButtonState.isEnabled {
return
}
self.webView?.lastTouchTimestamp = CACurrentMediaTime()
self.webView?.sendEvent(name: "secondary_button_pressed", data: nil)
}
private func updatePlaceholder(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
var shapes: [ShimmerEffect.ShimmerEffectNode.Shape] = []
var placeholderSize: CGSize = CGSize()
@ -656,7 +675,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
private weak var currentQrCodeScannerScreen: QrCodeScanScreen?
private var delayedScriptMessage: WKScriptMessage?
private var delayedScriptMessages: [WKScriptMessage] = []
private func handleScriptMessage(_ message: WKScriptMessage) {
guard let controller = self.controller else {
return
@ -706,7 +725,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
case "web_app_setup_main_button":
if let webView = self.webView, !webView.didTouchOnce && controller.url == nil && controller.source == .attachMenu {
self.delayedScriptMessage = message
self.delayedScriptMessages.append(message)
} else if let json = json {
if var isVisible = json["is_visible"] as? Bool {
let text = json["text"] as? String
@ -721,10 +740,35 @@ public final class WebAppController: ViewController, AttachmentContainable {
let isLoading = json["is_progress_visible"] as? Bool
let isEnabled = json["is_active"] as? Bool
let state = AttachmentMainButtonState(text: text, font: .bold, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .side : .none, isEnabled: isEnabled ?? true)
let hasShimmer = json["has_shine_effect"] as? Bool
let state = AttachmentMainButtonState(text: text, font: .bold, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .center : .none, isEnabled: isEnabled ?? true, hasShimmer: hasShimmer ?? false)
self.mainButtonState = state
}
}
case "web_app_setup_secondary_button":
if let webView = self.webView, !webView.didTouchOnce && controller.url == nil && controller.source == .attachMenu {
self.delayedScriptMessages.append(message)
} else if let json = json {
if var isVisible = json["is_visible"] as? Bool {
let text = json["text"] as? String
if (text ?? "").trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
isVisible = false
}
let backgroundColorString = json["color"] as? String
let backgroundColor = backgroundColorString.flatMap({ UIColor(hexString: $0) }) ?? self.presentationData.theme.list.itemCheckColors.fillColor
let textColorString = json["text_color"] as? String
let textColor = textColorString.flatMap({ UIColor(hexString: $0) }) ?? self.presentationData.theme.list.itemCheckColors.foregroundColor
let isLoading = json["is_progress_visible"] as? Bool
let isEnabled = json["is_active"] as? Bool
let hasShimmer = json["has_shine_effect"] as? Bool
let position = json["position"] as? String
let state = AttachmentMainButtonState(text: text, font: .bold, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .center : .none, isEnabled: isEnabled ?? true, hasShimmer: hasShimmer ?? false, position: position.flatMap { AttachmentMainButtonState.Position(rawValue: $0) })
self.secondaryButtonState = state
}
}
case "web_app_request_viewport":
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
@ -935,6 +979,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
self.updateHeaderBackgroundColor(transition: .animated(duration: 0.2, curve: .linear))
}
case "web_app_set_bottom_bar_color":
if let json = json {
if let hexColor = json["color"] as? String, let color = UIColor(hexString: hexColor) {
self.bottomPanelColor = color
}
}
case "web_app_open_popup":
if let json = json, let message = json["message"] as? String, let buttons = json["buttons"] as? [Any] {
let presentationData = self.presentationData
@ -1172,11 +1222,18 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
fileprivate var needDismissConfirmation = false
fileprivate var headerColor: UIColor?
fileprivate var headerPrimaryTextColor: UIColor?
private var headerColorKey: String?
fileprivate var bottomPanelColor: UIColor? {
didSet {
self.bottomPanelColorPromise.set(.single(self.bottomPanelColor))
}
}
fileprivate let bottomPanelColorPromise = Promise<UIColor?>(nil)
private func updateHeaderBackgroundColor(transition: ContainedViewLayoutTransition) {
guard let controller = self.controller else {
return
@ -2253,7 +2310,15 @@ final class WebAppPickerContext: AttachmentMediaPickerContext {
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
return self.controller?.controllerNode.mainButtonStatePromise.get() ?? .single(nil)
}
public var secondaryButtonState: Signal<AttachmentMainButtonState?, NoError> {
return self.controller?.controllerNode.secondaryButtonStatePromise.get() ?? .single(nil)
}
public var bottomPanelBackgroundColor: Signal<UIColor?, NoError> {
return self.controller?.controllerNode.bottomPanelColorPromise.get() ?? .single(nil)
}
init(controller: WebAppController) {
self.controller = controller
}
@ -2261,6 +2326,10 @@ final class WebAppPickerContext: AttachmentMediaPickerContext {
func mainButtonAction() {
self.controller?.controllerNode.mainButtonPressed()
}
func secondaryButtonAction() {
self.controller?.controllerNode.secondaryButtonPressed()
}
}