mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '76aacfc03eca5bcd74ba31aca25b5b4ff3c1f86c'
# Conflicts: # Telegram/Telegram-iOS/en.lproj/Localizable.strings
This commit is contained in:
commit
fbba4f9f94
@ -9084,3 +9084,9 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Conversation.SendMessageErrorTooFastTitle" = "Not so fast";
|
"Conversation.SendMessageErrorTooFastTitle" = "Not so fast";
|
||||||
"Conversation.SendMessageErrorTooFast" = "You are sending messages too fast. Please wait a bit.";
|
"Conversation.SendMessageErrorTooFast" = "You are sending messages too fast. Please wait a bit.";
|
||||||
|
|
||||||
|
"PeerInfo.CancelSelectionAlertText" = "Cancel selection?";
|
||||||
|
"PeerInfo.CancelSelectionAlertYes" = "Yes";
|
||||||
|
"PeerInfo.CancelSelectionAlertNo" = "No";
|
||||||
|
|
||||||
|
"StickerPacksSettings.SuggestAnimatedEmojiInfo" = "Each time you enter an emoji you can replace it with an animated emoji.";
|
||||||
|
@ -115,7 +115,7 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
|
|||||||
|
|
||||||
sectionItems.append(BotPaymentHeaderItemNode(text: strings.Checkout_NewCard_CardholderNameTitle))
|
sectionItems.append(BotPaymentHeaderItemNode(text: strings.Checkout_NewCard_CardholderNameTitle))
|
||||||
|
|
||||||
let cardholderItem = BotPaymentFieldItemNode(title: "", placeholder: strings.Checkout_NewCard_CardholderNamePlaceholder, contentType: .name)
|
let cardholderItem = BotPaymentFieldItemNode(title: "", placeholder: strings.Checkout_NewCard_CardholderNamePlaceholder, contentType: .asciiName)
|
||||||
self.cardholderItem = cardholderItem
|
self.cardholderItem = cardholderItem
|
||||||
sectionItems.append(cardholderItem)
|
sectionItems.append(cardholderItem)
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ private let titleFont = Font.regular(17.0)
|
|||||||
enum BotPaymentFieldContentType {
|
enum BotPaymentFieldContentType {
|
||||||
case generic
|
case generic
|
||||||
case name
|
case name
|
||||||
|
case asciiName
|
||||||
case phoneNumber
|
case phoneNumber
|
||||||
case email
|
case email
|
||||||
case address
|
case address
|
||||||
@ -51,6 +52,9 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate {
|
|||||||
case .generic:
|
case .generic:
|
||||||
break
|
break
|
||||||
case .name:
|
case .name:
|
||||||
|
self.textField.textField.autocorrectionType = .no
|
||||||
|
self.textField.textField.keyboardType = .default
|
||||||
|
case .asciiName:
|
||||||
self.textField.textField.autocorrectionType = .no
|
self.textField.textField.autocorrectionType = .no
|
||||||
self.textField.textField.keyboardType = .asciiCapable
|
self.textField.textField.keyboardType = .asciiCapable
|
||||||
case .address:
|
case .address:
|
||||||
|
@ -2561,10 +2561,6 @@ public final class ChatListNode: ListView {
|
|||||||
return strongSelf.isSelectionGestureEnabled
|
return strongSelf.isSelectionGestureEnabled
|
||||||
}
|
}
|
||||||
self.view.addGestureRecognizer(selectionRecognizer)
|
self.view.addGestureRecognizer(selectionRecognizer)
|
||||||
|
|
||||||
// if case .forum = location {
|
|
||||||
// self.isSelectionGestureEnabled = false
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -139,6 +139,11 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) {
|
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) {
|
||||||
self.currentLayout = (layout, navigationHeight)
|
self.currentLayout = (layout, navigationHeight)
|
||||||
|
|
||||||
|
var theme = self.theme ?? self.presentationData.theme
|
||||||
|
if theme.list.blocksBackgroundColor.rgb == theme.list.plainBackgroundColor.rgb {
|
||||||
|
theme = theme.withModalBlocksBackground()
|
||||||
|
}
|
||||||
|
|
||||||
let environment = ViewControllerComponentContainer.Environment(
|
let environment = ViewControllerComponentContainer.Environment(
|
||||||
statusBarHeight: layout.statusBarHeight ?? 0.0,
|
statusBarHeight: layout.statusBarHeight ?? 0.0,
|
||||||
navigationHeight: navigationHeight,
|
navigationHeight: navigationHeight,
|
||||||
@ -147,7 +152,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
metrics: layout.metrics,
|
metrics: layout.metrics,
|
||||||
deviceMetrics: layout.deviceMetrics,
|
deviceMetrics: layout.deviceMetrics,
|
||||||
isVisible: self.currentIsVisible,
|
isVisible: self.currentIsVisible,
|
||||||
theme: self.theme ?? self.presentationData.theme,
|
theme: theme,
|
||||||
strings: self.presentationData.strings,
|
strings: self.presentationData.strings,
|
||||||
dateTimeFormat: self.presentationData.dateTimeFormat,
|
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||||
controller: { [weak self] in
|
controller: { [weak self] in
|
||||||
|
@ -359,30 +359,36 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.itemsDimensionsUpdatedDisposable?.dispose()
|
self.itemsDimensionsUpdatedDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var selectionGesture: MediaPickerGridSelectionGesture?
|
private var selectionGesture: MediaPickerGridSelectionGesture<TGMediaSelectableItem>?
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||||
|
|
||||||
self.selectionGesture = MediaPickerGridSelectionGesture(target: nil, action: nil, gridNode: self.gridNode)
|
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
||||||
self.selectionGesture?.delegate = self
|
selectionGesture.delegate = self
|
||||||
self.selectionGesture?.began = { [weak self] in
|
selectionGesture.began = { [weak self] in
|
||||||
self?.controller?.cancelPanGesture()
|
self?.controller?.cancelPanGesture()
|
||||||
}
|
}
|
||||||
self.selectionGesture?.itemAt = { [weak self] point in
|
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
|
||||||
|
self?.gridNode.scrollView.isScrollEnabled = isEnabled
|
||||||
|
}
|
||||||
|
selectionGesture.itemAt = { [weak self] point in
|
||||||
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
|
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
|
||||||
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
|
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.selectionGesture?.updateSelection = { [weak self] asset, selected in
|
selectionGesture.updateSelection = { [weak self] asset, selected in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
|
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectionGesture.sideInset = 44.0
|
||||||
|
self.gridNode.view.addGestureRecognizer(selectionGesture)
|
||||||
|
self.selectionGesture = selectionGesture
|
||||||
|
|
||||||
if let controller = self.controller, case let .assets(collection) = controller.subject, collection != nil {
|
if let controller = self.controller, case let .assets(collection) = controller.subject, collection != nil {
|
||||||
self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in
|
self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in
|
||||||
@ -1546,7 +1552,6 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
|
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
|
||||||
|
|
||||||
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
|
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
|
||||||
self?.dismissAllTooltips()
|
self?.dismissAllTooltips()
|
||||||
completion()
|
completion()
|
||||||
@ -1794,37 +1799,31 @@ private final class MediaPickerContextReferenceContentSource: ContextReferenceCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
public class MediaPickerGridSelectionGesture<T> : UIPanGestureRecognizer {
|
||||||
var itemAt: (CGPoint) -> (TGMediaSelectableItem, Bool)? = { _ in return nil }
|
public var itemAt: (CGPoint) -> (T, Bool)? = { _ in return nil }
|
||||||
var updateSelection: (TGMediaSelectableItem, Bool) -> Void = { _, _ in}
|
public var updateSelection: (T, Bool) -> Void = { _, _ in}
|
||||||
var began: () -> Void = {}
|
public var updateIsScrollEnabled: (Bool) -> Void = { _ in}
|
||||||
|
public var began: () -> Void = {}
|
||||||
private weak var gridNode: GridNode?
|
|
||||||
|
|
||||||
private var processing = false
|
private var processing = false
|
||||||
private var selecting = false
|
private var selecting = false
|
||||||
|
|
||||||
private var initialLocation: CGPoint?
|
private var initialLocation: CGPoint?
|
||||||
|
|
||||||
var sideInset: CGFloat = 0.0
|
public var sideInset: CGFloat = 0.0
|
||||||
|
|
||||||
init(target: Any?, action: Selector?, gridNode: GridNode) {
|
public init() {
|
||||||
self.gridNode = gridNode
|
super.init(target: nil, action: nil)
|
||||||
|
|
||||||
super.init(target: target, action: action)
|
|
||||||
|
|
||||||
gridNode.view.addGestureRecognizer(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||||
super.touchesBegan(touches, with: event)
|
super.touchesBegan(touches, with: event)
|
||||||
|
|
||||||
guard let touch = touches.first, self.numberOfTouches == 1, let gridNode = self.gridNode else {
|
guard let touch = touches.first, self.numberOfTouches == 1 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let location = touch.location(in: gridNode.view)
|
let location = touch.location(in: self.view)
|
||||||
|
|
||||||
if location.x > self.sideInset {
|
if location.x > self.sideInset {
|
||||||
self.initialLocation = location
|
self.initialLocation = location
|
||||||
} else {
|
} else {
|
||||||
@ -1832,15 +1831,15 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||||
super.touchesMoved(touches, with: event)
|
super.touchesMoved(touches, with: event)
|
||||||
|
|
||||||
guard let touch = touches.first, let gridNode = self.gridNode, let initialLocation = self.initialLocation else {
|
guard let touch = touches.first, let initialLocation = self.initialLocation else {
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let location = touch.location(in: gridNode.view)
|
let location = touch.location(in: self.view)
|
||||||
let translation = CGPoint(x: location.x - initialLocation.x, y: location.y - initialLocation.y)
|
let translation = CGPoint(x: location.x - initialLocation.x, y: location.y - initialLocation.y)
|
||||||
|
|
||||||
var additionalLocation: CGPoint?
|
var additionalLocation: CGPoint?
|
||||||
@ -1849,7 +1848,7 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
|||||||
self.state = .failed
|
self.state = .failed
|
||||||
} else if abs(translation.x) > 8.0 {
|
} else if abs(translation.x) > 8.0 {
|
||||||
self.processing = true
|
self.processing = true
|
||||||
self.gridNode?.scrollView.isScrollEnabled = false
|
self.updateIsScrollEnabled(false)
|
||||||
self.began()
|
self.began()
|
||||||
|
|
||||||
if let (_, selected) = self.itemAt(location) {
|
if let (_, selected) = self.itemAt(location) {
|
||||||
@ -1873,25 +1872,25 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||||
super.touchesEnded(touches, with: event)
|
super.touchesEnded(touches, with: event)
|
||||||
|
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
self.reset()
|
self.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
|
public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||||
super.touchesCancelled(touches, with: event)
|
super.touchesCancelled(touches, with: event)
|
||||||
|
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
self.reset()
|
self.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func reset() {
|
public override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
|
|
||||||
self.processing = false
|
self.processing = false
|
||||||
self.initialLocation = nil
|
self.initialLocation = nil
|
||||||
self.gridNode?.scrollView.isScrollEnabled = true
|
self.updateIsScrollEnabled(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,7 +593,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
|||||||
strongSelf.wallpaperBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in
|
strongSelf.wallpaperBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
strongSelf.wallpaperBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
strongSelf.wallpaperBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
for (_, backgroundNode) in strongSelf.backgroundNodes {
|
for (_, backgroundNode) in strongSelf.backgroundNodes {
|
||||||
backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
|
backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
|
||||||
@ -641,7 +641,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.wallpaperBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
self.wallpaperBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
for (_, backgroundNode) in self.backgroundNodes {
|
for (_, backgroundNode) in self.backgroundNodes {
|
||||||
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
|
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
|
||||||
|
@ -167,7 +167,6 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
let overscroll = Child(Rectangle.self)
|
let overscroll = Child(Rectangle.self)
|
||||||
let fade = Child(RoundedRectangle.self)
|
|
||||||
let text = Child(MultilineTextComponent.self)
|
let text = Child(MultilineTextComponent.self)
|
||||||
let optionsSection = Child(SectionGroupComponent.self)
|
let optionsSection = Child(SectionGroupComponent.self)
|
||||||
let perksSection = Child(SectionGroupComponent.self)
|
let perksSection = Child(SectionGroupComponent.self)
|
||||||
@ -196,22 +195,6 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
|||||||
.position(CGPoint(x: overscroll.size.width / 2.0, y: -overscroll.size.height / 2.0))
|
.position(CGPoint(x: overscroll.size.width / 2.0, y: -overscroll.size.height / 2.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
let fade = fade.update(
|
|
||||||
component: RoundedRectangle(
|
|
||||||
colors: [
|
|
||||||
theme.list.plainBackgroundColor,
|
|
||||||
theme.list.blocksBackgroundColor
|
|
||||||
],
|
|
||||||
cornerRadius: 0.0,
|
|
||||||
gradientDirection: .vertical
|
|
||||||
),
|
|
||||||
availableSize: CGSize(width: availableWidth, height: 300),
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
context.add(fade
|
|
||||||
.position(CGPoint(x: fade.size.width / 2.0, y: fade.size.height / 2.0))
|
|
||||||
)
|
|
||||||
|
|
||||||
size.height += 183.0 + 10.0 + environment.navigationHeight - 56.0
|
size.height += 183.0 + 10.0 + environment.navigationHeight - 56.0
|
||||||
|
|
||||||
let textColor = theme.list.itemPrimaryTextColor
|
let textColor = theme.list.itemPrimaryTextColor
|
||||||
|
@ -45,13 +45,14 @@ public class ItemListReactionItem: ListViewItem, ItemListItem {
|
|||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
async {
|
async {
|
||||||
|
Queue.mainQueue().async {
|
||||||
let node = ItemListReactionItemNode()
|
let node = ItemListReactionItemNode()
|
||||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
|
||||||
node.contentSize = layout.contentSize
|
node.contentSize = layout.contentSize
|
||||||
node.insets = layout.insets
|
node.insets = layout.insets
|
||||||
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
completion(node, {
|
completion(node, {
|
||||||
return (nil, { _ in apply() })
|
return (nil, { _ in apply() })
|
||||||
})
|
})
|
||||||
|
@ -92,6 +92,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
case packOrder(PresentationTheme, String, Bool)
|
case packOrder(PresentationTheme, String, Bool)
|
||||||
case packOrderInfo(PresentationTheme, String)
|
case packOrderInfo(PresentationTheme, String)
|
||||||
case suggestAnimatedEmoji(String, Bool)
|
case suggestAnimatedEmoji(String, Bool)
|
||||||
|
case suggestAnimatedEmojiInfo(PresentationTheme, String)
|
||||||
case packsTitle(PresentationTheme, String)
|
case packsTitle(PresentationTheme, String)
|
||||||
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?)
|
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?)
|
||||||
case packsInfo(PresentationTheme, String)
|
case packsInfo(PresentationTheme, String)
|
||||||
@ -100,7 +101,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
switch self {
|
switch self {
|
||||||
case .trending, .masks, .emoji, .quickReaction, .archived:
|
case .trending, .masks, .emoji, .quickReaction, .archived:
|
||||||
return InstalledStickerPacksSection.categories.rawValue
|
return InstalledStickerPacksSection.categories.rawValue
|
||||||
case .suggestOptions, .largeEmoji, .suggestAnimatedEmoji, .packOrder, .packOrderInfo:
|
case .suggestOptions, .largeEmoji, .suggestAnimatedEmoji, .suggestAnimatedEmojiInfo, .packOrder, .packOrderInfo:
|
||||||
return InstalledStickerPacksSection.settings.rawValue
|
return InstalledStickerPacksSection.settings.rawValue
|
||||||
case .packsTitle, .pack, .packsInfo:
|
case .packsTitle, .pack, .packsInfo:
|
||||||
return InstalledStickerPacksSection.stickers.rawValue
|
return InstalledStickerPacksSection.stickers.rawValue
|
||||||
@ -125,16 +126,18 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
return .index(6)
|
return .index(6)
|
||||||
case .suggestAnimatedEmoji:
|
case .suggestAnimatedEmoji:
|
||||||
return .index(7)
|
return .index(7)
|
||||||
case .packOrder:
|
case .suggestAnimatedEmojiInfo:
|
||||||
return .index(8)
|
return .index(8)
|
||||||
case .packOrderInfo:
|
case .packOrder:
|
||||||
return .index(9)
|
return .index(9)
|
||||||
case .packsTitle:
|
case .packOrderInfo:
|
||||||
return .index(10)
|
return .index(10)
|
||||||
|
case .packsTitle:
|
||||||
|
return .index(11)
|
||||||
case let .pack(_, _, _, info, _, _, _, _, _, _):
|
case let .pack(_, _, _, info, _, _, _, _, _, _):
|
||||||
return .pack(info.id)
|
return .pack(info.id)
|
||||||
case .packsInfo:
|
case .packsInfo:
|
||||||
return .index(11)
|
return .index(12)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +203,12 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .suggestAnimatedEmojiInfo(lhsTheme, lhsText):
|
||||||
|
if case let .suggestAnimatedEmojiInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case let .packsTitle(lhsTheme, lhsText):
|
case let .packsTitle(lhsTheme, lhsText):
|
||||||
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
@ -323,9 +332,16 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
case .suggestAnimatedEmojiInfo:
|
||||||
|
switch rhs {
|
||||||
|
case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .suggestAnimatedEmojiInfo:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
case .packsTitle:
|
case .packsTitle:
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .packsTitle:
|
case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .suggestAnimatedEmojiInfo, .packsTitle:
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
@ -390,6 +406,8 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
arguments.toggleSuggestAnimatedEmoji(value)
|
arguments.toggleSuggestAnimatedEmoji(value)
|
||||||
})
|
})
|
||||||
|
case let .suggestAnimatedEmojiInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .packsTitle(_, text):
|
case let .packsTitle(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing, selected):
|
case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing, selected):
|
||||||
@ -531,6 +549,7 @@ private func installedStickerPacksControllerEntries(context: AccountContext, pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
entries.append(.suggestAnimatedEmoji(presentationData.strings.StickerPacksSettings_SuggestAnimatedEmoji, stickerSettings.suggestAnimatedEmoji))
|
entries.append(.suggestAnimatedEmoji(presentationData.strings.StickerPacksSettings_SuggestAnimatedEmoji, stickerSettings.suggestAnimatedEmoji))
|
||||||
|
entries.append(.suggestAnimatedEmojiInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_SuggestAnimatedEmojiInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView {
|
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView {
|
||||||
|
@ -711,7 +711,6 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.layoutParams = params
|
strongSelf.layoutParams = params
|
||||||
|
|
||||||
strongSelf.listNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
|
||||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
@ -828,10 +827,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self.snapshotView = snapshotView
|
self.snapshotView = snapshotView
|
||||||
}
|
}
|
||||||
|
|
||||||
self.listNode.forEachVisibleItemNode { node in
|
self.listNode.enumerateItemNodes { node in
|
||||||
if let node = node as? ThemeCarouselThemeItemIconNode {
|
if let node = node as? ThemeCarouselThemeItemIconNode {
|
||||||
node.prepareCrossfadeTransition()
|
node.prepareCrossfadeTransition()
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,13 +846,14 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self.snapshotView = nil
|
self.snapshotView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.listNode.forEachVisibleItemNode { node in
|
self.listNode.enumerateItemNodes { node in
|
||||||
if let node = node as? ThemeCarouselThemeItemIconNode {
|
if let node = node as? ThemeCarouselThemeItemIconNode {
|
||||||
if let snapshotView = node.snapshotView {
|
if let snapshotView = node.snapshotView {
|
||||||
views.append(snapshotView)
|
views.append(snapshotView)
|
||||||
node.snapshotView = nil
|
node.snapshotView = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.3, animations: {
|
UIView.animate(withDuration: 0.3, animations: {
|
||||||
|
@ -1292,9 +1292,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
|
|||||||
|
|
||||||
public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil, sideInset: CGFloat = 0.0) {
|
public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil, sideInset: CGFloat = 0.0) {
|
||||||
if let view = view {
|
if let view = view {
|
||||||
if var leftOffset = leftOffset {
|
if let leftOffset = leftOffset {
|
||||||
leftOffset += UIScreenPixel
|
|
||||||
|
|
||||||
if let view = view.snapshotView(afterScreenUpdates: false) {
|
if let view = view.snapshotView(afterScreenUpdates: false) {
|
||||||
let clipView = UIView()
|
let clipView = UIView()
|
||||||
clipView.clipsToBounds = true
|
clipView.clipsToBounds = true
|
||||||
@ -1306,13 +1304,13 @@ public final class ThemeSettingsCrossfadeController: ViewController {
|
|||||||
if let topOffset = topOffset, let bottomOffset = bottomOffset {
|
if let topOffset = topOffset, let bottomOffset = bottomOffset {
|
||||||
var frame = view.frame
|
var frame = view.frame
|
||||||
frame.origin.y = topOffset
|
frame.origin.y = topOffset
|
||||||
frame.size.width = leftOffset
|
frame.size.width = leftOffset + sideInset
|
||||||
frame.size.height = bottomOffset - topOffset
|
frame.size.height = bottomOffset - topOffset
|
||||||
clipView.frame = frame
|
clipView.frame = frame
|
||||||
|
|
||||||
frame = view.frame
|
frame = view.frame
|
||||||
frame.origin.y = -topOffset
|
frame.origin.y = -topOffset
|
||||||
frame.size.width = leftOffset
|
frame.size.width = leftOffset + sideInset
|
||||||
frame.size.height = bottomOffset
|
frame.size.height = bottomOffset
|
||||||
view.frame = frame
|
view.frame = frame
|
||||||
}
|
}
|
||||||
@ -1322,7 +1320,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sideInset > 0.0 {
|
if sideInset > 0.0 {
|
||||||
if let view = view.snapshotView(afterScreenUpdates: false) {
|
if let view = view.snapshotView(afterScreenUpdates: false), leftOffset == nil {
|
||||||
let clipView = UIView()
|
let clipView = UIView()
|
||||||
clipView.clipsToBounds = true
|
clipView.clipsToBounds = true
|
||||||
clipView.addSubview(view)
|
clipView.addSubview(view)
|
||||||
|
@ -1345,6 +1345,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isScrollEnabled: Bool = true {
|
||||||
|
didSet {
|
||||||
|
self.currentViewport?.scrollView.isScrollEnabled = self.isScrollEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(theme: PresentationTheme, initialZoomLevel: ZoomLevel? = nil) {
|
public init(theme: PresentationTheme, initialZoomLevel: ZoomLevel? = nil) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.initialZoomLevel = initialZoomLevel
|
self.initialZoomLevel = initialZoomLevel
|
||||||
|
@ -10,7 +10,7 @@ func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: Str
|
|||||||
return account.postbox.loadedPeerWithId(botPeerId)
|
return account.postbox.loadedPeerWithId(botPeerId)
|
||||||
|> mapToSignal { botPeer -> Signal<Void, NoError> in
|
|> mapToSignal { botPeer -> Signal<Void, NoError> in
|
||||||
if let inputUser = apiInputUser(botPeer) {
|
if let inputUser = apiInputUser(botPeer) {
|
||||||
let r = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload))
|
return account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload))
|
||||||
|> mapToSignal { result -> Signal<Void, MTRpcError> in
|
|> mapToSignal { result -> Signal<Void, MTRpcError> in
|
||||||
account.stateManager.addUpdates(result)
|
account.stateManager.addUpdates(result)
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -18,7 +18,6 @@ func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: Str
|
|||||||
|> `catch` { _ -> Signal<Void, MTRpcError> in
|
|> `catch` { _ -> Signal<Void, MTRpcError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return r
|
|
||||||
|> retryRequest
|
|> retryRequest
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
|
@ -4104,7 +4104,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
|
return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, isInline: false, isSimple: false)
|
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, fromAttachMenu: false, isInline: false, isSimple: false)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, 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
|
||||||
@ -4159,7 +4159,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: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, isInline: isInline, isSimple: true)
|
let params = WebAppParameters(peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, fromAttachMenu: false, isInline: isInline, isSimple: true)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url, concealed: true, forceExternal: true)
|
self?.openUrl(url, concealed: true, forceExternal: true)
|
||||||
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
|
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
|
||||||
@ -4199,7 +4199,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, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false, isInline: false, isSimple: 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, fromAttachMenu: false, isInline: false, isSimple: false)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, 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
|
||||||
@ -12727,7 +12727,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: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false)
|
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, fromAttachMenu: false, isInline: false, isSimple: false)
|
||||||
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, 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
|
||||||
@ -13247,10 +13247,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
case let .app(bot, botName, _):
|
case let .app(bot, botName, _):
|
||||||
var payload: String?
|
var payload: String?
|
||||||
|
var fromAttachMenu = true
|
||||||
if case let .bot(_, botPayload, _) = subject {
|
if case let .bot(_, botPayload, _) = subject {
|
||||||
payload = botPayload
|
payload = botPayload
|
||||||
|
fromAttachMenu = false
|
||||||
}
|
}
|
||||||
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false)
|
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, fromAttachMenu: fromAttachMenu, isInline: false, isSimple: 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, threadId: strongSelf.chatLocation.threadId)
|
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId, threadId: strongSelf.chatLocation.threadId)
|
||||||
controller.openUrl = { [weak self] url in
|
controller.openUrl = { [weak self] url in
|
||||||
|
@ -1597,8 +1597,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance
|
let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance
|
||||||
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() {
|
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance {
|
||||||
if textInputNode.isCurrentlyEmoji() {
|
if textInputNode.isFirstResponder() && textInputNode.isCurrentlyEmoji() {
|
||||||
textInputNode.initialPrimaryLanguage = "emoji"
|
textInputNode.initialPrimaryLanguage = "emoji"
|
||||||
textInputNode.resetInitialPrimaryLanguage()
|
textInputNode.resetInitialPrimaryLanguage()
|
||||||
}
|
}
|
||||||
@ -2268,7 +2268,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
|
|
||||||
let _ = placeholderApply()
|
let _ = placeholderApply()
|
||||||
|
|
||||||
transition.updateFrame(node: contextPlaceholderNode, frame: CGRect(origin: CGPoint(x: hideOffset.x + leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size))
|
let placeholderTransition: ContainedViewLayoutTransition
|
||||||
|
if placeholderSize.size.width == contextPlaceholderNode.frame.width {
|
||||||
|
placeholderTransition = transition
|
||||||
|
} else {
|
||||||
|
placeholderTransition = .immediate
|
||||||
|
}
|
||||||
|
placeholderTransition.updateFrame(node: contextPlaceholderNode, frame: CGRect(origin: CGPoint(x: hideOffset.x + leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size))
|
||||||
contextPlaceholderNode.alpha = audioRecordingItemsAlpha
|
contextPlaceholderNode.alpha = audioRecordingItemsAlpha
|
||||||
} else if let contextPlaceholderNode = self.contextPlaceholderNode {
|
} else if let contextPlaceholderNode = self.contextPlaceholderNode {
|
||||||
self.contextPlaceholderNode = nil
|
self.contextPlaceholderNode = nil
|
||||||
|
@ -210,7 +210,7 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 130.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 130.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (commandLayout, commandApply) = makeCommandLayout(TextNodeLayoutArguments(attributedString: commandString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: 120.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (commandLayout, commandApply) = makeCommandLayout(TextNodeLayoutArguments(attributedString: commandString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - rightInset - textLayout.size.width - 16.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(CommandMenuChatInputPanelItemNode.itemHeight, textLayout.size.height + 14.0)), insets: UIEdgeInsets())
|
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(CommandMenuChatInputPanelItemNode.itemHeight, textLayout.size.height + 14.0)), insets: UIEdgeInsets())
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import CheckNode
|
|||||||
import AppBundle
|
import AppBundle
|
||||||
import ChatControllerInteraction
|
import ChatControllerInteraction
|
||||||
import InvisibleInkDustNode
|
import InvisibleInkDustNode
|
||||||
|
import MediaPickerUI
|
||||||
|
|
||||||
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -1631,7 +1632,7 @@ private func tagMaskForType(_ type: PeerInfoVisualMediaPaneNode.ContentType) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
||||||
enum ContentType {
|
enum ContentType {
|
||||||
case photoOrVideo
|
case photoOrVideo
|
||||||
case photo
|
case photo
|
||||||
@ -2445,6 +2446,27 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.itemGrid.addToTransitionSurface(view: view)
|
self.itemGrid.addToTransitionSurface(view: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var gridSelectionGesture: MediaPickerGridSelectionGesture<EngineMessage.Id>?
|
||||||
|
private var listSelectionGesture: MediaPickerGridSelectionGesture<EngineMessage.Id>?
|
||||||
|
|
||||||
|
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
let location = gestureRecognizer.location(in: gestureRecognizer.view)
|
||||||
|
if location.x < 44.0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if gestureRecognizer.state != .failed, let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||||
|
otherGestureRecognizer.isEnabled = false
|
||||||
|
otherGestureRecognizer.isEnabled = true
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateSelectedMessages(animated: Bool) {
|
func updateSelectedMessages(animated: Bool) {
|
||||||
switch self.contentType {
|
switch self.contentType {
|
||||||
case .files, .music, .voiceAndVideoMessages:
|
case .files, .music, .voiceAndVideoMessages:
|
||||||
@ -2473,7 +2495,36 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.chatControllerInteraction.selectionState?.selectedIds.contains(item.message.id), animated: animated)
|
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.chatControllerInteraction.selectionState?.selectedIds.contains(item.message.id), animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.itemGrid.pinchEnabled = self.chatControllerInteraction.selectionState == nil
|
let isSelecting = self.chatControllerInteraction.selectionState != nil
|
||||||
|
self.itemGrid.pinchEnabled = !isSelecting
|
||||||
|
|
||||||
|
if isSelecting {
|
||||||
|
if self.gridSelectionGesture == nil {
|
||||||
|
let selectionGesture = MediaPickerGridSelectionGesture<EngineMessage.Id>()
|
||||||
|
selectionGesture.delegate = self
|
||||||
|
selectionGesture.sideInset = 44.0
|
||||||
|
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
|
||||||
|
self?.itemGrid.isScrollEnabled = isEnabled
|
||||||
|
}
|
||||||
|
selectionGesture.itemAt = { [weak self] point in
|
||||||
|
if let strongSelf = self, let itemLayer = strongSelf.itemGrid.item(at: point)?.layer as? ItemLayer, let messageId = itemLayer.item?.message.id {
|
||||||
|
return (messageId, strongSelf.chatControllerInteraction.selectionState?.selectedIds.contains(messageId) ?? false)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectionGesture.updateSelection = { [weak self] messageId, selected in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.chatControllerInteraction.toggleMessagesSelection([messageId], selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.itemGrid.view.addGestureRecognizer(selectionGesture)
|
||||||
|
self.gridSelectionGesture = selectionGesture
|
||||||
|
}
|
||||||
|
} else if let gridSelectionGesture = self.gridSelectionGesture {
|
||||||
|
self.itemGrid.view.removeGestureRecognizer(gridSelectionGesture)
|
||||||
|
self.gridSelectionGesture = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2101,7 +2101,7 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
|||||||
|
|
||||||
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
|
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
||||||
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
|
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||||
transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize()))
|
transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize()))
|
||||||
|
|
||||||
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
|
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
|
||||||
@ -3675,14 +3675,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class DynamicIslandMaskNode: ManagedAnimationNode {
|
private class DynamicIslandMaskNode: ManagedAnimationNode {
|
||||||
|
var frameIndex: Int = 0
|
||||||
|
|
||||||
func update(_ value: CGFloat) {
|
func update(_ value: CGFloat) {
|
||||||
let lowerBound = 0
|
let lowerBound = 0
|
||||||
let upperBound = 180
|
let upperBound = 180
|
||||||
let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound))
|
let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound))
|
||||||
|
if frameIndex != self.frameIndex {
|
||||||
|
self.frameIndex = frameIndex
|
||||||
self.trackTo(item: ManagedAnimationItem(source: .local("UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001))
|
self.trackTo(item: ManagedAnimationItem(source: .local("UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class DynamicIslandBlurNode: ASDisplayNode {
|
private class DynamicIslandBlurNode: ASDisplayNode {
|
||||||
private var effectView: UIVisualEffectView?
|
private var effectView: UIVisualEffectView?
|
||||||
@ -3730,6 +3734,7 @@ private class DynamicIslandBlurNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(_ value: CGFloat) {
|
func update(_ value: CGFloat) {
|
||||||
|
let fadeAlpha = min(1.0, max(0.0, -0.25 + value * 1.55))
|
||||||
if value > 0.0 {
|
if value > 0.0 {
|
||||||
self.prepare()
|
self.prepare()
|
||||||
self.effectView?.layer.timeOffset = max(0.0, -0.1 + value * 1.1)
|
self.effectView?.layer.timeOffset = max(0.0, -0.1 + value * 1.1)
|
||||||
@ -3739,8 +3744,7 @@ private class DynamicIslandBlurNode: ASDisplayNode {
|
|||||||
self.effectView?.layer.timeOffset = 0.0
|
self.effectView?.layer.timeOffset = 0.0
|
||||||
self.effectView?.effect = nil
|
self.effectView?.effect = nil
|
||||||
}
|
}
|
||||||
|
self.fadeNode.alpha = fadeAlpha
|
||||||
self.fadeNode.alpha = min(1.0, max(0.0, -0.25 + value * 1.55))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layout() {
|
override func layout() {
|
||||||
|
@ -9313,6 +9313,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func presentSelectionDiscardAlert(action: @escaping () -> Void = {}) -> Bool {
|
||||||
|
if let selectedIds = self.chatInterfaceInteraction.selectionState?.selectedIds, !selectedIds.isEmpty {
|
||||||
|
self.controller?.present(textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.PeerInfo_CancelSelectionAlertText, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.PeerInfo_CancelSelectionAlertNo, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.PeerInfo_CancelSelectionAlertYes, action: {
|
||||||
|
action()
|
||||||
|
})]), in: .window(.root))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder {
|
public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder {
|
||||||
@ -9666,6 +9676,20 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !isSettings {
|
||||||
|
self.attemptNavigation = { [weak self] action in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if strongSelf.controllerNode.presentSelectionDiscardAlert(action: action) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
|
@ -667,7 +667,7 @@ public class ShareRootControllerImpl {
|
|||||||
attemptSelectionImpl?(peer)
|
attemptSelectionImpl?(peer)
|
||||||
}, createNewGroup: {
|
}, createNewGroup: {
|
||||||
createNewGroupImpl?()
|
createNewGroupImpl?()
|
||||||
}, pretendPresentedInModal: true, selectForumThreads: true))
|
}, pretendPresentedInModal: true, selectForumThreads: false))
|
||||||
|
|
||||||
controller.customDismiss = {
|
controller.customDismiss = {
|
||||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||||
|
@ -154,6 +154,7 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var isUsingSoftlight: Bool = false
|
private var isUsingSoftlight: Bool = false
|
||||||
|
private var useFilter: Bool = false
|
||||||
|
|
||||||
var suspendCompositionUpdates: Bool = false
|
var suspendCompositionUpdates: Bool = false
|
||||||
private var needsCompositionUpdate: Bool = false
|
private var needsCompositionUpdate: Bool = false
|
||||||
@ -172,10 +173,11 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||||||
useSoftlight = true
|
useSoftlight = true
|
||||||
useFilter = false
|
useFilter = false
|
||||||
}
|
}
|
||||||
if self.isUsingSoftlight != useSoftlight {
|
if self.isUsingSoftlight != useSoftlight || self.useFilter != useFilter {
|
||||||
self.isUsingSoftlight = useSoftlight
|
self.isUsingSoftlight = useSoftlight
|
||||||
|
self.useFilter = useFilter
|
||||||
|
|
||||||
if self.isUsingSoftlight && useFilter {
|
if self.isUsingSoftlight && self.useFilter {
|
||||||
self.compositingFilter = "softLightBlendMode"
|
self.compositingFilter = "softLightBlendMode"
|
||||||
} else {
|
} else {
|
||||||
self.compositingFilter = nil
|
self.compositingFilter = nil
|
||||||
@ -842,10 +844,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
}
|
}
|
||||||
private static var cachedSharedPattern: (PatternKey, UIImage)?
|
private static var cachedSharedPattern: (PatternKey, UIImage)?
|
||||||
|
|
||||||
//private var inlineAnimationNodes: [(AnimatedStickerNode, CGPoint)] = []
|
|
||||||
//private let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
|
||||||
//private var activateInlineAnimationTimer: SwiftSignalKit.Timer?
|
|
||||||
|
|
||||||
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
var isReady: Signal<Bool, NoError> {
|
var isReady: Signal<Bool, NoError> {
|
||||||
return self._isReady.get()
|
return self._isReady.get()
|
||||||
@ -1309,13 +1307,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
|
|
||||||
self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: transition)
|
self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: transition)
|
||||||
|
|
||||||
/*for (animationNode, relativePosition) in self.inlineAnimationNodes {
|
|
||||||
let sizeNorm = CGSize(width: 1440, height: 2960)
|
|
||||||
let animationSize = CGSize(width: 512.0 / sizeNorm.width * size.width, height: 512.0 / sizeNorm.height * size.height)
|
|
||||||
animationNode.frame = CGRect(origin: CGPoint(x: relativePosition.x / sizeNorm.width * size.width, y: relativePosition.y / sizeNorm.height * size.height), size: animationSize)
|
|
||||||
animationNode.updateLayout(size: animationNode.frame.size)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if isFirstLayout && !self.frame.isEmpty {
|
if isFirstLayout && !self.frame.isEmpty {
|
||||||
self.updateScale()
|
self.updateScale()
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ public struct WebAppParameters {
|
|||||||
let buttonText: String?
|
let buttonText: String?
|
||||||
let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
||||||
let fromMenu: Bool
|
let fromMenu: Bool
|
||||||
|
let fromAttachMenu: Bool
|
||||||
let isInline: Bool
|
let isInline: Bool
|
||||||
let isSimple: Bool
|
let isSimple: Bool
|
||||||
|
|
||||||
@ -147,6 +148,7 @@ public struct WebAppParameters {
|
|||||||
buttonText: String?,
|
buttonText: String?,
|
||||||
keepAliveSignal: Signal<Never, KeepWebViewError>?,
|
keepAliveSignal: Signal<Never, KeepWebViewError>?,
|
||||||
fromMenu: Bool,
|
fromMenu: Bool,
|
||||||
|
fromAttachMenu: Bool,
|
||||||
isInline: Bool,
|
isInline: Bool,
|
||||||
isSimple: Bool
|
isSimple: Bool
|
||||||
) {
|
) {
|
||||||
@ -159,6 +161,7 @@ public struct WebAppParameters {
|
|||||||
self.buttonText = buttonText
|
self.buttonText = buttonText
|
||||||
self.keepAliveSignal = keepAliveSignal
|
self.keepAliveSignal = keepAliveSignal
|
||||||
self.fromMenu = fromMenu
|
self.fromMenu = fromMenu
|
||||||
|
self.fromAttachMenu = fromAttachMenu
|
||||||
self.isInline = isInline
|
self.isInline = isInline
|
||||||
self.isSimple = isSimple
|
self.isSimple = isSimple
|
||||||
}
|
}
|
||||||
@ -656,7 +659,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.handleSendData(data: eventData)
|
self.handleSendData(data: eventData)
|
||||||
}
|
}
|
||||||
case "web_app_setup_main_button":
|
case "web_app_setup_main_button":
|
||||||
if let webView = self.webView, !webView.didTouchOnce && controller.url == nil {
|
if let webView = self.webView, !webView.didTouchOnce && controller.url == nil && controller.fromAttachMenu {
|
||||||
self.delayedScriptMessage = message
|
self.delayedScriptMessage = message
|
||||||
} else if let json = json {
|
} else if let json = json {
|
||||||
if var isVisible = json["is_visible"] as? Bool {
|
if var isVisible = json["is_visible"] as? Bool {
|
||||||
@ -1058,6 +1061,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
private let payload: String?
|
private let payload: String?
|
||||||
private let buttonText: String?
|
private let buttonText: String?
|
||||||
private let fromMenu: Bool
|
private let fromMenu: Bool
|
||||||
|
private let fromAttachMenu: Bool
|
||||||
private let isInline: Bool
|
private let isInline: Bool
|
||||||
private let isSimple: Bool
|
private let isSimple: Bool
|
||||||
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
|
||||||
@ -1083,6 +1087,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.payload = params.payload
|
self.payload = params.payload
|
||||||
self.buttonText = params.buttonText
|
self.buttonText = params.buttonText
|
||||||
self.fromMenu = params.fromMenu
|
self.fromMenu = params.fromMenu
|
||||||
|
self.fromAttachMenu = params.fromAttachMenu
|
||||||
self.isInline = params.isInline
|
self.isInline = params.isInline
|
||||||
self.isSimple = params.isSimple
|
self.isSimple = params.isSimple
|
||||||
self.keepAliveSignal = params.keepAliveSignal
|
self.keepAliveSignal = params.keepAliveSignal
|
||||||
|
Loading…
x
Reference in New Issue
Block a user