mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '30d5f9ddef061fc8e91aec95e561db5494a2e690'
This commit is contained in:
commit
067c944222
@ -30,6 +30,7 @@ swift_library(
|
|||||||
"//submodules/ContextUI:ContextUI",
|
"//submodules/ContextUI:ContextUI",
|
||||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||||
"//submodules/PhotoResources:PhotoResources",
|
"//submodules/PhotoResources:PhotoResources",
|
||||||
|
"//submodules/MediaResources:MediaResources",
|
||||||
"//submodules/SemanticStatusNode:SemanticStatusNode",
|
"//submodules/SemanticStatusNode:SemanticStatusNode",
|
||||||
"//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent",
|
"//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent",
|
||||||
],
|
],
|
||||||
|
@ -161,7 +161,8 @@ public class AttachmentController: ViewController {
|
|||||||
private let initialButton: AttachmentButtonType
|
private let initialButton: AttachmentButtonType
|
||||||
private let fromMenu: Bool
|
private let fromMenu: Bool
|
||||||
|
|
||||||
public var dismissed: () -> Void = {}
|
public var willDismiss: () -> Void = {}
|
||||||
|
public var didDismiss: () -> Void = {}
|
||||||
|
|
||||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||||
get {
|
get {
|
||||||
@ -268,6 +269,7 @@ public class AttachmentController: ViewController {
|
|||||||
self.container.canHaveKeyboardFocus = true
|
self.container.canHaveKeyboardFocus = true
|
||||||
self.panel = AttachmentPanel(context: controller.context, chatLocation: controller.chatLocation, updatedPresentationData: controller.updatedPresentationData)
|
self.panel = AttachmentPanel(context: controller.context, chatLocation: controller.chatLocation, updatedPresentationData: controller.updatedPresentationData)
|
||||||
self.panel.fromMenu = controller.fromMenu
|
self.panel.fromMenu = controller.fromMenu
|
||||||
|
self.panel.isStandalone = controller.isStandalone
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -636,7 +638,10 @@ public class AttachmentController: ViewController {
|
|||||||
|
|
||||||
var containerLayout = layout
|
var containerLayout = layout
|
||||||
let containerRect: CGRect
|
let containerRect: CGRect
|
||||||
|
var isCompact = true
|
||||||
if case .regular = layout.metrics.widthClass {
|
if case .regular = layout.metrics.widthClass {
|
||||||
|
isCompact = false
|
||||||
|
|
||||||
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
|
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
|
||||||
|
|
||||||
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
|
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
|
||||||
@ -645,17 +650,28 @@ public class AttachmentController: ViewController {
|
|||||||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||||
let position: CGPoint = CGPoint(x: masterWidth - 174.0, y: layout.size.height - size.height - insets.bottom - 40.0)
|
let position: CGPoint = CGPoint(x: masterWidth - 174.0, y: layout.size.height - size.height - insets.bottom - 40.0)
|
||||||
|
|
||||||
|
if controller.isStandalone {
|
||||||
|
var containerY = floorToScreenPixels((layout.size.height - size.height) / 2.0)
|
||||||
|
if let inputHeight = layout.inputHeight, inputHeight > 88.0 {
|
||||||
|
containerY = layout.size.height - inputHeight - size.height - 80.0
|
||||||
|
}
|
||||||
|
containerRect = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: containerY), size: size)
|
||||||
|
} else {
|
||||||
containerRect = CGRect(origin: position, size: size)
|
containerRect = CGRect(origin: position, size: size)
|
||||||
|
}
|
||||||
containerLayout.size = containerRect.size
|
containerLayout.size = containerRect.size
|
||||||
containerLayout.intrinsicInsets.bottom = 12.0
|
containerLayout.intrinsicInsets.bottom = 12.0
|
||||||
containerLayout.inputHeight = nil
|
containerLayout.inputHeight = nil
|
||||||
|
|
||||||
if self.wrapperNode.view.mask == nil {
|
if controller.isStandalone {
|
||||||
|
self.wrapperNode.cornerRadius = 10.0
|
||||||
|
} else if self.wrapperNode.view.mask == nil {
|
||||||
let maskView = UIImageView()
|
let maskView = UIImageView()
|
||||||
maskView.image = generateMaskImage()
|
maskView.image = generateMaskImage()
|
||||||
maskView.contentMode = .scaleToFill
|
maskView.contentMode = .scaleToFill
|
||||||
self.wrapperNode.view.mask = maskView
|
self.wrapperNode.view.mask = maskView
|
||||||
}
|
}
|
||||||
|
|
||||||
if let maskView = self.wrapperNode.view.mask {
|
if let maskView = self.wrapperNode.view.mask {
|
||||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
}
|
}
|
||||||
@ -697,7 +713,7 @@ public class AttachmentController: ViewController {
|
|||||||
if fromMenu && !hasButton, let inputContainerHeight = self.inputContainerHeight {
|
if fromMenu && !hasButton, let inputContainerHeight = self.inputContainerHeight {
|
||||||
panelHeight = inputContainerHeight
|
panelHeight = inputContainerHeight
|
||||||
}
|
}
|
||||||
if hasPanel || hasButton || fromMenu {
|
if hasPanel || hasButton || (fromMenu && isCompact) {
|
||||||
containerInsets.bottom = panelHeight
|
containerInsets.bottom = panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,13 +732,13 @@ public class AttachmentController: ViewController {
|
|||||||
panelTransition = .animated(duration: 0.25, curve: .easeInOut)
|
panelTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||||
}
|
}
|
||||||
var panelY = containerRect.height - panelHeight
|
var panelY = containerRect.height - panelHeight
|
||||||
if fromMenu {
|
if fromMenu && isCompact {
|
||||||
panelY = layout.size.height - panelHeight
|
panelY = layout.size.height - panelHeight
|
||||||
} else if !hasPanel && !hasButton {
|
} else if !hasPanel && !hasButton {
|
||||||
panelY = containerRect.height
|
panelY = containerRect.height
|
||||||
}
|
}
|
||||||
|
|
||||||
if fromMenu {
|
if fromMenu && isCompact {
|
||||||
if hasButton {
|
if hasButton {
|
||||||
self.panel.isHidden = false
|
self.panel.isHidden = false
|
||||||
self.inputContainerNode?.isHidden = true
|
self.inputContainerNode?.isHidden = true
|
||||||
@ -735,7 +751,7 @@ public class AttachmentController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
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 {
|
if transitioning && finished, isCompact {
|
||||||
self?.panel.isHidden = !hasButton
|
self?.panel.isHidden = !hasButton
|
||||||
self?.inputContainerNode?.isHidden = hasButton
|
self?.inputContainerNode?.isHidden = hasButton
|
||||||
}
|
}
|
||||||
@ -765,7 +781,8 @@ public class AttachmentController: ViewController {
|
|||||||
|
|
||||||
if self.container.supernode == nil, !controllers.isEmpty && self.container.isReady {
|
if self.container.supernode == nil, !controllers.isEmpty && self.container.isReady {
|
||||||
self.wrapperNode.addSubnode(self.container)
|
self.wrapperNode.addSubnode(self.container)
|
||||||
if controller.fromMenu {
|
|
||||||
|
if fromMenu, let _ = controller.getInputContainerNode() {
|
||||||
self.addSubnode(self.panel)
|
self.addSubnode(self.panel)
|
||||||
} else {
|
} else {
|
||||||
self.container.addSubnode(self.panel)
|
self.container.addSubnode(self.panel)
|
||||||
@ -814,6 +831,10 @@ public class AttachmentController: ViewController {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate var isStandalone: Bool {
|
||||||
|
return self.buttons.contains(.standalone)
|
||||||
|
}
|
||||||
|
|
||||||
private var node: Node {
|
private var node: Node {
|
||||||
return self.displayNode as! Node
|
return self.displayNode as! Node
|
||||||
}
|
}
|
||||||
@ -823,24 +844,29 @@ public class AttachmentController: ViewController {
|
|||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var didDismiss = false
|
private var dismissedFlag = false
|
||||||
public func _dismiss() {
|
public func _dismiss() {
|
||||||
self.dismissed()
|
|
||||||
super.dismiss(animated: false, completion: {})
|
super.dismiss(animated: false, completion: {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var ensureUnfocused = true
|
||||||
|
|
||||||
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
|
if self.ensureUnfocused {
|
||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
|
}
|
||||||
if flag {
|
if flag {
|
||||||
if !self.didDismiss {
|
if !self.dismissedFlag {
|
||||||
self.didDismiss = true
|
self.dismissedFlag = true
|
||||||
self.dismissed()
|
self.willDismiss()
|
||||||
self.node.animateOut(completion: { [weak self] in
|
self.node.animateOut(completion: { [weak self] in
|
||||||
|
self?.didDismiss()
|
||||||
self?._dismiss()
|
self?._dismiss()
|
||||||
completion?()
|
completion?()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
self.didDismiss()
|
||||||
self._dismiss()
|
self._dismiss()
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import ChatTextLinkEditUI
|
|||||||
import PhotoResources
|
import PhotoResources
|
||||||
import AnimatedStickerComponent
|
import AnimatedStickerComponent
|
||||||
import SemanticStatusNode
|
import SemanticStatusNode
|
||||||
|
import MediaResources
|
||||||
|
|
||||||
private let buttonSize = CGSize(width: 88.0, height: 49.0)
|
private let buttonSize = CGSize(width: 88.0, height: 49.0)
|
||||||
private let smallButtonWidth: CGFloat = 69.0
|
private let smallButtonWidth: CGFloat = 69.0
|
||||||
@ -79,7 +80,7 @@ private final class IconComponent: Component {
|
|||||||
self.image = nil
|
self.image = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.disposable = (svgIconImageFile(account: component.account, fileReference: fileReference, fetched: true)
|
self.disposable = (svgIconImageFile(account: component.account, fileReference: fileReference)
|
||||||
|> runOn(Queue.concurrentDefaultQueue())
|
|> runOn(Queue.concurrentDefaultQueue())
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
||||||
let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: availableSize, boundingSize: availableSize, intrinsicInsets: UIEdgeInsets())
|
let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: availableSize, boundingSize: availableSize, intrinsicInsets: UIEdgeInsets())
|
||||||
@ -221,9 +222,9 @@ private final class AttachButtonComponent: CombinedComponent {
|
|||||||
animation: AnimatedStickerComponent.Animation(
|
animation: AnimatedStickerComponent.Animation(
|
||||||
source: .file(media: animationFile),
|
source: .file(media: animationFile),
|
||||||
scale: UIScreenScale,
|
scale: UIScreenScale,
|
||||||
loop: false,
|
loop: false
|
||||||
tintColor: tintColor
|
|
||||||
),
|
),
|
||||||
|
tintColor: tintColor,
|
||||||
isAnimating: component.isSelected,
|
isAnimating: component.isSelected,
|
||||||
size: CGSize(width: iconSize.width, height: iconSize.height)
|
size: CGSize(width: iconSize.width, height: iconSize.height)
|
||||||
),
|
),
|
||||||
@ -361,23 +362,26 @@ public struct AttachmentMainButtonState {
|
|||||||
let textColor: UIColor
|
let textColor: UIColor
|
||||||
let isVisible: Bool
|
let isVisible: Bool
|
||||||
let isLoading: Bool
|
let isLoading: Bool
|
||||||
|
let isEnabled: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
text: String?,
|
text: String?,
|
||||||
backgroundColor: UIColor,
|
backgroundColor: UIColor,
|
||||||
textColor: UIColor,
|
textColor: UIColor,
|
||||||
isVisible: Bool,
|
isVisible: Bool,
|
||||||
isLoading: Bool
|
isLoading: Bool,
|
||||||
|
isEnabled: Bool
|
||||||
) {
|
) {
|
||||||
self.text = text
|
self.text = text
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.isVisible = isVisible
|
self.isVisible = isVisible
|
||||||
self.isLoading = isLoading
|
self.isLoading = isLoading
|
||||||
|
self.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
static var initial: AttachmentMainButtonState {
|
static var initial: AttachmentMainButtonState {
|
||||||
return AttachmentMainButtonState(text: nil, backgroundColor: .clear, textColor: .clear, isVisible: false, isLoading: false)
|
return AttachmentMainButtonState(text: nil, backgroundColor: .clear, textColor: .clear, isVisible: false, isLoading: false, isEnabled: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +405,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
|||||||
self.addSubnode(self.statusNode)
|
self.addSubnode(self.statusNode)
|
||||||
|
|
||||||
self.highligthedChanged = { [weak self] highlighted in
|
self.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, strongSelf.state.isEnabled {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
strongSelf.layer.removeAnimation(forKey: "opacity")
|
strongSelf.layer.removeAnimation(forKey: "opacity")
|
||||||
strongSelf.alpha = 0.65
|
strongSelf.alpha = 0.65
|
||||||
@ -479,6 +483,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private var scrollLayout: (width: CGFloat, contentSize: CGSize)?
|
private var scrollLayout: (width: CGFloat, contentSize: CGSize)?
|
||||||
|
|
||||||
var fromMenu: Bool = false
|
var fromMenu: Bool = false
|
||||||
|
var isStandalone: Bool = false
|
||||||
|
|
||||||
var selectionChanged: (AttachmentButtonType) -> Bool = { _ in return false }
|
var selectionChanged: (AttachmentButtonType) -> Bool = { _ in return false }
|
||||||
var beganTextEditing: () -> Void = {}
|
var beganTextEditing: () -> Void = {}
|
||||||
@ -775,10 +780,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
var leftNodeOriginX = (layout.size.width - internalWidth) / 2.0
|
var leftNodeOriginX = (layout.size.width - internalWidth) / 2.0
|
||||||
|
|
||||||
var buttonWidth = buttonSize.width
|
var buttonWidth = buttonSize.width
|
||||||
if self.buttons.count > 6 {
|
if self.buttons.count > 6 && layout.size.width < layout.size.height {
|
||||||
buttonWidth = smallButtonWidth
|
buttonWidth = smallButtonWidth
|
||||||
distanceBetweenNodes = buttonWidth
|
distanceBetweenNodes = buttonWidth
|
||||||
leftNodeOriginX = sideInset + buttonWidth / 2.0
|
leftNodeOriginX = layout.safeInsets.left + sideInset + buttonWidth / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 ..< self.buttons.count {
|
for i in 0 ..< self.buttons.count {
|
||||||
@ -803,13 +808,33 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let type = self.buttons[i]
|
let type = self.buttons[i]
|
||||||
if case let .app(peer, _, iconFiles) = type {
|
if case let .app(peer, _, iconFiles) = type {
|
||||||
for (name, file) in iconFiles {
|
for (name, file) in iconFiles {
|
||||||
if [.default, .iOSAnimated].contains(name) {
|
if [.default, .iOSAnimated, .placeholder].contains(name) {
|
||||||
if self.iconDisposables[file.fileId] == nil, let peer = PeerReference(peer) {
|
if self.iconDisposables[file.fileId] == nil, let peer = PeerReference(peer) {
|
||||||
|
if case .placeholder = name {
|
||||||
|
let account = self.context.account
|
||||||
|
let path = account.postbox.mediaBox.cachedRepresentationCompletePath(file.resource.id, representation: CachedPreparedSvgRepresentation())
|
||||||
|
if !FileManager.default.fileExists(atPath: path) {
|
||||||
|
let accountFullSizeData = Signal<(Data?, Bool), NoError> { subscriber in
|
||||||
|
let accountResource = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedPreparedSvgRepresentation(), complete: false, fetch: true)
|
||||||
|
|
||||||
|
let fetchedFullSize = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .media(media: .attachBot(peer: peer, media: file), resource: file.resource))
|
||||||
|
let fetchedFullSizeDisposable = fetchedFullSize.start()
|
||||||
|
let fullSizeDisposable = accountResource.start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
fetchedFullSizeDisposable.dispose()
|
||||||
|
fullSizeDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.iconDisposables[file.fileId] = accountFullSizeData.start()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.iconDisposables[file.fileId] = freeMediaFileInteractiveFetched(account: self.context.account, fileReference: .attachBot(peer: peer, media: file)).start()
|
self.iconDisposables[file.fileId] = freeMediaFileInteractiveFetched(account: self.context.account, fileReference: .attachBot(peer: peer, media: file)).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let _ = buttonView.update(
|
let _ = buttonView.update(
|
||||||
transition: buttonTransition,
|
transition: buttonTransition,
|
||||||
component: AnyComponent(AttachButtonComponent(
|
component: AnyComponent(AttachButtonComponent(
|
||||||
@ -848,9 +873,9 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
var contentSize = CGSize(width: layout.size.width, height: buttonSize.height)
|
var contentSize = CGSize(width: layout.size.width, height: buttonSize.height)
|
||||||
var buttonWidth = buttonSize.width
|
var buttonWidth = buttonSize.width
|
||||||
if self.buttons.count > 6 {
|
if self.buttons.count > 6 && layout.size.width < layout.size.height {
|
||||||
buttonWidth = smallButtonWidth
|
buttonWidth = smallButtonWidth
|
||||||
contentSize = CGSize(width: sideInset * 2.0 + CGFloat(self.buttons.count) * buttonWidth, height: buttonSize.height)
|
contentSize.width = layout.safeInsets.left + layout.safeInsets.right + sideInset * 2.0 + CGFloat(self.buttons.count) * buttonWidth
|
||||||
}
|
}
|
||||||
self.scrollLayout = (layout.size.width, contentSize)
|
self.scrollLayout = (layout.size.width, contentSize)
|
||||||
|
|
||||||
@ -899,7 +924,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
func updateMainButtonState(_ mainButtonState: AttachmentMainButtonState?) {
|
func updateMainButtonState(_ mainButtonState: AttachmentMainButtonState?) {
|
||||||
var currentButtonState = self.mainButtonState
|
var currentButtonState = self.mainButtonState
|
||||||
if mainButtonState == nil {
|
if mainButtonState == nil {
|
||||||
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, backgroundColor: currentButtonState.backgroundColor, textColor: currentButtonState.textColor, isVisible: false, isLoading: false)
|
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, backgroundColor: currentButtonState.backgroundColor, textColor: currentButtonState.textColor, isVisible: false, isLoading: false, isEnabled: currentButtonState.isEnabled)
|
||||||
}
|
}
|
||||||
self.mainButtonState = mainButtonState ?? currentButtonState
|
self.mainButtonState = mainButtonState ?? currentButtonState
|
||||||
}
|
}
|
||||||
@ -955,9 +980,9 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.view.addSubview(inputNodeSnapshotView)
|
self.view.addSubview(inputNodeSnapshotView)
|
||||||
|
|
||||||
let targetInputPosition = CGPoint(x: inputNodeSnapshotView.center.x + inputNodeSnapshotView.frame.width, y: self.mainButtonNode.position.y)
|
let targetInputPosition = CGPoint(x: inputNodeSnapshotView.center.x + inputNodeSnapshotView.frame.width, y: self.mainButtonNode.position.y)
|
||||||
transition.updatePosition(layer: inputNodeSnapshotView.layer, position: targetInputPosition, completion: { [weak inputNodeSnapshotView] _ in
|
transition.updatePosition(layer: inputNodeSnapshotView.layer, position: targetInputPosition, completion: { [weak inputNodeSnapshotView, weak self] _ in
|
||||||
inputNodeSnapshotView?.removeFromSuperview()
|
inputNodeSnapshotView?.removeFromSuperview()
|
||||||
self.animatingTransition = false
|
self?.animatingTransition = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,7 +999,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.dismissed = dismissed
|
self.dismissed = dismissed
|
||||||
|
|
||||||
let action = {
|
let action = {
|
||||||
guard let menuIconSnapshotView = inputTransition.menuIconNode.view.snapshotView(afterScreenUpdates: true), let menuTextSnapshotView = inputTransition.menuTextNode.view.snapshotView(afterScreenUpdates: false) else {
|
guard let menuIconSnapshotView = inputTransition.menuIconNode.view.snapshotView(afterScreenUpdates: false), let menuTextSnapshotView = inputTransition.menuTextNode.view.snapshotView(afterScreenUpdates: false) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,19 +1036,19 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let targetInputFrame = CGRect(x: inputTransition.menuButtonNode.frame.maxX, y: 0.0, width: inputNodeSnapshotView.frame.width - inputTransition.menuButtonNode.frame.maxX, height: inputNodeSnapshotView.frame.height)
|
let targetInputFrame = CGRect(x: inputTransition.menuButtonNode.frame.maxX, y: 0.0, width: inputNodeSnapshotView.frame.width - inputTransition.menuButtonNode.frame.maxX, height: inputNodeSnapshotView.frame.height)
|
||||||
inputNodeSnapshotView.frame = targetInputFrame.offsetBy(dx: targetInputFrame.width, dy: self.mainButtonNode.position.y - inputNodeSnapshotView.frame.height / 2.0)
|
inputNodeSnapshotView.frame = targetInputFrame.offsetBy(dx: targetInputFrame.width, dy: self.mainButtonNode.position.y - inputNodeSnapshotView.frame.height / 2.0)
|
||||||
self.view.addSubview(inputNodeSnapshotView)
|
self.view.addSubview(inputNodeSnapshotView)
|
||||||
transition.updateFrame(layer: inputNodeSnapshotView.layer, frame: targetInputFrame, completion: { [weak inputNodeSnapshotView, weak menuIconSnapshotView, weak menuTextSnapshotView] _ in
|
transition.updateFrame(layer: inputNodeSnapshotView.layer, frame: targetInputFrame, completion: { [weak inputNodeSnapshotView, weak menuIconSnapshotView, weak menuTextSnapshotView, weak self] _ in
|
||||||
inputNodeSnapshotView?.removeFromSuperview()
|
inputNodeSnapshotView?.removeFromSuperview()
|
||||||
self.animatingTransition = false
|
self?.animatingTransition = false
|
||||||
|
|
||||||
if !dismissed {
|
if !dismissed {
|
||||||
menuIconSnapshotView?.removeFromSuperview()
|
menuIconSnapshotView?.removeFromSuperview()
|
||||||
menuTextSnapshotView?.removeFromSuperview()
|
menuTextSnapshotView?.removeFromSuperview()
|
||||||
|
|
||||||
self.mainButtonNode.backgroundColor = sourceButtonColor
|
self?.mainButtonNode.backgroundColor = sourceButtonColor
|
||||||
self.mainButtonNode.frame = sourceButtonFrame
|
self?.mainButtonNode.frame = sourceButtonFrame
|
||||||
self.mainButtonNode.textNode.position = sourceButtonTextPosition
|
self?.mainButtonNode.textNode.position = sourceButtonTextPosition
|
||||||
self.mainButtonNode.textNode.layer.removeAllAnimations()
|
self?.mainButtonNode.textNode.layer.removeAllAnimations()
|
||||||
self.mainButtonNode.cornerRadius = sourceButtonCornerRadius
|
self?.mainButtonNode.cornerRadius = sourceButtonCornerRadius
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1087,9 +1112,16 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let containerTransition: ContainedViewLayoutTransition
|
let containerTransition: ContainedViewLayoutTransition
|
||||||
let containerFrame: CGRect
|
let containerFrame: CGRect
|
||||||
if isButtonVisible {
|
if isButtonVisible {
|
||||||
let height: CGFloat
|
var height: CGFloat
|
||||||
if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero {
|
if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero {
|
||||||
height = bounds.height + 9.0
|
height = bounds.height + 9.0
|
||||||
|
if case .regular = layout.metrics.widthClass {
|
||||||
|
if self.isStandalone {
|
||||||
|
height -= 3.0
|
||||||
|
} else {
|
||||||
|
height += 6.0
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
height = bounds.height + 9.0 + 8.0
|
height = bounds.height + 9.0 + 8.0
|
||||||
}
|
}
|
||||||
|
@ -16,24 +16,24 @@ public final class AnimatedStickerComponent: Component {
|
|||||||
public var source: Source
|
public var source: Source
|
||||||
public var scale: CGFloat
|
public var scale: CGFloat
|
||||||
public var loop: Bool
|
public var loop: Bool
|
||||||
public var tintColor: UIColor?
|
|
||||||
|
|
||||||
public init(source: Source, scale: CGFloat = 2.0, loop: Bool, tintColor: UIColor? = nil) {
|
public init(source: Source, scale: CGFloat = 2.0, loop: Bool) {
|
||||||
self.source = source
|
self.source = source
|
||||||
self.scale = scale
|
self.scale = scale
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.tintColor = tintColor
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public let account: Account
|
public let account: Account
|
||||||
public let animation: Animation
|
public let animation: Animation
|
||||||
|
public var tintColor: UIColor?
|
||||||
public let isAnimating: Bool
|
public let isAnimating: Bool
|
||||||
public let size: CGSize
|
public let size: CGSize
|
||||||
|
|
||||||
public init(account: Account, animation: Animation, isAnimating: Bool = true, size: CGSize) {
|
public init(account: Account, animation: Animation, tintColor: UIColor? = nil, isAnimating: Bool = true, size: CGSize) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.animation = animation
|
self.animation = animation
|
||||||
|
self.tintColor = tintColor
|
||||||
self.isAnimating = isAnimating
|
self.isAnimating = isAnimating
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
@ -45,6 +45,9 @@ public final class AnimatedStickerComponent: Component {
|
|||||||
if lhs.animation != rhs.animation {
|
if lhs.animation != rhs.animation {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.tintColor != rhs.tintColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.isAnimating != rhs.isAnimating {
|
if lhs.isAnimating != rhs.isAnimating {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -100,13 +103,14 @@ public final class AnimatedStickerComponent: Component {
|
|||||||
case let .file(media):
|
case let .file(media):
|
||||||
source = AnimatedStickerResourceSource(account: component.account, resource: media.resource, fitzModifier: nil, isVideo: false)
|
source = AnimatedStickerResourceSource(account: component.account, resource: media.resource, fitzModifier: nil, isVideo: false)
|
||||||
}
|
}
|
||||||
animationNode.setOverlayColor(component.animation.tintColor, replace: true, animated: false)
|
|
||||||
|
|
||||||
var playbackMode: AnimatedStickerPlaybackMode = .still(.start)
|
var playbackMode: AnimatedStickerPlaybackMode = .still(.start)
|
||||||
if component.animation.loop {
|
if component.animation.loop {
|
||||||
playbackMode = .loop
|
playbackMode = .loop
|
||||||
} else if component.isAnimating {
|
} else if component.isAnimating {
|
||||||
playbackMode = .once
|
playbackMode = .once
|
||||||
|
} else {
|
||||||
|
animationNode.autoplay = true
|
||||||
}
|
}
|
||||||
animationNode.setup(source: source, width: Int(component.size.width * component.animation.scale), height: Int(component.size.height * component.animation.scale), playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
animationNode.setup(source: source, width: Int(component.size.width * component.animation.scale), height: Int(component.size.height * component.animation.scale), playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||||
animationNode.visibility = self.isInHierarchy
|
animationNode.visibility = self.isInHierarchy
|
||||||
@ -115,6 +119,8 @@ public final class AnimatedStickerComponent: Component {
|
|||||||
self.addSubnode(animationNode)
|
self.addSubnode(animationNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.animationNode?.setOverlayColor(component.tintColor, replace: true, animated: false)
|
||||||
|
|
||||||
if !component.animation.loop && component.isAnimating != self.component?.isAnimating {
|
if !component.animation.loop && component.isAnimating != self.component?.isAnimating {
|
||||||
if component.isAnimating {
|
if component.isAnimating {
|
||||||
let _ = self.animationNode?.playIfNeeded()
|
let _ = self.animationNode?.playIfNeeded()
|
||||||
|
@ -714,7 +714,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
override init() {
|
override init() {
|
||||||
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false)
|
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false)
|
||||||
self.parentShadowNode = ASImageNode()
|
self.parentShadowNode = ASImageNode()
|
||||||
self.parentShadowNode.image = UIImage(bundleImageName: "Components/Context Menu/Shadow")?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 60)
|
self.parentShadowNode.image = UIImage(bundleImageName: "Components/Context Menu/Shadow")?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 48)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import OverlayStatusController
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import ZipArchive
|
import ZipArchive
|
||||||
|
import WebKit
|
||||||
|
|
||||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
@ -74,6 +75,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case resetHoles(PresentationTheme)
|
case resetHoles(PresentationTheme)
|
||||||
case reindexUnread(PresentationTheme)
|
case reindexUnread(PresentationTheme)
|
||||||
case resetBiometricsData(PresentationTheme)
|
case resetBiometricsData(PresentationTheme)
|
||||||
|
case resetWebViewCache(PresentationTheme)
|
||||||
case optimizeDatabase(PresentationTheme)
|
case optimizeDatabase(PresentationTheme)
|
||||||
case photoPreview(PresentationTheme, Bool)
|
case photoPreview(PresentationTheme, Bool)
|
||||||
case knockoutWallpaper(PresentationTheme, Bool)
|
case knockoutWallpaper(PresentationTheme, Bool)
|
||||||
@ -103,7 +105,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.logging.rawValue
|
return DebugControllerSection.logging.rawValue
|
||||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .snow:
|
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .snow:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .preferredVideoCodec:
|
case .preferredVideoCodec:
|
||||||
return DebugControllerSection.videoExperiments.rawValue
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
@ -160,30 +162,32 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 20
|
return 20
|
||||||
case .resetBiometricsData:
|
case .resetBiometricsData:
|
||||||
return 21
|
return 21
|
||||||
case .optimizeDatabase:
|
case .resetWebViewCache:
|
||||||
return 22
|
return 22
|
||||||
case .photoPreview:
|
case .optimizeDatabase:
|
||||||
return 23
|
return 23
|
||||||
case .knockoutWallpaper:
|
case .photoPreview:
|
||||||
return 24
|
return 24
|
||||||
|
case .knockoutWallpaper:
|
||||||
|
return 25
|
||||||
case .experimentalCompatibility:
|
case .experimentalCompatibility:
|
||||||
return 26
|
return 26
|
||||||
case .enableDebugDataDisplay:
|
case .enableDebugDataDisplay:
|
||||||
return 27
|
return 27
|
||||||
case .acceleratedStickers:
|
case .acceleratedStickers:
|
||||||
return 29
|
return 28
|
||||||
case .experimentalBackground:
|
case .experimentalBackground:
|
||||||
return 30
|
return 29
|
||||||
case .snow:
|
case .snow:
|
||||||
return 31
|
return 30
|
||||||
case .playerEmbedding:
|
case .playerEmbedding:
|
||||||
return 32
|
return 31
|
||||||
case .playlistPlayback:
|
case .playlistPlayback:
|
||||||
return 33
|
return 32
|
||||||
case .voiceConference:
|
case .voiceConference:
|
||||||
return 34
|
return 33
|
||||||
case let .preferredVideoCodec(index, _, _, _):
|
case let .preferredVideoCodec(index, _, _, _):
|
||||||
return 35 + index
|
return 34 + index
|
||||||
case .disableVideoAspectScaling:
|
case .disableVideoAspectScaling:
|
||||||
return 100
|
return 100
|
||||||
case .enableVoipTcp:
|
case .enableVoipTcp:
|
||||||
@ -777,6 +781,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil)
|
return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil)
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
|
case .resetWebViewCache:
|
||||||
|
return ItemListActionItem(presentationData: presentationData, title: "Clear Web View Cache", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
|
WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{ })
|
||||||
|
})
|
||||||
case .optimizeDatabase:
|
case .optimizeDatabase:
|
||||||
return ItemListActionItem(presentationData: presentationData, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(presentationData: presentationData, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
guard let context = arguments.context else {
|
guard let context = arguments.context else {
|
||||||
@ -968,6 +976,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.resetHoles(presentationData.theme))
|
entries.append(.resetHoles(presentationData.theme))
|
||||||
if isMainApp {
|
if isMainApp {
|
||||||
entries.append(.reindexUnread(presentationData.theme))
|
entries.append(.reindexUnread(presentationData.theme))
|
||||||
|
entries.append(.resetWebViewCache(presentationData.theme))
|
||||||
}
|
}
|
||||||
entries.append(.optimizeDatabase(presentationData.theme))
|
entries.append(.optimizeDatabase(presentationData.theme))
|
||||||
if isMainApp {
|
if isMainApp {
|
||||||
|
@ -344,3 +344,23 @@ public final class CachedPreparedPatternWallpaperRepresentation: CachedMediaReso
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final class CachedPreparedSvgRepresentation: CachedMediaResourceRepresentation {
|
||||||
|
public let keepDuration: CachedMediaRepresentationKeepDuration = .general
|
||||||
|
|
||||||
|
public var uniqueId: String {
|
||||||
|
return "prepared-svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
|
||||||
|
if to is CachedPreparedSvgRepresentation {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2283,28 +2283,35 @@ public func instantPageImageFile(account: Account, fileReference: FileMediaRefer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func svgIconImageFile(account: Account, fileReference: FileMediaReference, fetched: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func svgIconImageFile(account: Account, fileReference: FileMediaReference, stickToTop: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
return chatMessageFileDatas(account: account, fileReference: fileReference, progressive: false, fetched: false)
|
let data = account.postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedPreparedSvgRepresentation(), complete: false, fetch: true)
|
||||||
|
|
||||||
|
return data
|
||||||
|> map { value in
|
|> map { value in
|
||||||
let fullSizePath = value._1
|
let fullSizePath = value.path
|
||||||
let fullSizeComplete = value._2
|
let fullSizeComplete = value.complete
|
||||||
return { arguments in
|
return { arguments in
|
||||||
// assertNotOnMainThread()
|
|
||||||
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
||||||
|
|
||||||
let drawingRect = arguments.drawingRect
|
let drawingRect = arguments.drawingRect
|
||||||
let fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
var fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
||||||
|
|
||||||
var fullSizeImage: UIImage?
|
var fullSizeImage: UIImage?
|
||||||
let imageOrientation: UIImage.Orientation = .up
|
let imageOrientation: UIImage.Orientation = .up
|
||||||
|
|
||||||
if let fullSizePath = fullSizePath {
|
|
||||||
if fullSizeComplete, let data = try? Data(contentsOf: URL(fileURLWithPath: fullSizePath)) {
|
if fullSizeComplete, let data = try? Data(contentsOf: URL(fileURLWithPath: fullSizePath)) {
|
||||||
fullSizeImage = drawSvgImage(data, CGSize(width: 90.0, height: 90.0), .clear, .black, false)
|
fullSizeImage = renderPreparedImage(data, CGSize.zero, .clear, UIScreenScale)
|
||||||
|
|
||||||
|
// fullSizeImage = drawSvgImage(data, stickToTop ? CGSize.zero : CGSize(width: 90.0, height: 90.0), .clear, .black, false)
|
||||||
|
if let image = fullSizeImage {
|
||||||
|
fittedSize = image.size.aspectFitted(arguments.boundingSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
var fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||||
|
if stickToTop {
|
||||||
|
fittedRect.origin.y = drawingRect.size.height - fittedSize.height
|
||||||
|
}
|
||||||
|
|
||||||
context.withFlippedContext { c in
|
context.withFlippedContext { c in
|
||||||
if let fullSizeImage = fullSizeImage?.cgImage {
|
if let fullSizeImage = fullSizeImage?.cgImage {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
||||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
|
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale);
|
||||||
|
|
||||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque);
|
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque);
|
||||||
|
|
||||||
|
@ -111,6 +111,10 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CGSizeEqualToSize(size, CGSizeZero)) {
|
||||||
|
size = CGSizeMake(image->width, image->height);
|
||||||
|
}
|
||||||
|
|
||||||
double deltaTime = -1.0f * [startTime timeIntervalSinceNow];
|
double deltaTime = -1.0f * [startTime timeIntervalSinceNow];
|
||||||
printf("parseTime = %f\n", deltaTime);
|
printf("parseTime = %f\n", deltaTime);
|
||||||
|
|
||||||
@ -354,11 +358,11 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size) {
|
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale) {
|
||||||
NSDate *startTime = [NSDate date];
|
NSDate *startTime = [NSDate date];
|
||||||
|
|
||||||
UIColor *foregroundColor = [UIColor whiteColor];
|
UIColor *foregroundColor = [UIColor whiteColor];
|
||||||
UIColor *backgroundColor = [UIColor blackColor];
|
|
||||||
|
|
||||||
int32_t ptr = 0;
|
int32_t ptr = 0;
|
||||||
int32_t width;
|
int32_t width;
|
||||||
@ -373,17 +377,27 @@ UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size) {
|
|||||||
[data getBytes:&height range:NSMakeRange(ptr, sizeof(height))];
|
[data getBytes:&height range:NSMakeRange(ptr, sizeof(height))];
|
||||||
ptr += sizeof(height);
|
ptr += sizeof(height);
|
||||||
|
|
||||||
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
|
if (CGSizeEqualToSize(size, CGSizeZero)) {
|
||||||
|
size = CGSizeMake(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTransparent = [backgroundColor isEqual:[UIColor clearColor]];
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(size, !isTransparent, scale);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
if (isTransparent) {
|
||||||
|
CGContextClearRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
||||||
|
} else {
|
||||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||||
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
||||||
|
}
|
||||||
|
|
||||||
CGSize svgSize = CGSizeMake(width, height);
|
CGSize svgSize = CGSizeMake(width, height);
|
||||||
CGSize drawingSize = aspectFillSize(svgSize, size);
|
CGSize drawingSize = aspectFillSize(svgSize, size);
|
||||||
|
|
||||||
CGFloat scale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height));
|
CGFloat renderScale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height));
|
||||||
|
|
||||||
CGContextScaleCTM(context, scale, scale);
|
CGContextScaleCTM(context, renderScale, renderScale);
|
||||||
CGContextTranslateCTM(context, (size.width - drawingSize.width) / 2.0, (size.height - drawingSize.height) / 2.0);
|
CGContextTranslateCTM(context, (size.width - drawingSize.width) / 2.0, (size.height - drawingSize.height) / 2.0);
|
||||||
|
|
||||||
while (ptr < data.length) {
|
while (ptr < data.length) {
|
||||||
|
@ -16,6 +16,7 @@ public final class AttachMenuBots: Equatable, Codable {
|
|||||||
case iOSStatic
|
case iOSStatic
|
||||||
case iOSAnimated
|
case iOSAnimated
|
||||||
case macOSAnimated
|
case macOSAnimated
|
||||||
|
case placeholder
|
||||||
|
|
||||||
init?(string: String) {
|
init?(string: String) {
|
||||||
switch string {
|
switch string {
|
||||||
@ -27,6 +28,8 @@ public final class AttachMenuBots: Equatable, Codable {
|
|||||||
self = .iOSAnimated
|
self = .iOSAnimated
|
||||||
case "macos_animated":
|
case "macos_animated":
|
||||||
self = .macOSAnimated
|
self = .macOSAnimated
|
||||||
|
case "placeholder_static":
|
||||||
|
self = .placeholder
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 6.5 KiB |
@ -279,6 +279,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private var bankCardDisposable: MetaDisposable?
|
private var bankCardDisposable: MetaDisposable?
|
||||||
private var hasActiveGroupCallDisposable: Disposable?
|
private var hasActiveGroupCallDisposable: Disposable?
|
||||||
private var sendAsPeersDisposable: Disposable?
|
private var sendAsPeersDisposable: Disposable?
|
||||||
|
private let preloadAttachBotIconsDisposables = DisposableSet()
|
||||||
|
|
||||||
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
|
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
|
||||||
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
@ -2931,6 +2932,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .speak:
|
case .speak:
|
||||||
let _ = speakText(text.string)
|
let _ = speakText(text.string)
|
||||||
case .translate:
|
case .translate:
|
||||||
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
|
|
||||||
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { sharedData in
|
|> deliverOnMainQueue).start(next: { sharedData in
|
||||||
@ -3360,11 +3363,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
let openWebView = {
|
let openWebView = {
|
||||||
if fromMenu {
|
if fromMenu {
|
||||||
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true)
|
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url, concealed: true, forceExternal: true)
|
self?.openUrl(url, concealed: true, forceExternal: true)
|
||||||
}, getInputContainerNode: { [weak self] in
|
}, getInputContainerNode: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, let layout = strongSelf.validLayout, case .compact = layout.metrics.widthClass {
|
||||||
return (strongSelf.chatDisplayNode.getWindowInputAccessoryHeight(), strongSelf.chatDisplayNode.inputPanelContainerNode, {
|
return (strongSelf.chatDisplayNode.getWindowInputAccessoryHeight(), strongSelf.chatDisplayNode.inputPanelContainerNode, {
|
||||||
return strongSelf.chatDisplayNode.textInputPanelNode?.makeAttachmentMenuTransition(accessoryPanelNode: nil)
|
return strongSelf.chatDisplayNode.textInputPanelNode?.makeAttachmentMenuTransition(accessoryPanelNode: nil)
|
||||||
})
|
})
|
||||||
@ -3373,11 +3376,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, completion: { [weak self] in
|
}, completion: { [weak self] in
|
||||||
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||||
}, dismissed: { [weak self] in
|
}, willDismiss: { [weak self] in
|
||||||
self?.interfaceInteraction?.updateShowWebView { _ in
|
self?.interfaceInteraction?.updateShowWebView { _ in
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}, didDismiss: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let isFocused = strongSelf.chatDisplayNode.textInputPanelNode?.isFocused ?? false
|
||||||
strongSelf.chatDisplayNode.insertSubnode(strongSelf.chatDisplayNode.inputPanelContainerNode, aboveSubnode: strongSelf.chatDisplayNode.historyNodeContainer)
|
strongSelf.chatDisplayNode.insertSubnode(strongSelf.chatDisplayNode.inputPanelContainerNode, aboveSubnode: strongSelf.chatDisplayNode.historyNodeContainer)
|
||||||
|
if isFocused {
|
||||||
|
strongSelf.chatDisplayNode.textInputPanelNode?.ensureFocused()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
strongSelf.present(controller, in: .window(.root))
|
strongSelf.present(controller, in: .window(.root))
|
||||||
strongSelf.currentMenuWebAppController = controller
|
strongSelf.currentMenuWebAppController = controller
|
||||||
@ -3390,7 +3400,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false)
|
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url, concealed: true, forceExternal: true)
|
self?.openUrl(url, concealed: true, forceExternal: true)
|
||||||
})
|
})
|
||||||
@ -3410,7 +3420,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false)
|
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url, concealed: true, forceExternal: true)
|
self?.openUrl(url, concealed: true, forceExternal: true)
|
||||||
}, completion: { [weak self] in
|
}, completion: { [weak self] in
|
||||||
@ -4822,6 +4832,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.nextChannelToReadDisposable?.dispose()
|
self.nextChannelToReadDisposable?.dispose()
|
||||||
self.inviteRequestsDisposable.dispose()
|
self.inviteRequestsDisposable.dispose()
|
||||||
self.sendAsPeersDisposable?.dispose()
|
self.sendAsPeersDisposable?.dispose()
|
||||||
|
self.preloadAttachBotIconsDisposables.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
|
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
|
||||||
@ -6784,7 +6795,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if updatedInputMode == .text {
|
var dismissWebView = false
|
||||||
|
switch updatedInputMode {
|
||||||
|
case .text, .media, .inputButtons:
|
||||||
|
dismissWebView = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if dismissWebView {
|
||||||
updated = updated.updatedShowWebView(false)
|
updated = updated.updatedShowWebView(false)
|
||||||
}
|
}
|
||||||
return updated
|
return updated
|
||||||
@ -6794,9 +6812,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.interfaceInteraction?.updateShowWebView { _ in
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
strongSelf.chatDisplayNode.openStickers()
|
strongSelf.chatDisplayNode.openStickers()
|
||||||
strongSelf.mediaRecordingModeTooltipController?.dismissImmediately()
|
strongSelf.mediaRecordingModeTooltipController?.dismissImmediately()
|
||||||
}, editMessage: { [weak self] in
|
}, editMessage: { [weak self] in
|
||||||
@ -9047,6 +9062,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}).start())
|
}).start())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.preloadAttachBotIcons()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = self.focusOnSearchAfterAppearance {
|
if let _ = self.focusOnSearchAfterAppearance {
|
||||||
@ -9772,6 +9789,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
if let currentMenuWebAppController = self.currentMenuWebAppController, !self.presentationInterfaceState.showWebView {
|
if let currentMenuWebAppController = self.currentMenuWebAppController, !self.presentationInterfaceState.showWebView {
|
||||||
self.currentMenuWebAppController = nil
|
self.currentMenuWebAppController = nil
|
||||||
|
if let currentMenuWebAppController = currentMenuWebAppController as? AttachmentController {
|
||||||
|
currentMenuWebAppController.ensureUnfocused = false
|
||||||
|
}
|
||||||
currentMenuWebAppController.dismiss(animated: true, completion: nil)
|
currentMenuWebAppController.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10941,7 +10961,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
completion(controller, nil)
|
completion(controller, nil)
|
||||||
strongSelf.controllerNavigationDisposable.set(nil)
|
strongSelf.controllerNavigationDisposable.set(nil)
|
||||||
case let .app(bot, botName, _):
|
case let .app(bot, botName, _):
|
||||||
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, buttonText: nil, keepAliveSignal: nil, fromMenu: false)
|
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: botPayload, buttonText: nil, keepAliveSignal: nil, fromMenu: false)
|
||||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||||
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId)
|
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId)
|
||||||
controller.openUrl = { [weak self] url in
|
controller.openUrl = { [weak self] url in
|
||||||
@ -15702,6 +15722,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func preloadAttachBotIcons() {
|
||||||
|
let _ = (self.context.engine.messages.attachMenuBots()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] bots in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for bot in bots {
|
||||||
|
for (name, file) in bot.icons {
|
||||||
|
if [.iOSAnimated].contains(name), let peer = PeerReference(bot.peer) {
|
||||||
|
strongSelf.preloadAttachBotIconsDisposables.add(freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: file)).start())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
@ -907,6 +907,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let buttonTitle: String
|
||||||
|
if case let .webView(title, _) = interfaceState.botMenuButton {
|
||||||
|
buttonTitle = title
|
||||||
|
} else {
|
||||||
|
buttonTitle = interfaceState.strings.Conversation_InputMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
self.menuButtonTextNode.attributedText = NSAttributedString(string: buttonTitle, font: Font.with(size: 16.0, design: .round, weight: .medium, traits: []), textColor: interfaceState.theme.chat.inputPanel.actionControlForegroundColor)
|
||||||
|
self.menuButton.accessibilityLabel = self.menuButtonTextNode.attributedText?.string
|
||||||
|
menuTextSize = self.menuButtonTextNode.updateLayout(CGSize(width: width, height: 44.0))
|
||||||
|
|
||||||
var updateSendButtonIcon = false
|
var updateSendButtonIcon = false
|
||||||
if (previousState?.interfaceState.editMessage != nil) != (interfaceState.interfaceState.editMessage != nil) {
|
if (previousState?.interfaceState.editMessage != nil) != (interfaceState.interfaceState.editMessage != nil) {
|
||||||
updateSendButtonIcon = true
|
updateSendButtonIcon = true
|
||||||
@ -945,17 +956,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
|
|
||||||
self.menuButtonBackgroundNode.backgroundColor = interfaceState.theme.chat.inputPanel.actionControlFillColor
|
self.menuButtonBackgroundNode.backgroundColor = interfaceState.theme.chat.inputPanel.actionControlFillColor
|
||||||
|
|
||||||
let buttonTitle: String
|
|
||||||
if case let .webView(title, _) = interfaceState.botMenuButton {
|
|
||||||
buttonTitle = title
|
|
||||||
} else {
|
|
||||||
buttonTitle = interfaceState.strings.Conversation_InputMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
self.menuButtonTextNode.attributedText = NSAttributedString(string: buttonTitle, font: Font.with(size: 16.0, design: .round, weight: .medium, traits: []), textColor: interfaceState.theme.chat.inputPanel.actionControlForegroundColor)
|
|
||||||
self.menuButton.accessibilityLabel = interfaceState.strings.Conversation_InputMenu
|
|
||||||
menuTextSize = self.menuButtonTextNode.updateLayout(CGSize(width: width, height: 44.0))
|
|
||||||
|
|
||||||
if isEditingMedia {
|
if isEditingMedia {
|
||||||
self.attachmentButton.setImage(PresentationResourcesChat.chatInputPanelEditAttachmentButtonImage(interfaceState.theme), for: [])
|
self.attachmentButton.setImage(PresentationResourcesChat.chatInputPanelEditAttachmentButtonImage(interfaceState.theme), for: [])
|
||||||
} else {
|
} else {
|
||||||
|
@ -137,6 +137,14 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
|
|||||||
}
|
}
|
||||||
return fetchPreparedPatternWallpaperRepresentation(resource: resource, resourceData: data, representation: representation)
|
return fetchPreparedPatternWallpaperRepresentation(resource: resource, resourceData: data, representation: representation)
|
||||||
}
|
}
|
||||||
|
} else if let representation = representation as? CachedPreparedSvgRepresentation {
|
||||||
|
return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
||||||
|
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||||
|
if !data.complete {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
return fetchPreparedSvgRepresentation(resource: resource, resourceData: data, representation: representation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
@ -755,3 +763,18 @@ private func fetchPreparedPatternWallpaperRepresentation(resource: MediaResource
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}) |> runOn(Queue.concurrentDefaultQueue())
|
}) |> runOn(Queue.concurrentDefaultQueue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func fetchPreparedSvgRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPreparedSvgRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||||
|
return Signal({ subscriber in
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||||
|
if let data = prepareSvgImage(data) {
|
||||||
|
let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))"
|
||||||
|
let url = URL(fileURLWithPath: path)
|
||||||
|
let _ = try? data.write(to: url)
|
||||||
|
subscriber.putNext(.temporaryPath(path))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EmptyDisposable
|
||||||
|
}) |> runOn(Queue.concurrentDefaultQueue())
|
||||||
|
}
|
||||||
|
@ -55,10 +55,12 @@ private final class PlayPauseIconNode: ManagedAnimationNode {
|
|||||||
|
|
||||||
final class PlayPauseIconComponent: Component {
|
final class PlayPauseIconComponent: Component {
|
||||||
let state: PlayPauseIconNodeState
|
let state: PlayPauseIconNodeState
|
||||||
|
let tintColor: UIColor?
|
||||||
let size: CGSize
|
let size: CGSize
|
||||||
|
|
||||||
init(state: PlayPauseIconNodeState, size: CGSize) {
|
init(state: PlayPauseIconNodeState, tintColor: UIColor?, size: CGSize) {
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.tintColor = tintColor
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +68,9 @@ final class PlayPauseIconComponent: Component {
|
|||||||
if lhs.state != rhs.state {
|
if lhs.state != rhs.state {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.tintColor != rhs.tintColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.size != rhs.size {
|
if lhs.size != rhs.size {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -95,6 +100,8 @@ final class PlayPauseIconComponent: Component {
|
|||||||
self.animationNode.enqueueState(component.state, animated: true)
|
self.animationNode.enqueueState(component.state, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.animationNode.customColor = component.tintColor
|
||||||
|
|
||||||
let animationSize = component.size
|
let animationSize = component.size
|
||||||
let size = CGSize(width: min(animationSize.width, availableSize.width), height: min(animationSize.height, availableSize.height))
|
let size = CGSize(width: min(animationSize.width, availableSize.width), height: min(animationSize.height, availableSize.height))
|
||||||
self.animationNode.view.frame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: floor((size.height - animationSize.height) / 2.0)), size: animationSize)
|
self.animationNode.view.frame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: floor((size.height - animationSize.height) / 2.0)), size: animationSize)
|
||||||
|
@ -15,11 +15,10 @@ import MultilineTextComponent
|
|||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
|
||||||
private func generateExpandBackground(size: CGSize) -> UIImage {
|
private func generateExpandBackground(size: CGSize, color: UIColor) -> UIImage {
|
||||||
return generateImage(size, rotatedContext: { size, context in
|
return generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
let color = UIColor.white
|
|
||||||
var locations: [CGFloat] = [0.0, 1.0]
|
var locations: [CGFloat] = [0.0, 1.0]
|
||||||
let colors: [CGColor] = [color.withAlphaComponent(0.0).cgColor, color.cgColor]
|
let colors: [CGColor] = [color.withAlphaComponent(0.0).cgColor, color.cgColor]
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
private var speechHolder: SpeechSynthesizerHolder?
|
private var speechHolder: SpeechSynthesizerHolder?
|
||||||
fileprivate var availableSpeakLanguages: Set<String>
|
fileprivate var availableSpeakLanguages: Set<String>
|
||||||
|
|
||||||
fileprivate var moreBackgroundImage: (CGSize, UIImage)?
|
fileprivate var moreBackgroundImage: (CGSize, UIImage, UIColor)?
|
||||||
|
|
||||||
init(context: AccountContext, fromLanguage: String?, text: String, toLanguage: String, expand: @escaping () -> Void) {
|
init(context: AccountContext, fromLanguage: String?, text: String, toLanguage: String, expand: @escaping () -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -115,6 +114,7 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
self.speechHolder?.stop()
|
||||||
self.translationDisposable.dispose()
|
self.translationDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,6 +337,11 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
if state.textExpanded {
|
if state.textExpanded {
|
||||||
if let fromLanguage = state.fromLanguage, state.availableSpeakLanguages.contains(fromLanguage) {
|
if let fromLanguage = state.fromLanguage, state.availableSpeakLanguages.contains(fromLanguage) {
|
||||||
|
var checkColor = theme.list.itemCheckColors.foregroundColor
|
||||||
|
if checkColor.rgb == theme.list.itemPrimaryTextColor.rgb {
|
||||||
|
checkColor = theme.list.plainBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
let originalSpeakButton = originalSpeakButton.update(
|
let originalSpeakButton = originalSpeakButton.update(
|
||||||
component: Button(
|
component: Button(
|
||||||
content: AnyComponent(ZStack([
|
content: AnyComponent(ZStack([
|
||||||
@ -346,6 +351,7 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
))),
|
))),
|
||||||
AnyComponentWithIdentity(id: "a", component: AnyComponent(PlayPauseIconComponent(
|
AnyComponentWithIdentity(id: "a", component: AnyComponent(PlayPauseIconComponent(
|
||||||
state: state.isSpeakingOriginalText ? .pause : .play,
|
state: state.isSpeakingOriginalText ? .pause : .play,
|
||||||
|
tintColor: checkColor,
|
||||||
size: CGSize(width: 18.0, height: 18.0)
|
size: CGSize(width: 18.0, height: 18.0)
|
||||||
))),
|
))),
|
||||||
])),
|
])),
|
||||||
@ -381,14 +387,15 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
let originalMoreBackgroundSize = CGSize(width: originalMoreButton.size.width + 50.0, height: originalMoreButton.size.height)
|
let originalMoreBackgroundSize = CGSize(width: originalMoreButton.size.width + 50.0, height: originalMoreButton.size.height)
|
||||||
let originalMoreBackgroundImage: UIImage
|
let originalMoreBackgroundImage: UIImage
|
||||||
if let (size, image) = state.moreBackgroundImage, size == originalMoreBackgroundSize {
|
let backgroundColor = theme.list.itemBlocksBackgroundColor
|
||||||
|
if let (size, image, color) = state.moreBackgroundImage, size == originalMoreBackgroundSize && color == backgroundColor {
|
||||||
originalMoreBackgroundImage = image
|
originalMoreBackgroundImage = image
|
||||||
} else {
|
} else {
|
||||||
originalMoreBackgroundImage = generateExpandBackground(size: originalMoreBackgroundSize)
|
originalMoreBackgroundImage = generateExpandBackground(size: originalMoreBackgroundSize, color: backgroundColor)
|
||||||
state.moreBackgroundImage = (originalMoreBackgroundSize, originalMoreBackgroundImage)
|
state.moreBackgroundImage = (originalMoreBackgroundSize, originalMoreBackgroundImage, backgroundColor)
|
||||||
}
|
}
|
||||||
let originalMoreBackground = originalMoreBackground.update(
|
let originalMoreBackground = originalMoreBackground.update(
|
||||||
component: Image(image: originalMoreBackgroundImage, tintColor: theme.list.itemBlocksBackgroundColor),
|
component: Image(image: originalMoreBackgroundImage, tintColor: backgroundColor),
|
||||||
availableSize: originalMoreBackgroundSize,
|
availableSize: originalMoreBackgroundSize,
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
@ -426,6 +433,7 @@ private final class TranslateScreenComponent: CombinedComponent {
|
|||||||
))),
|
))),
|
||||||
AnyComponentWithIdentity(id: "a", component: AnyComponent(PlayPauseIconComponent(
|
AnyComponentWithIdentity(id: "a", component: AnyComponent(PlayPauseIconComponent(
|
||||||
state: state.isSpeakingTranslatedText ? .pause : .play,
|
state: state.isSpeakingTranslatedText ? .pause : .play,
|
||||||
|
tintColor: theme.list.itemCheckColors.foregroundColor,
|
||||||
size: CGSize(width: 18.0, height: 18.0)
|
size: CGSize(width: 18.0, height: 18.0)
|
||||||
))),
|
))),
|
||||||
])),
|
])),
|
||||||
|
@ -531,7 +531,7 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete
|
|||||||
var image: UIImage?
|
var image: UIImage?
|
||||||
if let fullSizeData = fullSizeData {
|
if let fullSizeData = fullSizeData {
|
||||||
if mode == .screen {
|
if mode == .screen {
|
||||||
image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale))
|
image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0)
|
||||||
} else {
|
} else {
|
||||||
image = UIImage(data: fullSizeData)
|
image = UIImage(data: fullSizeData)
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ public struct WebAppParameters {
|
|||||||
let botName: String
|
let botName: String
|
||||||
let url: String?
|
let url: String?
|
||||||
let queryId: Int64?
|
let queryId: Int64?
|
||||||
|
let payload: String?
|
||||||
let buttonText: String?
|
let buttonText: String?
|
||||||
let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
||||||
let fromMenu: Bool
|
let fromMenu: Bool
|
||||||
@ -34,6 +35,7 @@ public struct WebAppParameters {
|
|||||||
botName: String,
|
botName: String,
|
||||||
url: String?,
|
url: String?,
|
||||||
queryId: Int64?,
|
queryId: Int64?,
|
||||||
|
payload: String?,
|
||||||
buttonText: String?,
|
buttonText: String?,
|
||||||
keepAliveSignal: Signal<Never, KeepWebViewError>?,
|
keepAliveSignal: Signal<Never, KeepWebViewError>?,
|
||||||
fromMenu: Bool
|
fromMenu: Bool
|
||||||
@ -43,6 +45,7 @@ public struct WebAppParameters {
|
|||||||
self.botName = botName
|
self.botName = botName
|
||||||
self.url = url
|
self.url = url
|
||||||
self.queryId = queryId
|
self.queryId = queryId
|
||||||
|
self.payload = payload
|
||||||
self.buttonText = buttonText
|
self.buttonText = buttonText
|
||||||
self.keepAliveSignal = keepAliveSignal
|
self.keepAliveSignal = keepAliveSignal
|
||||||
self.fromMenu = fromMenu
|
self.fromMenu = fromMenu
|
||||||
@ -75,7 +78,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
private weak var controller: WebAppController?
|
private weak var controller: WebAppController?
|
||||||
|
|
||||||
fileprivate var webView: WebAppWebView?
|
fileprivate var webView: WebAppWebView?
|
||||||
private var placeholderIcon: UIImage?
|
private var placeholderIcon: (UIImage, Bool)?
|
||||||
private var placeholderNode: ShimmerEffectNode?
|
private var placeholderNode: ShimmerEffectNode?
|
||||||
|
|
||||||
fileprivate let loadingProgressPromise = Promise<CGFloat?>(nil)
|
fileprivate let loadingProgressPromise = Promise<CGFloat?>(nil)
|
||||||
@ -124,22 +127,32 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var iconFile: TelegramMediaFile?
|
var imageFile: TelegramMediaFile?
|
||||||
if let file = bot.icons[.iOSStatic] {
|
var isPlaceholder = false
|
||||||
iconFile = file
|
if let file = bot.icons[.placeholder] {
|
||||||
|
imageFile = file
|
||||||
|
isPlaceholder = true
|
||||||
|
} else if let file = bot.icons[.iOSStatic] {
|
||||||
|
imageFile = file
|
||||||
} else if let file = bot.icons[.default] {
|
} else if let file = bot.icons[.default] {
|
||||||
iconFile = file
|
imageFile = file
|
||||||
}
|
}
|
||||||
if let iconFile = iconFile, let peer = PeerReference(bot.peer) {
|
if let imageFile = imageFile, let peer = PeerReference(bot.peer) {
|
||||||
let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: iconFile)).start()
|
let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: imageFile)).start()
|
||||||
strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: iconFile))
|
strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: imageFile), stickToTop: isPlaceholder)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let imageSize = CGSize(width: 75.0, height: 75.0)
|
let imageSize: CGSize
|
||||||
|
if isPlaceholder, let (layout, _) = strongSelf.validLayout {
|
||||||
|
let minSize = min(layout.size.width, layout.size.height)
|
||||||
|
imageSize = CGSize(width: minSize, height: minSize * 3.0)
|
||||||
|
} else {
|
||||||
|
imageSize = CGSize(width: 75.0, height: 75.0)
|
||||||
|
}
|
||||||
let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())
|
let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())
|
||||||
let drawingContext = transform(arguments)
|
let drawingContext = transform(arguments)
|
||||||
if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) {
|
if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) {
|
||||||
strongSelf.placeholderIcon = image
|
strongSelf.placeholderIcon = (image, isPlaceholder)
|
||||||
if let (layout, navigationBarHeight) = strongSelf.validLayout {
|
if let (layout, navigationBarHeight) = strongSelf.validLayout {
|
||||||
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
}
|
}
|
||||||
@ -168,7 +181,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = (context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: nil, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.fromMenu, replyToMessageId: controller.replyToMessageId)
|
let _ = (context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: controller.payload, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.fromMenu, replyToMessageId: controller.replyToMessageId)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -213,29 +226,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.webView?.sendEvent(name: "main_button_pressed", data: nil)
|
self.webView?.sendEvent(name: "main_button_pressed", data: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePlaceholder(grid: Bool, layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
private func updatePlaceholder(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
var shapes: [ShimmerEffect.ShimmerEffectNode.Shape] = []
|
var shapes: [ShimmerEffect.ShimmerEffectNode.Shape] = []
|
||||||
var placeholderSize: CGSize = CGSize()
|
var placeholderSize: CGSize = CGSize()
|
||||||
if grid {
|
|
||||||
let spacing: CGFloat = 8.0
|
|
||||||
let cols: Int = min(4, Int(floor(layout.size.width / 118.0)))
|
|
||||||
let itemSize: CGSize = CGSize(width: cols == 4 ? 111.0 : 118.0, height: 150.0)
|
|
||||||
let rows: Int = 4
|
|
||||||
|
|
||||||
let sideInset = floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - itemSize.width * CGFloat(cols) - spacing * CGFloat(cols - 1)) / 2.0)
|
if let (image, _) = self.placeholderIcon {
|
||||||
|
shapes = [.image(image: image, rect: CGRect(origin: CGPoint(), size: image.size))]
|
||||||
for row in 0 ..< rows {
|
placeholderSize = image.size
|
||||||
for col in 0 ..< cols {
|
|
||||||
shapes.append(.roundedRect(rect: CGRect(x: layout.safeInsets.left + sideInset + CGFloat(col) * (itemSize.width + spacing), y: navigationBarHeight + CGFloat(row) * (itemSize.height + spacing), width: itemSize.width, height: itemSize.height), cornerRadius: 10.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
placeholderSize = layout.size
|
|
||||||
} else {
|
|
||||||
if let icon = self.placeholderIcon {
|
|
||||||
shapes = [.image(image: icon, rect: CGRect(origin: CGPoint(), size: icon.size))]
|
|
||||||
placeholderSize = icon.size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let theme = self.presentationData.theme
|
let theme = self.presentationData.theme
|
||||||
@ -264,16 +261,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private var loadCount = 0
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
Queue.mainQueue().after(0.65, {
|
||||||
self.loadCount += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
||||||
self.loadCount -= 1
|
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1, {
|
|
||||||
if self.loadCount == 0, let webView = self.webView {
|
|
||||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear)
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear)
|
||||||
transition.updateAlpha(layer: webView.layer, alpha: 1.0)
|
transition.updateAlpha(layer: webView.layer, alpha: 1.0)
|
||||||
if let placeholderNode = self.placeholderNode {
|
if let placeholderNode = self.placeholderNode {
|
||||||
@ -282,12 +271,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
placeholderNode?.removeFromSupernode()
|
placeholderNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if let (layout, navigationBarHeight) = self.validLayout {
|
if let (layout, navigationBarHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
||||||
@ -308,7 +296,14 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
if let webView = self.webView {
|
if let webView = self.webView {
|
||||||
let frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom)))
|
let frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom)))
|
||||||
let viewportFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom - layout.additionalInsets.bottom)))
|
let viewportFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom - layout.additionalInsets.bottom)))
|
||||||
|
|
||||||
|
if previousLayout != nil && (previousLayout?.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0, transition.isAnimated {
|
||||||
|
Queue.mainQueue().after(0.4, {
|
||||||
transition.updateFrame(view: webView, frame: frame)
|
transition.updateFrame(view: webView, frame: frame)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
transition.updateFrame(view: webView, frame: frame)
|
||||||
|
}
|
||||||
|
|
||||||
webView.updateFrame(frame: viewportFrame, transition: transition)
|
webView.updateFrame(frame: viewportFrame, transition: transition)
|
||||||
}
|
}
|
||||||
@ -321,9 +316,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
height = layout.size.height - layout.intrinsicInsets.bottom
|
height = layout.size.height - layout.intrinsicInsets.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
let grid = self.controller?.botId.id._internalGetInt64Value() == 2200339955
|
let placeholderSize = self.updatePlaceholder(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||||
let placeholderSize = self.updatePlaceholder(grid: grid, layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
let placeholderY: CGFloat
|
||||||
let placeholderY: CGFloat = grid ? 0.0 : floorToScreenPixels((height - placeholderSize.height) / 2.0)
|
if let (_, isPlaceholder) = self.placeholderIcon, isPlaceholder {
|
||||||
|
placeholderY = navigationBarHeight
|
||||||
|
} else {
|
||||||
|
placeholderY = floorToScreenPixels((height - placeholderSize.height) / 2.0)
|
||||||
|
}
|
||||||
let placeholderFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - placeholderSize.width) / 2.0), y: placeholderY), size: placeholderSize)
|
let placeholderFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - placeholderSize.width) / 2.0), y: placeholderY), size: placeholderSize)
|
||||||
transition.updateFrame(node: placeholderNode, frame: placeholderFrame)
|
transition.updateFrame(node: placeholderNode, frame: placeholderFrame)
|
||||||
placeholderNode.updateAbsoluteRect(placeholderFrame, within: layout.size)
|
placeholderNode.updateAbsoluteRect(placeholderFrame, within: layout.size)
|
||||||
@ -372,7 +371,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
let textColor = textColorString.flatMap({ UIColor(hexString: $0) }) ?? self.presentationData.theme.list.itemCheckColors.foregroundColor
|
let textColor = textColorString.flatMap({ UIColor(hexString: $0) }) ?? self.presentationData.theme.list.itemCheckColors.foregroundColor
|
||||||
|
|
||||||
let isLoading = json["is_progress_visible"] as? Bool
|
let isLoading = json["is_progress_visible"] as? Bool
|
||||||
let state = AttachmentMainButtonState(text: text, backgroundColor: backgroundColor, textColor: textColor, isVisible: isVisible, isLoading: isLoading ?? false)
|
let isEnabled = json["is_active"] as? Bool
|
||||||
|
let state = AttachmentMainButtonState(text: text, backgroundColor: backgroundColor, textColor: textColor, isVisible: isVisible, isLoading: isLoading ?? false, isEnabled: isEnabled ?? true)
|
||||||
self.mainButtonStatePromise.set(.single(state))
|
self.mainButtonStatePromise.set(.single(state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,6 +448,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
private let botId: PeerId
|
private let botId: PeerId
|
||||||
private let url: String?
|
private let url: String?
|
||||||
private let queryId: Int64?
|
private let queryId: Int64?
|
||||||
|
private let payload: String?
|
||||||
private let buttonText: String?
|
private let buttonText: String?
|
||||||
private let fromMenu: Bool
|
private let fromMenu: Bool
|
||||||
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
||||||
@ -467,6 +468,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.botId = params.botId
|
self.botId = params.botId
|
||||||
self.url = params.url
|
self.url = params.url
|
||||||
self.queryId = params.queryId
|
self.queryId = params.queryId
|
||||||
|
self.payload = params.payload
|
||||||
self.buttonText = params.buttonText
|
self.buttonText = params.buttonText
|
||||||
self.fromMenu = params.fromMenu
|
self.fromMenu = params.fromMenu
|
||||||
self.keepAliveSignal = params.keepAliveSignal
|
self.keepAliveSignal = params.keepAliveSignal
|
||||||
@ -666,7 +668,7 @@ private final class WebAppContextReferenceContentSource: ContextReferenceContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, openUrl: @escaping (String) -> Void, getInputContainerNode: @escaping () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil }, completion: @escaping () -> Void = {}, dismissed: @escaping () -> Void = {}) -> ViewController {
|
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, openUrl: @escaping (String) -> Void, getInputContainerNode: @escaping () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil }, completion: @escaping () -> Void = {}, willDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {}) -> ViewController {
|
||||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.fromMenu)
|
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.fromMenu)
|
||||||
controller.getInputContainerNode = getInputContainerNode
|
controller.getInputContainerNode = getInputContainerNode
|
||||||
controller.requestController = { _, present in
|
controller.requestController = { _, present in
|
||||||
@ -675,6 +677,7 @@ public func standaloneWebAppController(context: AccountContext, updatedPresentat
|
|||||||
webAppController.completion = completion
|
webAppController.completion = completion
|
||||||
present(webAppController, webAppController.mediaPickerContext)
|
present(webAppController, webAppController.mediaPickerContext)
|
||||||
}
|
}
|
||||||
controller.dismissed = dismissed
|
controller.willDismiss = willDismiss
|
||||||
|
controller.didDismiss = didDismiss
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user