Merge commit '76aacfc03eca5bcd74ba31aca25b5b4ff3c1f86c'

# Conflicts:
#	Telegram/Telegram-iOS/en.lproj/Localizable.strings
This commit is contained in:
Ali 2023-03-20 16:35:01 +04:00
commit fbba4f9f94
23 changed files with 228 additions and 128 deletions

View File

@ -9084,3 +9084,9 @@ Sorry for the inconvenience.";
"Conversation.SendMessageErrorTooFastTitle" = "Not so fast";
"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.";

View File

@ -115,7 +115,7 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
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
sectionItems.append(cardholderItem)

View File

@ -9,6 +9,7 @@ private let titleFont = Font.regular(17.0)
enum BotPaymentFieldContentType {
case generic
case name
case asciiName
case phoneNumber
case email
case address
@ -51,6 +52,9 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate {
case .generic:
break
case .name:
self.textField.textField.autocorrectionType = .no
self.textField.textField.keyboardType = .default
case .asciiName:
self.textField.textField.autocorrectionType = .no
self.textField.textField.keyboardType = .asciiCapable
case .address:

View File

@ -2561,10 +2561,6 @@ public final class ChatListNode: ListView {
return strongSelf.isSelectionGestureEnabled
}
self.view.addGestureRecognizer(selectionRecognizer)
// if case .forum = location {
// self.isSelectionGestureEnabled = false
// }
}
deinit {

View File

@ -139,6 +139,11 @@ open class ViewControllerComponentContainer: ViewController {
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) {
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(
statusBarHeight: layout.statusBarHeight ?? 0.0,
navigationHeight: navigationHeight,
@ -147,7 +152,7 @@ open class ViewControllerComponentContainer: ViewController {
metrics: layout.metrics,
deviceMetrics: layout.deviceMetrics,
isVisible: self.currentIsVisible,
theme: self.theme ?? self.presentationData.theme,
theme: theme,
strings: self.presentationData.strings,
dateTimeFormat: self.presentationData.dateTimeFormat,
controller: { [weak self] in

View File

@ -248,7 +248,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.backgroundNode)
self.containerNode.addSubnode(self.gridNode)
// self.containerNode.addSubnode(self.scrollingArea)
//self.containerNode.addSubnode(self.scrollingArea)
let preloadPromise = self.preloadPromise
let updatedState: Signal<State, NoError>
@ -359,30 +359,36 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.itemsDimensionsUpdatedDisposable?.dispose()
}
private var selectionGesture: MediaPickerGridSelectionGesture?
private var selectionGesture: MediaPickerGridSelectionGesture<TGMediaSelectableItem>?
override func didLoad() {
super.didLoad()
self.gridNode.scrollView.alwaysBounceVertical = true
self.gridNode.scrollView.showsVerticalScrollIndicator = false
self.selectionGesture = MediaPickerGridSelectionGesture(target: nil, action: nil, gridNode: self.gridNode)
self.selectionGesture?.delegate = self
self.selectionGesture?.began = { [weak self] in
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
selectionGesture.delegate = self
selectionGesture.began = { [weak self] in
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 {
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
} else {
return nil
}
}
self.selectionGesture?.updateSelection = { [weak self] asset, selected in
selectionGesture.updateSelection = { [weak self] asset, selected in
if let strongSelf = self {
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 {
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: {
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
self?.dismissAllTooltips()
completion()
@ -1794,37 +1799,31 @@ private final class MediaPickerContextReferenceContentSource: ContextReferenceCo
}
}
private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
var itemAt: (CGPoint) -> (TGMediaSelectableItem, Bool)? = { _ in return nil }
var updateSelection: (TGMediaSelectableItem, Bool) -> Void = { _, _ in}
var began: () -> Void = {}
private weak var gridNode: GridNode?
public class MediaPickerGridSelectionGesture<T> : UIPanGestureRecognizer {
public var itemAt: (CGPoint) -> (T, Bool)? = { _ in return nil }
public var updateSelection: (T, Bool) -> Void = { _, _ in}
public var updateIsScrollEnabled: (Bool) -> Void = { _ in}
public var began: () -> Void = {}
private var processing = false
private var selecting = false
private var initialLocation: CGPoint?
var sideInset: CGFloat = 0.0
public var sideInset: CGFloat = 0.0
init(target: Any?, action: Selector?, gridNode: GridNode) {
self.gridNode = gridNode
super.init(target: target, action: action)
gridNode.view.addGestureRecognizer(self)
public init() {
super.init(target: nil, action: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
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
}
let location = touch.location(in: gridNode.view)
let location = touch.location(in: self.view)
if location.x > self.sideInset {
self.initialLocation = location
} 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)
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
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)
var additionalLocation: CGPoint?
@ -1849,7 +1848,7 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
self.state = .failed
} else if abs(translation.x) > 8.0 {
self.processing = true
self.gridNode?.scrollView.isScrollEnabled = false
self.updateIsScrollEnabled(false)
self.began()
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)
self.state = .failed
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)
self.state = .failed
self.reset()
}
override func reset() {
public override func reset() {
super.reset()
self.processing = false
self.initialLocation = nil
self.gridNode?.scrollView.isScrollEnabled = true
self.updateIsScrollEnabled(true)
}
}

View File

@ -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
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 {
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 {
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)

View File

@ -167,7 +167,6 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
static var body: Body {
let overscroll = Child(Rectangle.self)
let fade = Child(RoundedRectangle.self)
let text = Child(MultilineTextComponent.self)
let optionsSection = Child(SectionGroupComponent.self)
let perksSection = Child(SectionGroupComponent.self)
@ -195,23 +194,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
context.add(overscroll
.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
let textColor = theme.list.itemPrimaryTextColor

View File

@ -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) {
async {
let node = ItemListReactionItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
node.contentSize = layout.contentSize
node.insets = layout.insets
Queue.mainQueue().async {
let node = ItemListReactionItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
node.contentSize = layout.contentSize
node.insets = layout.insets
completion(node, {
return (nil, { _ in apply() })
})

View File

@ -92,6 +92,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
case packOrder(PresentationTheme, String, Bool)
case packOrderInfo(PresentationTheme, String)
case suggestAnimatedEmoji(String, Bool)
case suggestAnimatedEmojiInfo(PresentationTheme, String)
case packsTitle(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?)
case packsInfo(PresentationTheme, String)
@ -100,7 +101,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
switch self {
case .trending, .masks, .emoji, .quickReaction, .archived:
return InstalledStickerPacksSection.categories.rawValue
case .suggestOptions, .largeEmoji, .suggestAnimatedEmoji, .packOrder, .packOrderInfo:
case .suggestOptions, .largeEmoji, .suggestAnimatedEmoji, .suggestAnimatedEmojiInfo, .packOrder, .packOrderInfo:
return InstalledStickerPacksSection.settings.rawValue
case .packsTitle, .pack, .packsInfo:
return InstalledStickerPacksSection.stickers.rawValue
@ -125,16 +126,18 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
return .index(6)
case .suggestAnimatedEmoji:
return .index(7)
case .packOrder:
case .suggestAnimatedEmojiInfo:
return .index(8)
case .packOrderInfo:
case .packOrder:
return .index(9)
case .packsTitle:
case .packOrderInfo:
return .index(10)
case .packsTitle:
return .index(11)
case let .pack(_, _, _, info, _, _, _, _, _, _):
return .pack(info.id)
case .packsInfo:
return .index(11)
return .index(12)
}
}
@ -200,6 +203,12 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else {
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):
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@ -323,9 +332,16 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
default:
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:
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
default:
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
arguments.toggleSuggestAnimatedEmoji(value)
})
case let .suggestAnimatedEmojiInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .packsTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
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(.suggestAnimatedEmojiInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_SuggestAnimatedEmojiInfo))
}
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView {

View File

@ -711,11 +711,10 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.item = item
strongSelf.layoutParams = params
strongSelf.listNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
if strongSelf.backgroundNode.supernode == nil {
strongSelf.containerNode.insertSubnode(strongSelf.backgroundNode, at: 0)
}
@ -828,10 +827,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
self.snapshotView = snapshotView
}
self.listNode.forEachVisibleItemNode { node in
self.listNode.enumerateItemNodes { node in
if let node = node as? ThemeCarouselThemeItemIconNode {
node.prepareCrossfadeTransition()
}
return true
}
}
@ -839,22 +839,23 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
return
}
var views: [UIView] = []
if let snapshotView = self.snapshotView {
views.append(snapshotView)
self.snapshotView = nil
}
self.listNode.forEachVisibleItemNode { node in
self.listNode.enumerateItemNodes { node in
if let node = node as? ThemeCarouselThemeItemIconNode {
if let snapshotView = node.snapshotView {
views.append(snapshotView)
node.snapshotView = nil
}
}
return true
}
UIView.animate(withDuration: 0.3, animations: {
for view in views {
view.alpha = 0.0

View File

@ -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) {
if let view = view {
if var leftOffset = leftOffset {
leftOffset += UIScreenPixel
if let leftOffset = leftOffset {
if let view = view.snapshotView(afterScreenUpdates: false) {
let clipView = UIView()
clipView.clipsToBounds = true
@ -1306,13 +1304,13 @@ public final class ThemeSettingsCrossfadeController: ViewController {
if let topOffset = topOffset, let bottomOffset = bottomOffset {
var frame = view.frame
frame.origin.y = topOffset
frame.size.width = leftOffset
frame.size.width = leftOffset + sideInset
frame.size.height = bottomOffset - topOffset
clipView.frame = frame
frame = view.frame
frame.origin.y = -topOffset
frame.size.width = leftOffset
frame.size.width = leftOffset + sideInset
frame.size.height = bottomOffset
view.frame = frame
}
@ -1322,7 +1320,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
}
if sideInset > 0.0 {
if let view = view.snapshotView(afterScreenUpdates: false) {
if let view = view.snapshotView(afterScreenUpdates: false), leftOffset == nil {
let clipView = UIView()
clipView.clipsToBounds = true
clipView.addSubview(view)

View File

@ -1344,6 +1344,12 @@ public final class SparseItemGrid: ASDisplayNode {
self.pinchRecognizer?.isEnabled = self.pinchEnabled
}
}
public var isScrollEnabled: Bool = true {
didSet {
self.currentViewport?.scrollView.isScrollEnabled = self.isScrollEnabled
}
}
public init(theme: PresentationTheme, initialZoomLevel: ZoomLevel? = nil) {
self.theme = theme

View File

@ -8,22 +8,21 @@ import MtProtoKit
func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: String?) -> Signal<Void, NoError> {
if let payload = payload, !payload.isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)
|> mapToSignal { botPeer -> Signal<Void, NoError> in
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))
|> mapToSignal { result -> Signal<Void, MTRpcError> in
account.stateManager.addUpdates(result)
return .complete()
}
|> `catch` { _ -> Signal<Void, MTRpcError> in
return .complete()
}
return r
|> retryRequest
} else {
|> mapToSignal { botPeer -> Signal<Void, NoError> in
if let inputUser = apiInputUser(botPeer) {
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
account.stateManager.addUpdates(result)
return .complete()
}
|> `catch` { _ -> Signal<Void, MTRpcError> in
return .complete()
}
|> retryRequest
} else {
return .complete()
}
}
} else {
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) |> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()

View File

@ -4104,7 +4104,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
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
self?.openUrl(url, concealed: true, forceExternal: true)
}, getInputContainerNode: { [weak self] in
@ -4159,7 +4159,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
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
self?.openUrl(url, concealed: true, forceExternal: true)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
@ -4199,7 +4199,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
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
self?.openUrl(url, concealed: true, forceExternal: true)
}, completion: { [weak self] in
@ -12727,7 +12727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
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
self?.openUrl(url, concealed: true, forceExternal: true)
}, completion: { [weak self] in
@ -13247,10 +13247,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
case let .app(bot, botName, _):
var payload: String?
var fromAttachMenu = true
if case let .bot(_, botPayload, _) = subject {
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 controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId, threadId: strongSelf.chatLocation.threadId)
controller.openUrl = { [weak self] url in

View File

@ -1597,8 +1597,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() {
if textInputNode.isCurrentlyEmoji() {
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance {
if textInputNode.isFirstResponder() && textInputNode.isCurrentlyEmoji() {
textInputNode.initialPrimaryLanguage = "emoji"
textInputNode.resetInitialPrimaryLanguage()
}
@ -2268,7 +2268,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
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
} else if let contextPlaceholderNode = self.contextPlaceholderNode {
self.contextPlaceholderNode = nil

View File

@ -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 (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())

View File

@ -25,6 +25,7 @@ import CheckNode
import AppBundle
import ChatControllerInteraction
import InvisibleInkDustNode
import MediaPickerUI
private final class FrameSequenceThumbnailNode: ASDisplayNode {
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 {
case photoOrVideo
case photo
@ -2445,6 +2446,27 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
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) {
switch self.contentType {
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)
}
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
}
}
}

View File

@ -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 {
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()))
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
@ -3675,12 +3675,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
private class DynamicIslandMaskNode: ManagedAnimationNode {
var frameIndex: Int = 0
func update(_ value: CGFloat) {
let lowerBound = 0
let upperBound = 180
let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound))
self.trackTo(item: ManagedAnimationItem(source: .local("UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001))
if frameIndex != self.frameIndex {
self.frameIndex = frameIndex
self.trackTo(item: ManagedAnimationItem(source: .local("UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001))
}
}
}
@ -3730,6 +3734,7 @@ private class DynamicIslandBlurNode: ASDisplayNode {
}
func update(_ value: CGFloat) {
let fadeAlpha = min(1.0, max(0.0, -0.25 + value * 1.55))
if value > 0.0 {
self.prepare()
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?.effect = nil
}
self.fadeNode.alpha = min(1.0, max(0.0, -0.25 + value * 1.55))
self.fadeNode.alpha = fadeAlpha
}
override func layout() {

View File

@ -9313,6 +9313,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
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 {
@ -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) {

View File

@ -667,7 +667,7 @@ public class ShareRootControllerImpl {
attemptSelectionImpl?(peer)
}, createNewGroup: {
createNewGroupImpl?()
}, pretendPresentedInModal: true, selectForumThreads: true))
}, pretendPresentedInModal: true, selectForumThreads: false))
controller.customDismiss = {
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)

View File

@ -154,6 +154,7 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
}
private var isUsingSoftlight: Bool = false
private var useFilter: Bool = false
var suspendCompositionUpdates: Bool = false
private var needsCompositionUpdate: Bool = false
@ -172,10 +173,11 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
useSoftlight = true
useFilter = false
}
if self.isUsingSoftlight != useSoftlight {
if self.isUsingSoftlight != useSoftlight || self.useFilter != useFilter {
self.isUsingSoftlight = useSoftlight
self.useFilter = useFilter
if self.isUsingSoftlight && useFilter {
if self.isUsingSoftlight && self.useFilter {
self.compositingFilter = "softLightBlendMode"
} else {
self.compositingFilter = nil
@ -842,10 +844,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
}
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)
var isReady: Signal<Bool, NoError> {
return self._isReady.get()
@ -1308,13 +1306,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
}
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 {
self.updateScale()

View File

@ -134,6 +134,7 @@ public struct WebAppParameters {
let buttonText: String?
let keepAliveSignal: Signal<Never, KeepWebViewError>?
let fromMenu: Bool
let fromAttachMenu: Bool
let isInline: Bool
let isSimple: Bool
@ -147,6 +148,7 @@ public struct WebAppParameters {
buttonText: String?,
keepAliveSignal: Signal<Never, KeepWebViewError>?,
fromMenu: Bool,
fromAttachMenu: Bool,
isInline: Bool,
isSimple: Bool
) {
@ -159,6 +161,7 @@ public struct WebAppParameters {
self.buttonText = buttonText
self.keepAliveSignal = keepAliveSignal
self.fromMenu = fromMenu
self.fromAttachMenu = fromAttachMenu
self.isInline = isInline
self.isSimple = isSimple
}
@ -656,7 +659,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.handleSendData(data: eventData)
}
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
} else if let json = json {
if var isVisible = json["is_visible"] as? Bool {
@ -1058,6 +1061,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
private let payload: String?
private let buttonText: String?
private let fromMenu: Bool
private let fromAttachMenu: Bool
private let isInline: Bool
private let isSimple: Bool
private let keepAliveSignal: Signal<Never, KeepWebViewError>?
@ -1083,6 +1087,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.payload = params.payload
self.buttonText = params.buttonText
self.fromMenu = params.fromMenu
self.fromAttachMenu = params.fromAttachMenu
self.isInline = params.isInline
self.isSimple = params.isSimple
self.keepAliveSignal = params.keepAliveSignal