diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index d5ecbd7337..fc63876b10 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -46,15 +46,18 @@ public final class PagerComponentChildEnvironment: Equatable { public let containerInsets: UIEdgeInsets public let onChildScrollingUpdate: (ContentScrollingUpdate) -> Void public let onWantsExclusiveModeUpdated: (Bool) -> Void + public let scrollToTop: ActionSlot init( containerInsets: UIEdgeInsets, onChildScrollingUpdate: @escaping (ContentScrollingUpdate) -> Void, - onWantsExclusiveModeUpdated: @escaping (Bool) -> Void + onWantsExclusiveModeUpdated: @escaping (Bool) -> Void, + scrollToTop: ActionSlot ) { self.containerInsets = containerInsets self.onChildScrollingUpdate = onChildScrollingUpdate self.onWantsExclusiveModeUpdated = onWantsExclusiveModeUpdated + self.scrollToTop = scrollToTop } public static func ==(lhs: PagerComponentChildEnvironment, rhs: PagerComponentChildEnvironment) -> Bool { @@ -296,6 +299,7 @@ public final class PagerComponent() init(view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>) { self.view = view @@ -368,7 +372,7 @@ public final class PagerComponent 0.0 { + if let centralId = self.centralId, let component = self.component, let centralIndex = component.contents.firstIndex(where: { $0.id == centralId }), let centralView = self.contentViews[centralId], !centralView.wantsExclusiveMode, var paneTransitionGestureState = self.paneTransitionGestureState, self.bounds.width > 0.0 { var fraction = recognizer.translation(in: self).x / self.bounds.width if centralIndex <= 0 { fraction = min(0.0, fraction) @@ -463,6 +467,8 @@ public final class PagerComponent() var nextX: CGFloat = sideInset for i in 0 ..< self.items.count { diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 08a96013cd..301643c9d1 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -552,206 +552,435 @@ public final class ShareController: ViewController { }) } self.controllerNode.share = { [weak self] text, peerIds, topicIds, showNames, silently in - guard let strongSelf = self else { + guard let self else { return .complete() } - - var shareSignals: [Signal<[MessageId?], NoError>] = [] - var subject = strongSelf.subject - if let segmentedValues = strongSelf.segmentedValues { - let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex] - subject = selectedValue.subject - } - func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool) -> [EnqueueMessage] { - return messages.map { message in - return message.withUpdatedAttributes({ attributes in - var attributes = attributes - if !showNames { - attributes.append(ForwardOptionsMessageAttribute(hideNames: true, hideCaptions: false)) - } - if silently { - attributes.append(NotificationInfoMessageAttribute(flags: .muted)) - } - return attributes - }) - } - } - - switch subject { - case let .url(url): - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } else { - messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .text(string): - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .quote(string, url): - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) - attributedText.append(NSAttributedString(string: "\n\n\(url)")) - let entities = generateChatInputTextEntities(attributedText) - messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .image(representations): - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .media(mediaReference): - var sendTextAsCaption = false - if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile { - sendTextAsCaption = true + return self.currentContext.engine.data.get(EngineDataMap( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) + )) + |> castError(ShareControllerError.self) + |> mapToSignal { [weak self] peers -> Signal in + guard let strongSelf = self else { + return .complete() } - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + var shareSignals: [Signal<[MessageId?], NoError>] = [] + var subject = strongSelf.subject + if let segmentedValues = strongSelf.segmentedValues { + let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex] + subject = selectedValue.subject + } + + func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool) -> [EnqueueMessage] { + return messages.map { message in + return message.withUpdatedAttributes({ attributes in + var attributes = attributes + if !showNames { + attributes.append(ForwardOptionsMessageAttribute(hideNames: true, hideCaptions: false)) + } + if silently { + attributes.append(NotificationInfoMessageAttribute(flags: .muted)) + } + return attributes + }) + } + } + + switch subject { + case let .url(url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } else { + messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .text(string): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .quote(string, url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) + attributedText.append(NSAttributedString(string: "\n\n\(url)")) + let entities = generateChatInputTextEntities(attributedText) + messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .image(representations): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendPhotos = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendPhotos = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendPhotos = true + } + + if banSendPhotos { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .media(mediaReference): + var sendTextAsCaption = false + if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile { + sendTextAsCaption = true } - var messages: [EnqueueMessage] = [] - if !text.isEmpty && !sendTextAsCaption { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendType = false + if mediaReference.media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = mediaReference.media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty && !sendTextAsCaption { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } - messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .mapMedia(media): - for peerId in peerIds { - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + case let .mapMedia(media): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + case let .messages(messages): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var replyToMessageId: MessageId? + var threadId: Int64? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + threadId = topicId + } + + var messagesToEnqueue: [EnqueueMessage] = [] + if !text.isEmpty { + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + + messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + for message in messages { + for media in message.media { + var banSendType = false + if media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .complete() + } + } + + messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) + } + messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) } - messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .messages(messages): - for peerId in peerIds { - var replyToMessageId: MessageId? - var threadId: Int64? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - threadId = topicId - } - - var messagesToEnqueue: [EnqueueMessage] = [] - if !text.isEmpty { - messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - for message in messages { - messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) - } - messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) - } - case let .fromExternal(f): - return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) - |> map { state -> ShareState in - switch state { + case let .fromExternal(f): + return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) + |> map { state -> ShareState in + switch state { case let .preparing(long): return .preparing(long) case let .progress(value): return .progress(value) case .done: return .done + } } } - } - let account = strongSelf.currentAccount - let queue = Queue.mainQueue() - var displayedError = false - return combineLatest(queue: queue, shareSignals) - |> castError(ShareControllerError.self) - |> mapToSignal { messageIdSets -> Signal in - var statuses: [Signal<(MessageId, PendingMessageStatus?, PendingMessageFailureReason?), ShareControllerError>] = [] - for messageIds in messageIdSets { - for case let id? in messageIds { - statuses.append(account.pendingMessageManager.pendingMessageStatus(id) - |> castError(ShareControllerError.self) - |> map { status, error -> (MessageId, PendingMessageStatus?, PendingMessageFailureReason?) in - return (id, status, error) - }) + let account = strongSelf.currentAccount + let queue = Queue.mainQueue() + var displayedError = false + return combineLatest(queue: queue, shareSignals) + |> castError(ShareControllerError.self) + |> mapToSignal { messageIdSets -> Signal in + var statuses: [Signal<(MessageId, PendingMessageStatus?, PendingMessageFailureReason?), ShareControllerError>] = [] + for messageIds in messageIdSets { + for case let id? in messageIds { + statuses.append(account.pendingMessageManager.pendingMessageStatus(id) + |> castError(ShareControllerError.self) + |> map { status, error -> (MessageId, PendingMessageStatus?, PendingMessageFailureReason?) in + return (id, status, error) + }) + } } - } - return combineLatest(queue: queue, statuses) - |> mapToSignal { statuses -> Signal in - var hasStatuses = false - for (id, status, error) in statuses { - if let error = error { - Queue.mainQueue().async { - let _ = TelegramEngine(account: account).messages.deleteMessagesInteractively(messageIds: [id], type: .forEveryone).start() - let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId)) - |> deliverOnMainQueue).start(next: { peer in - guard let strongSelf = self, let peer = peer else { - return - } - if !displayedError, case .slowmodeActive = error { - displayedError = true - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } - }) + return combineLatest(queue: queue, statuses) + |> mapToSignal { statuses -> Signal in + var hasStatuses = false + for (id, status, error) in statuses { + if let error = error { + Queue.mainQueue().async { + let _ = TelegramEngine(account: account).messages.deleteMessagesInteractively(messageIds: [id], type: .forEveryone).start() + let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer else { + return + } + if !displayedError { + if case .slowmodeActive = error { + displayedError = true + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } else if case .mediaRestricted = error { + displayedError = true + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + } + }) + } + } + if status != nil { + hasStatuses = true } } - if status != nil { - hasStatuses = true + if !hasStatuses { + return .single(.done) } + return .complete() } - if !hasStatuses { - return .single(.done) - } - return .complete() + |> take(1) } - |> take(1) } } self.controllerNode.shareExternal = { [weak self] _ in @@ -1261,3 +1490,73 @@ public func presentExternalShare(context: AccountContext, text: String, parentCo } context.sharedContext.applicationBindings.presentNativeController(activityController) } + +private func restrictedSendingContentsText(peer: EnginePeer, presentationData: PresentationData) -> String { + //TODO:localize + var itemList: [String] = [] + + let order: [TelegramChatBannedRightsFlags] = [ + .banSendText, + .banSendPhotos, + .banSendVideos, + .banSendVoice, + .banSendInstantVideos, + .banSendFiles, + .banSendMusic, + .banSendStickers + ] + + for right in order { + if case let .channel(channel) = peer { + if channel.hasBannedPermission(right) != nil { + continue + } + } else if case let .legacyGroup(group) = peer { + if group.hasBannedPermission(right) { + continue + } + } + + var title: String? + switch right { + case .banSendText: + title = "text messages" + case .banSendPhotos: + title = "photos" + case .banSendVideos: + title = "videos" + case .banSendVoice: + title = "voice messages" + case .banSendInstantVideos: + title = "video messages" + case .banSendFiles: + title = "files" + case .banSendMusic: + title = "music" + case .banSendStickers: + title = "Stickers & GIFs" + default: + break + } + if let title { + itemList.append(title) + } + } + + if itemList.isEmpty { + return "Sending messages is disabled in \(peer.compactDisplayTitle)" + } + + var itemListString = "" + for i in 0 ..< itemList.count { + if i != 0 { + itemListString.append(", ") + } + if i == itemList.count - 1 && i != 0 { + itemListString.append("and ") + } + itemListString.append(itemList[i]) + } + + return "The admins of \(peer.compactDisplayTitle) group only allow to send \(itemListString)." +} diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 00bec289d1..9403c7b7bc 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -1222,7 +1222,7 @@ final class AvatarEditorScreenComponent: Component { defaultToEmojiTab: true, externalTopPanelContainer: self.panelHostView, externalBottomPanelContainer: nil, - displayTopPanelBackground: true, + displayTopPanelBackground: .blur, topPanelExtensionUpdated: { _, _ in }, hideInputUpdated: { _, _, _ in }, hideTopPanelUpdated: { [weak self] hideTopPanel, transition in diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 6d6c8efc13..19e7429472 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -178,7 +178,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { displaySearchWithPlaceholder: nil, searchCategories: nil, searchInitiallyHidden: true, - searchState: .empty + searchState: .empty(hasResults: false) ) )) @@ -244,6 +244,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { private var currentInputData: InputData private var inputDataDisposable: Disposable? private var hasRecentGifsDisposable: Disposable? + private let opaqueTopPanelBackground: Bool private struct EmojiSearchResult { var groups: [EmojiPagerContentComponent.ItemGroup] @@ -385,7 +386,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { displaySearchWithPlaceholder: presentationData.strings.Common_Search, searchCategories: searchCategories, searchInitiallyHidden: true, - searchState: .empty + searchState: .empty(hasResults: false) ) ) } @@ -418,7 +419,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { displaySearchWithPlaceholder: presentationData.strings.Common_Search, searchCategories: searchCategories, searchInitiallyHidden: true, - searchState: .empty + searchState: .empty(hasResults: false) ) ) } @@ -569,10 +570,11 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { private weak var currentUndoOverlayController: UndoOverlayController? - public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal, defaultToEmojiTab: Bool, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, chatPeerId: PeerId?) { + public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal, defaultToEmojiTab: Bool, opaqueTopPanelBackground: Bool = false, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, chatPeerId: PeerId?) { self.context = context self.currentInputData = currentInputData self.defaultToEmojiTab = defaultToEmojiTab + self.opaqueTopPanelBackground = opaqueTopPanelBackground self.controllerInteraction = controllerInteraction @@ -1493,7 +1495,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { ) } if let emoji = inputData.emoji { - let defaultSearchState: EmojiPagerContentComponent.SearchState = emojiSearchResult.isPreset ? .active : .empty + let defaultSearchState: EmojiPagerContentComponent.SearchState = emojiSearchResult.isPreset ? .active : .empty(hasResults: true) inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: emojiSearchResult.version), emptySearchResults: emptySearchResults, searchState: emojiSearchState.isSearching ? .searching : defaultSearchState) } } else if emojiSearchState.isSearching { @@ -1512,7 +1514,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { ) } if let stickers = inputData.stickers { - let defaultSearchState: EmojiPagerContentComponent.SearchState = stickerSearchResult.isPreset ? .active : .empty + let defaultSearchState: EmojiPagerContentComponent.SearchState = stickerSearchResult.isPreset ? .active : .empty(hasResults: true) inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickerSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: stickerSearchResult.id, version: stickerSearchResult.version), emptySearchResults: stickerSearchResults, searchState: stickerSearchState.isSearching ? .searching : defaultSearchState) } } else if stickerSearchState.isSearching { @@ -1771,7 +1773,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { defaultToEmojiTab: self.defaultToEmojiTab, externalTopPanelContainer: self.externalTopPanelContainerImpl, externalBottomPanelContainer: nil, - displayTopPanelBackground: false, + displayTopPanelBackground: self.opaqueTopPanelBackground ? .opaque : .none, topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in guard let strongSelf = self else { return @@ -2332,17 +2334,19 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi ) }, defaultToEmojiTab: true, + opaqueTopPanelBackground: true, controllerInteraction: nil, interfaceInteraction: nil, chatPeerId: nil ) self.inputNode = inputNode - inputNode.clipContentToTopPanel = hideBackground + inputNode.clipContentToTopPanel = true inputNode.emojiInputInteraction = inputInteraction inputNode.externalTopPanelContainerImpl = nil inputNode.switchToTextInput = { [weak self] in self?.switchToKeyboard?() } + inputNode.backgroundColor = self.presentationData.theme.chat.inputMediaPanel.backgroundColor self.addSubnode(inputNode) } } diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index bb803050ed..bbbfb7608f 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -156,7 +156,7 @@ public final class EmojiStatusSelectionComponent: Component { let topPanelHeight: CGFloat = component.hideTopPanel ? 0.0 : 42.0 let keyboardSize = self.keyboardView.update( - transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), + transition: transition,//.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), component: AnyComponent(EntityKeyboardComponent( theme: component.theme, strings: component.strings, @@ -172,7 +172,7 @@ public final class EmojiStatusSelectionComponent: Component { defaultToEmojiTab: true, externalTopPanelContainer: self.panelHostView, externalBottomPanelContainer: nil, - displayTopPanelBackground: true, + displayTopPanelBackground: .blur, topPanelExtensionUpdated: { _, _ in }, hideInputUpdated: { _, _, _ in }, hideTopPanelUpdated: { [weak self] hideTopPanel, transition in diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 015f1fd79b..c0e449ad21 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1278,7 +1278,7 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager } for (_, itemLayer) in self.visibleItemLayers { - if itemLayer.frame.inset(by: UIEdgeInsets(top: 6.0, left: itemLayout.itemSpacing, bottom: 6.0, right: itemLayout.itemSpacing)).contains(point) { + if itemLayer.frame.inset(by: UIEdgeInsets(top: -6.0, left: -itemLayout.itemSpacing, bottom: -6.0, right: -itemLayout.itemSpacing)).contains(point) { self.performItemAction(itemLayer.item, self, itemLayer.frame, itemLayer) return true } @@ -2104,7 +2104,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { var hasText = false if let textField = self.textField { textField.textColor = theme.contextMenu.primaryColor - transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width - sideTextInset, height: backgroundFrame.height))) + transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height))) if let text = textField.text, !text.isEmpty { hasText = true @@ -2556,8 +2556,8 @@ public final class EmojiPagerContentComponent: Component { case detailed } - public enum SearchState { - case empty + public enum SearchState: Equatable { + case empty(hasResults: Bool) case searching case active } @@ -4382,11 +4382,11 @@ public final class EmojiPagerContentComponent: Component { } public func scrollToItemGroup(id supergroupId: AnyHashable, subgroupId: Int32?, animated: Bool) { - guard let component = self.component, let pagerEnvironment = self.pagerEnvironment, let itemLayout = self.itemLayout else { + guard let component = self.component, let itemGroup = component.contentItemGroups.first(where: { $0.supergroupId == supergroupId }), let pagerEnvironment = self.pagerEnvironment, let itemLayout = self.itemLayout else { return } - if self.isSearchActivated { + if !component.contentItemGroups.contains(where: { $0.groupId == supergroupId }), self.isSearchActivated { self.visibleSearchHeader?.clearCategorySearch() return } @@ -4446,6 +4446,10 @@ public final class EmojiPagerContentComponent: Component { highlightFrame.size.width = self.scrollView.bounds.width - 4.0 - highlightFrame.minX } + if (itemGroup.isPremiumLocked || itemGroup.isFeatured), !itemGroup.isEmbedded, case .compact = itemLayout.layoutType { + highlightFrame.size.height += 6.0 + } + highlightLayer.frame = highlightFrame self.scrollView.layer.insertSublayer(highlightLayer, at: 0) highlightLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.8, removeOnCompletion: false, completion: { [weak highlightLayer] _ in @@ -5122,7 +5126,7 @@ public final class EmojiPagerContentComponent: Component { scrollView.layer.removeAllAnimations() } - if self.isSearchActivated, let component = self.component, component.searchState == .empty, !component.searchAlwaysActive, let visibleSearchHeader = self.visibleSearchHeader, visibleSearchHeader.currentPresetSearchTerm == nil { + if self.isSearchActivated, let component = self.component, component.searchState == .empty(hasResults: false), !component.searchAlwaysActive, let visibleSearchHeader = self.visibleSearchHeader, visibleSearchHeader.currentPresetSearchTerm == nil { scrollView.isScrollEnabled = false DispatchQueue.main.async { scrollView.isScrollEnabled = true @@ -6122,6 +6126,14 @@ public final class EmojiPagerContentComponent: Component { self.pagerEnvironment = pagerEnvironment + pagerEnvironment.scrollToTop.connect { [weak self] in + guard let self else { + return + } + + self.scrollView.setContentOffset(CGPoint(), animated: true) + } + self.updateIsWarpEnabled(isEnabled: component.warpContentsOnEdges) if let longTapRecognizer = self.longTapRecognizer { @@ -7714,7 +7726,7 @@ public final class EmojiPagerContentComponent: Component { contentItemGroups: allItemGroups, itemLayoutType: .compact, itemContentUniqueId: nil, - searchState: .empty, + searchState: .empty(hasResults: false), warpContentsOnEdges: isReactionSelection || isStatusSelection || isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection, displaySearchWithPlaceholder: displaySearchWithPlaceholder, searchCategories: searchCategories, @@ -8236,7 +8248,7 @@ public final class EmojiPagerContentComponent: Component { contentItemGroups: allItemGroups, itemLayoutType: .detailed, itemContentUniqueId: nil, - searchState: .empty, + searchState: .empty(hasResults: false), warpContentsOnEdges: isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection, displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, searchCategories: searchCategories, diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift index 4564a0d9a8..c32e01f34d 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift @@ -391,7 +391,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode contentItemGroups: self.itemGroups, itemLayoutType: .compact, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: "main", version: 0), - searchState: .empty, + searchState: .empty(hasResults: false), warpContentsOnEdges: false, displaySearchWithPlaceholder: "Search Emoji", searchCategories: nil, @@ -411,7 +411,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode iconFile: nil ) } - emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: 0), emptySearchResults: emptySearchResults, searchState: .active) + emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: 0), emptySearchResults: emptySearchResults, searchState: .empty(hasResults: true)) } let _ = self.keyboardView.update( @@ -431,7 +431,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode defaultToEmojiTab: true, externalTopPanelContainer: self.panelHostView, externalBottomPanelContainer: nil, - displayTopPanelBackground: true, + displayTopPanelBackground: .blur, topPanelExtensionUpdated: { _, _ in }, hideInputUpdated: { _, _, _ in }, hideTopPanelUpdated: { _, _ in diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 01eef81f38..1f3e69607e 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -61,6 +61,12 @@ public final class EntityKeyboardComponent: Component { case masks } + public enum DisplayTopPanelBackground { + case none + case blur + case opaque + } + public struct GifSearchEmoji: Equatable { public var emoji: String public var file: TelegramMediaFile @@ -100,7 +106,7 @@ public final class EntityKeyboardComponent: Component { public let defaultToEmojiTab: Bool public let externalTopPanelContainer: PagerExternalTopPanelContainer? public let externalBottomPanelContainer: PagerExternalTopPanelContainer? - public let displayTopPanelBackground: Bool + public let displayTopPanelBackground: DisplayTopPanelBackground public let topPanelExtensionUpdated: (CGFloat, Transition) -> Void public let hideInputUpdated: (Bool, Bool, Transition) -> Void public let hideTopPanelUpdated: (Bool, Transition) -> Void @@ -132,7 +138,7 @@ public final class EntityKeyboardComponent: Component { defaultToEmojiTab: Bool, externalTopPanelContainer: PagerExternalTopPanelContainer?, externalBottomPanelContainer: PagerExternalTopPanelContainer?, - displayTopPanelBackground: Bool, + displayTopPanelBackground: DisplayTopPanelBackground, topPanelExtensionUpdated: @escaping (CGFloat, Transition) -> Void, hideInputUpdated: @escaping (Bool, Bool, Transition) -> Void, hideTopPanelUpdated: @escaping (Bool, Transition) -> Void, @@ -727,7 +733,7 @@ public final class EntityKeyboardComponent: Component { topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent( theme: component.theme, overflowHeight: component.hiddenInputHeight, - displayBackground: component.externalTopPanelContainer == nil && component.displayTopPanelBackground + displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground )), externalTopPanelContainer: component.externalTopPanelContainer, bottomPanel: component.displayBottomPanel ? AnyComponent(EntityKeyboardBottomPanelComponent( diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift index 5e2a9d9d36..0d65cbf0a2 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift @@ -38,12 +38,12 @@ final class EntityKeyboardTopContainerPanelComponent: Component { let theme: PresentationTheme let overflowHeight: CGFloat - let displayBackground: Bool + let displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground init( theme: PresentationTheme, overflowHeight: CGFloat, - displayBackground: Bool + displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground ) { self.theme = theme self.overflowHeight = overflowHeight @@ -193,7 +193,9 @@ final class EntityKeyboardTopContainerPanelComponent: Component { strongSelf.updateVisibilityFraction(value: fraction, transition: transition) } - if component.displayBackground { + if case .blur = component.displayBackground { + self.backgroundColor = nil + let backgroundView: BlurredBackgroundView if let current = self.backgroundView { backgroundView = current @@ -216,7 +218,9 @@ final class EntityKeyboardTopContainerPanelComponent: Component { backgroundSeparatorView.backgroundColor = component.theme.chat.inputPanel.panelSeparatorColor transition.setFrame(view: backgroundSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: height), size: CGSize(width: availableSize.width, height: UIScreenPixel))) - } else { + } else if case .none = component.displayBackground { + self.backgroundColor = nil + if let backgroundView = self.backgroundView { self.backgroundView = nil backgroundView.removeFromSuperview() @@ -225,6 +229,17 @@ final class EntityKeyboardTopContainerPanelComponent: Component { self.backgroundSeparatorView = nil backgroundSeparatorView.removeFromSuperview() } + } else if case .opaque = component.displayBackground { + if let backgroundView = self.backgroundView { + self.backgroundView = nil + backgroundView.removeFromSuperview() + } + if let backgroundSeparatorView = self.backgroundSeparatorView { + self.backgroundSeparatorView = nil + backgroundSeparatorView.removeFromSuperview() + } + + self.backgroundColor = component.theme.chat.inputMediaPanel.backgroundColor } return CGSize(width: availableSize.width, height: height) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index ead8df10df..ec1d0a397e 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -1814,8 +1814,8 @@ public final class EntityKeyboardTopPanelComponent: Component { let itemFrame = CGRect(origin: CGPoint(x: itemOuterFrame.minX + floor((itemOuterFrame.width - itemSize.width) / 2.0), y: itemOuterFrame.minY + floor((itemOuterFrame.height - itemSize.height) / 2.0)), size: itemSize) itemTransition.setFrame(view: itemView, frame: itemFrame) - transition.setSublayerTransform(view: itemView, transform: CATransform3DMakeScale(scale, scale, 1.0)) - transition.setAlpha(view: itemView, alpha: self.visibilityFraction) + itemTransition.setSublayerTransform(view: itemView, transform: CATransform3DMakeScale(scale, scale, 1.0)) + itemTransition.setAlpha(view: itemView, alpha: self.visibilityFraction) } } var removedIds: [AnyHashable] = [] diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 586ec28d1a..2fbbf956d0 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -408,7 +408,7 @@ private final class TopicIconSelectionComponent: Component { defaultToEmojiTab: true, externalTopPanelContainer: self.panelHostView, externalBottomPanelContainer: nil, - displayTopPanelBackground: true, + displayTopPanelBackground: .blur, topPanelExtensionUpdated: { _, _ in }, hideInputUpdated: { _, _, _ in }, hideTopPanelUpdated: { _, _ in }, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 4695e36b80..21fb84b000 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -8612,7 +8612,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if bannedMediaInput { - strongSelf.controllerInteraction?.displayUndo(.info(title: nil, text: strongSelf.restrictedSendingContentsText())) + strongSelf.controllerInteraction?.displayUndo(.universal(animation: "premium_unlock", scale: 1.0, colors: ["__allcolors__": UIColor(white: 1.0, alpha: 1.0)], title: nil, text: strongSelf.restrictedSendingContentsText(), customUndoText: nil)) return } @@ -8754,7 +8754,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch displayType { case .tooltip: if displayToast { - strongSelf.controllerInteraction?.displayUndo(.info(title: nil, text: banDescription)) + strongSelf.controllerInteraction?.displayUndo(.universal(animation: "premium_unlock", scale: 1.0, colors: ["__allcolors__": UIColor(white: 1.0, alpha: 1.0)], title: nil, text: banDescription, customUndoText: nil)) } else { var rect: CGRect? let isStickers: Bool = subject == .stickers @@ -8924,7 +8924,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if bannedMediaInput { - strongSelf.controllerInteraction?.displayUndo(.info(title: nil, text: strongSelf.restrictedSendingContentsText())) + strongSelf.controllerInteraction?.displayUndo(.universal(animation: "premium_unlock", scale: 1.0, colors: ["__allcolors__": UIColor(white: 1.0, alpha: 1.0)], title: nil, text: strongSelf.restrictedSendingContentsText(), customUndoText: nil)) return } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index b75cf9e60f..7ccefebcac 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -368,6 +368,8 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist } } } + } else if case let .replyThread(message) = chatLocation, message.isForumPost { + automaticDownloadPeerId = message.messageId.peerId } return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index ec011420a2..d856ff800d 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -933,7 +933,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { return } //TODO:localize - controller.controllerInteraction?.displayUndo(.info(title: nil, text: controller.restrictedSendingContentsText())) + controller.controllerInteraction?.displayUndo(.universal(animation: "premium_unlock", scale: 1.0, colors: ["__allcolors__": UIColor(white: 1.0, alpha: 1.0)], title: nil, text: controller.restrictedSendingContentsText(), customUndoText: nil)) } else { strongSelf.ensureFocused() } diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 0a4c01fa7d..b81dc5a47b 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -830,6 +830,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil + self.animationNode = AnimationNode(animation: animation, colors: colors, scale: scale) self.animatedStickerNode = nil if let title = title { diff --git a/versions.json b/versions.json index 0d05857c5a..f194d55736 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "9.3.3", + "app": "9.4", "bazel": "5.3.1", "xcode": "14.1" }