mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Reaction improvements
This commit is contained in:
parent
216ddc4a6e
commit
4803a3c6cc
@ -487,6 +487,12 @@ private func compressFrame(width: Int, height: Int, rgbData: Data) -> Data? {
|
||||
}
|
||||
|
||||
private final class AnimatedStickerDirectFrameSourceCache {
|
||||
private enum FrameRangeResult {
|
||||
case range(Range<Int>)
|
||||
case notFound
|
||||
case corruptedFile
|
||||
}
|
||||
|
||||
private let queue: Queue
|
||||
private let storeQueue: Queue
|
||||
private let file: ManagedFileImpl
|
||||
@ -548,27 +554,31 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
||||
}
|
||||
}
|
||||
|
||||
private func readFrameRange(index: Int) -> Range<Int>? {
|
||||
private func readFrameRange(index: Int) -> FrameRangeResult {
|
||||
if index < 0 || index >= self.frameCount {
|
||||
return nil
|
||||
return .notFound
|
||||
}
|
||||
|
||||
self.file.seek(position: Int64(index * 4 * 2))
|
||||
var offset: Int32 = 0
|
||||
var length: Int32 = 0
|
||||
if self.file.read(&offset, 4) != 4 {
|
||||
return nil
|
||||
return .corruptedFile
|
||||
}
|
||||
if self.file.read(&length, 4) != 4 {
|
||||
return nil
|
||||
return .corruptedFile
|
||||
}
|
||||
if length == 0 {
|
||||
return nil
|
||||
return .notFound
|
||||
}
|
||||
if length < 0 || offset < 0 {
|
||||
return nil
|
||||
return .corruptedFile
|
||||
}
|
||||
return (Int(offset) ..< Int(offset + length))
|
||||
if Int64(offset) + Int64(length) > 100 * 1024 * 1024 {
|
||||
return .corruptedFile
|
||||
}
|
||||
|
||||
return .range(Int(offset) ..< Int(offset + length))
|
||||
}
|
||||
|
||||
func storeUncompressedRgbFrame(index: Int, rgbData: Data) {
|
||||
@ -617,43 +627,52 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
||||
if index < 0 || index >= self.frameCount {
|
||||
return nil
|
||||
}
|
||||
guard let range = self.readFrameRange(index: index) else {
|
||||
return nil
|
||||
}
|
||||
self.file.seek(position: Int64(range.lowerBound))
|
||||
let length = range.upperBound - range.lowerBound
|
||||
let compressedData = self.file.readData(count: length)
|
||||
if compressedData.count != length {
|
||||
return nil
|
||||
}
|
||||
let rangeResult = self.readFrameRange(index: index)
|
||||
|
||||
var frameData: Data?
|
||||
|
||||
let decodeBufferLength = self.decodeBuffer.count
|
||||
|
||||
compressedData.withUnsafeBytes { buffer -> Void in
|
||||
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
switch rangeResult {
|
||||
case let .range(range):
|
||||
self.file.seek(position: Int64(range.lowerBound))
|
||||
let length = range.upperBound - range.lowerBound
|
||||
let compressedData = self.file.readData(count: length)
|
||||
if compressedData.count != length {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.scratchBuffer.withUnsafeMutableBytes { scratchBuffer -> Void in
|
||||
guard let scratchBytes = scratchBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
var frameData: Data?
|
||||
|
||||
let decodeBufferLength = self.decodeBuffer.count
|
||||
|
||||
compressedData.withUnsafeBytes { buffer -> Void in
|
||||
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.decodeBuffer.withUnsafeMutableBytes { decodeBuffer -> Void in
|
||||
guard let decodeBytes = decodeBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
|
||||
self.scratchBuffer.withUnsafeMutableBytes { scratchBuffer -> Void in
|
||||
guard let scratchBytes = scratchBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
}
|
||||
|
||||
let resultLength = compression_decode_buffer(decodeBytes, decodeBufferLength, bytes, length, UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE)
|
||||
|
||||
frameData = Data(bytes: decodeBytes, count: resultLength)
|
||||
self.decodeBuffer.withUnsafeMutableBytes { decodeBuffer -> Void in
|
||||
guard let decodeBytes = decodeBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return
|
||||
}
|
||||
|
||||
let resultLength = compression_decode_buffer(decodeBytes, decodeBufferLength, bytes, length, UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE)
|
||||
|
||||
frameData = Data(bytes: decodeBytes, count: resultLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frameData
|
||||
case .notFound:
|
||||
return nil
|
||||
case .corruptedFile:
|
||||
self.file.truncate(count: 0)
|
||||
self.initializeFrameTable()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return frameData
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,14 +78,14 @@ public final class JoinLinkPreviewController: ViewController {
|
||||
if let strongSelf = self {
|
||||
strongSelf.resolvedState = result
|
||||
switch result {
|
||||
case let .invite(flags, title, about, photoRepresentation, participantsCount, participants):
|
||||
if flags.requestNeeded {
|
||||
case let .invite(invite):
|
||||
if invite.flags.requestNeeded {
|
||||
strongSelf.isRequest = true
|
||||
strongSelf.isGroup = !flags.isBroadcast
|
||||
strongSelf.controllerNode.setRequestPeer(image: photoRepresentation, title: title, about: about, memberCount: participantsCount, isGroup: !flags.isBroadcast)
|
||||
strongSelf.isGroup = !invite.flags.isBroadcast
|
||||
strongSelf.controllerNode.setRequestPeer(image: invite.photoRepresentation, title: invite.title, about: invite.about, memberCount: invite.participantsCount, isGroup: !invite.flags.isBroadcast)
|
||||
} else {
|
||||
let data = JoinLinkPreviewData(isGroup: participants != nil, isJoined: false)
|
||||
strongSelf.controllerNode.setInvitePeer(image: photoRepresentation, title: title, memberCount: participantsCount, members: participants?.map({ EnginePeer($0) }) ?? [], data: data)
|
||||
let data = JoinLinkPreviewData(isGroup: invite.participants != nil, isJoined: false)
|
||||
strongSelf.controllerNode.setInvitePeer(image: invite.photoRepresentation, title: invite.title, memberCount: invite.participantsCount, members: invite.participants?.map({ $0 }) ?? [], data: data)
|
||||
}
|
||||
case let .alreadyJoined(peerId):
|
||||
strongSelf.navigateToPeer(peerId, nil)
|
||||
|
@ -185,11 +185,16 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||
|
||||
self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
|
||||
self.backgroundShadowLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.backgroundShadowLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
|
||||
}
|
||||
|
||||
func animateOut() {
|
||||
self.backgroundLayer.animateAlpha(from: CGFloat(self.backgroundLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.backgroundShadowLayer.animateAlpha(from: CGFloat(self.backgroundShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.largeCircleLayer.animateAlpha(from: CGFloat(self.largeCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.largeCircleShadowLayer.animateAlpha(from: CGFloat(self.largeCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.smallCircleLayer.animateAlpha(from: CGFloat(self.smallCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.smallCircleShadowLayer.animateAlpha(from: CGFloat(self.smallCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
@ -17,17 +17,20 @@ public final class ReactionContextItem {
|
||||
}
|
||||
|
||||
public let reaction: ReactionContextItem.Reaction
|
||||
public let appearAnimation: TelegramMediaFile
|
||||
public let stillAnimation: TelegramMediaFile
|
||||
public let listAnimation: TelegramMediaFile
|
||||
public let applicationAnimation: TelegramMediaFile
|
||||
|
||||
public init(
|
||||
reaction: ReactionContextItem.Reaction,
|
||||
appearAnimation: TelegramMediaFile,
|
||||
stillAnimation: TelegramMediaFile,
|
||||
listAnimation: TelegramMediaFile,
|
||||
applicationAnimation: TelegramMediaFile
|
||||
) {
|
||||
self.reaction = reaction
|
||||
self.appearAnimation = appearAnimation
|
||||
self.stillAnimation = stillAnimation
|
||||
self.listAnimation = listAnimation
|
||||
self.applicationAnimation = applicationAnimation
|
||||
|
@ -148,6 +148,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
standaloneReactionAnimation.animateReactionSelection(
|
||||
context: item.context, theme: item.theme, reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
appearAnimation: reaction.appearAnimation,
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
|
@ -2,14 +2,17 @@ import Foundation
|
||||
import Postbox
|
||||
|
||||
public final class AdMessageAttribute: MessageAttribute {
|
||||
public enum MessageTarget {
|
||||
case peer(id: EnginePeer.Id, message: EngineMessage.Id?, startParam: String?)
|
||||
case join(title: String, joinHash: String)
|
||||
}
|
||||
|
||||
public let opaqueId: Data
|
||||
public let startParam: String?
|
||||
public let messageId: MessageId?
|
||||
public let target: MessageTarget
|
||||
|
||||
public init(opaqueId: Data, startParam: String?, messageId: MessageId?) {
|
||||
public init(opaqueId: Data, target: MessageTarget) {
|
||||
self.opaqueId = opaqueId
|
||||
self.startParam = startParam
|
||||
self.messageId = messageId
|
||||
self.target = target
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
|
@ -234,6 +234,7 @@ func managedSynchronizeAvailableReactions(postbox: Postbox, network: Network) ->
|
||||
|
||||
for reaction in availableReactions.reactions {
|
||||
resources.append(reaction.staticIcon.resource)
|
||||
resources.append(reaction.appearAnimation.resource)
|
||||
resources.append(reaction.selectAnimation.resource)
|
||||
resources.append(reaction.activateAnimation.resource)
|
||||
resources.append(reaction.effectAnimation.resource)
|
||||
|
@ -22,19 +22,39 @@ private class AdMessagesHistoryContextImpl {
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case peer
|
||||
case invite
|
||||
}
|
||||
|
||||
struct Invite: Equatable, Codable {
|
||||
var title: String
|
||||
var joinHash: String
|
||||
}
|
||||
|
||||
case peer(PeerId)
|
||||
case invite(Invite)
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
|
||||
self = .peer(PeerId(peer))
|
||||
} else if let invite = try container.decodeIfPresent(Invite.self, forKey: .invite) {
|
||||
self = .invite(invite)
|
||||
} else {
|
||||
throw DecodingError.generic
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
try container.encode(peerId.toInt64(), forKey: .peer)
|
||||
case let .invite(invite):
|
||||
try container.encode(invite, forKey: .invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let opaqueId: Data
|
||||
@ -133,7 +153,14 @@ private class AdMessagesHistoryContextImpl {
|
||||
func toMessage(peerId: PeerId, transaction: Transaction) -> Message? {
|
||||
var attributes: [MessageAttribute] = []
|
||||
|
||||
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, startParam: self.startParam, messageId: self.messageId))
|
||||
let target: AdMessageAttribute.MessageTarget
|
||||
switch self.target {
|
||||
case let .peer(peerId):
|
||||
target = .peer(id: peerId, message: self.messageId, startParam: self.startParam)
|
||||
case let .invite(invite):
|
||||
target = .join(title: invite.title, joinHash: invite.joinHash)
|
||||
}
|
||||
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, target: target))
|
||||
if !self.textEntities.isEmpty {
|
||||
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
|
||||
attributes.append(attribute)
|
||||
@ -153,6 +180,23 @@ private class AdMessagesHistoryContextImpl {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .invite(invite):
|
||||
author = TelegramChannel(
|
||||
id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(1)),
|
||||
accessHash: nil,
|
||||
title: invite.title,
|
||||
username: nil,
|
||||
photo: [],
|
||||
creationDate: 0,
|
||||
version: 0,
|
||||
participationStatus: .left,
|
||||
info: .broadcast(TelegramChannelBroadcastInfo(flags: [])),
|
||||
flags: [],
|
||||
restrictionInfo: nil,
|
||||
adminRights: nil,
|
||||
bannedRights: nil,
|
||||
defaultBannedRights: nil
|
||||
)
|
||||
}
|
||||
|
||||
messagePeers[author.id] = author
|
||||
@ -368,6 +412,36 @@ private class AdMessagesHistoryContextImpl {
|
||||
var target: CachedMessage.Target?
|
||||
if let fromId = fromId {
|
||||
target = .peer(fromId.peerId)
|
||||
} else if let chatInvite = chatInvite, let chatInviteHash = chatInviteHash {
|
||||
switch chatInvite {
|
||||
case let .chatInvite(flags, title, _, photo, participantsCount, participants):
|
||||
let photo = telegramMediaImageFromApiPhoto(photo).flatMap({ smallestImageRepresentation($0.representations) })
|
||||
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0)
|
||||
|
||||
let _ = photo
|
||||
let _ = flags
|
||||
let _ = participantsCount
|
||||
let _ = participants
|
||||
|
||||
target = .invite(CachedMessage.Target.Invite(
|
||||
title: title,
|
||||
joinHash: chatInviteHash
|
||||
))
|
||||
case let .chatInvitePeek(chat, _):
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
target = .invite(CachedMessage.Target.Invite(
|
||||
title: peer.debugDisplayTitle,
|
||||
joinHash: chatInviteHash
|
||||
))
|
||||
}
|
||||
case let .chatInviteAlready(chat):
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
target = .invite(CachedMessage.Target.Invite(
|
||||
title: peer.debugDisplayTitle,
|
||||
joinHash: chatInviteHash
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var messageId: MessageId?
|
||||
|
@ -29,21 +29,30 @@ func apiUpdatesGroups(_ updates: Api.Updates) -> [Api.Chat] {
|
||||
}
|
||||
|
||||
public enum ExternalJoiningChatState {
|
||||
public struct InviteFlags : Equatable {
|
||||
public let isChannel: Bool
|
||||
public let isBroadcast: Bool
|
||||
public let isPublic: Bool
|
||||
public let isMegagroup: Bool
|
||||
public let requestNeeded: Bool
|
||||
public struct Invite: Equatable {
|
||||
public struct Flags: Equatable, Codable {
|
||||
public let isChannel: Bool
|
||||
public let isBroadcast: Bool
|
||||
public let isPublic: Bool
|
||||
public let isMegagroup: Bool
|
||||
public let requestNeeded: Bool
|
||||
}
|
||||
|
||||
public let flags: Flags
|
||||
public let title: String
|
||||
public let about: String?
|
||||
public let photoRepresentation: TelegramMediaImageRepresentation?
|
||||
public let participantsCount: Int32
|
||||
public let participants: [EnginePeer]?
|
||||
}
|
||||
|
||||
case invite(flags: InviteFlags, title: String, about: String?, photoRepresentation: TelegramMediaImageRepresentation?, participantsCount: Int32, participants: [Peer]?)
|
||||
case invite(Invite)
|
||||
case alreadyJoined(PeerId)
|
||||
case invalidHash
|
||||
case peek(PeerId, Int32)
|
||||
}
|
||||
|
||||
func _internal_joinChatInteractively(with hash: String, account: Account) -> Signal <PeerId?, JoinLinkError> {
|
||||
func _internal_joinChatInteractively(with hash: String, account: Account) -> Signal<PeerId?, JoinLinkError> {
|
||||
return account.network.request(Api.functions.messages.importChatInvite(hash: hash), automaticFloodWait: false)
|
||||
|> mapError { error -> JoinLinkError in
|
||||
switch error.errorDescription {
|
||||
@ -94,8 +103,8 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
||||
switch result {
|
||||
case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants):
|
||||
let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) })
|
||||
let flags:ExternalJoiningChatState.InviteFlags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0)
|
||||
return .single(.invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({TelegramUser(user: $0)})))
|
||||
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0)
|
||||
return .single(.invite(ExternalJoiningChatState.Invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({ EnginePeer(TelegramUser(user: $0)) }))))
|
||||
case let .chatInviteAlready(chat):
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in
|
||||
|
@ -1004,7 +1004,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
actions.context = strongSelf.context
|
||||
|
||||
if canAddMessageReactions(message: topMessage), let availableReactions = availableReactions, let allowedReactions = allowedReactions {
|
||||
filterReactions: for reaction in availableReactions.reactions {
|
||||
filterReactions: for reaction in availableReactions.reactions {
|
||||
switch allowedReactions {
|
||||
case let .set(set):
|
||||
if !set.contains(reaction.value) {
|
||||
@ -1015,6 +1015,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
actions.reactionItems.append(ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
appearAnimation: reaction.appearAnimation,
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
@ -1249,6 +1250,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
theme: strongSelf.presentationData.theme,
|
||||
reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
appearAnimation: reaction.appearAnimation,
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
@ -3193,6 +3195,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
])])
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}, openJoinLink: { [weak self] joinHash in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil)
|
||||
}, requestMessageUpdate: { [weak self] id in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
|
||||
|
@ -129,6 +129,7 @@ public final class ChatControllerInteraction {
|
||||
let updateChoosingSticker: (Bool) -> Void
|
||||
let commitEmojiInteraction: (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void
|
||||
let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void
|
||||
let openJoinLink: (String) -> Void
|
||||
|
||||
let requestMessageUpdate: (MessageId) -> Void
|
||||
let cancelInteractiveKeyboardGestures: () -> Void
|
||||
@ -227,6 +228,7 @@ public final class ChatControllerInteraction {
|
||||
updateChoosingSticker: @escaping (Bool) -> Void,
|
||||
commitEmojiInteraction: @escaping (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void,
|
||||
openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void,
|
||||
openJoinLink: @escaping (String) -> Void,
|
||||
requestMessageUpdate: @escaping (MessageId) -> Void,
|
||||
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||
@ -311,6 +313,7 @@ public final class ChatControllerInteraction {
|
||||
self.updateChoosingSticker = updateChoosingSticker
|
||||
self.commitEmojiInteraction = commitEmojiInteraction
|
||||
self.openLargeEmojiInfo = openLargeEmojiInfo
|
||||
self.openJoinLink = openJoinLink
|
||||
self.requestMessageUpdate = requestMessageUpdate
|
||||
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
||||
|
||||
@ -369,6 +372,7 @@ public final class ChatControllerInteraction {
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -245,7 +245,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
} else {
|
||||
if result.last?.1 == ChatMessageWebpageBubbleContentNode.self ||
|
||||
result.last?.1 == ChatMessagePollBubbleContentNode.self ||
|
||||
result.last?.1 == ChatMessageContactBubbleContentNode.self {
|
||||
result.last?.1 == ChatMessageContactBubbleContentNode.self ||
|
||||
result.last?.1 == ChatMessageGameBubbleContentNode.self ||
|
||||
result.last?.1 == ChatMessageInvoiceBubbleContentNode.self {
|
||||
result.append((firstMessage, ChatMessageReactionsFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
} else if result.last?.1 == ChatMessageCommentFooterContentNode.self {
|
||||
@ -254,11 +256,6 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
result[result.count - 2].1 == ChatMessageContactBubbleContentNode.self {
|
||||
result.insert((firstMessage, ChatMessageReactionsFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)), at: result.count - 1)
|
||||
}
|
||||
/*if result[result.count - 2].1 == ChatMessageTextBubbleContentNode.self {
|
||||
} else {
|
||||
result.insert((firstMessage, ChatMessageReactionsFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)), at: result.count - 1)
|
||||
needReactions = false
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,8 +301,15 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let trailingWidthToMeasure: CGFloat
|
||||
if textLayout.hasRTL {
|
||||
trailingWidthToMeasure = 10000.0
|
||||
} else {
|
||||
trailingWidthToMeasure = textLayout.trailingLineWidth
|
||||
}
|
||||
|
||||
let dateLayoutInput: ChatMessageDateAndStatusNode.LayoutInput
|
||||
dateLayoutInput = .trailingContent(contentWidth: textLayout.trailingLineWidth, reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: item.message), preferAdditionalInset: false))
|
||||
dateLayoutInput = .trailingContent(contentWidth: trailingWidthToMeasure, reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: item.message), preferAdditionalInset: false))
|
||||
|
||||
statusSuggestedWidthAndContinue = statusLayout(ChatMessageDateAndStatusNode.Arguments(
|
||||
context: item.context,
|
||||
|
@ -63,18 +63,23 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
self.contentNode.activateAction = { [weak self] in
|
||||
if let strongSelf = self, let item = strongSelf.item {
|
||||
if let adAttribute = item.message.adAttribute, let author = item.message.author {
|
||||
let navigationData: ChatControllerInteractionNavigateToPeer
|
||||
if let bot = author as? TelegramUser, bot.botInfo != nil, let startParam = adAttribute.startParam {
|
||||
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
|
||||
} else {
|
||||
var subject: ChatControllerSubject?
|
||||
if let messageId = adAttribute.messageId {
|
||||
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
|
||||
if let adAttribute = item.message.adAttribute {
|
||||
switch adAttribute.target {
|
||||
case let .peer(id, messageId, startParam):
|
||||
let navigationData: ChatControllerInteractionNavigateToPeer
|
||||
if let bot = item.message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam {
|
||||
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
|
||||
} else {
|
||||
var subject: ChatControllerSubject?
|
||||
if let messageId = messageId {
|
||||
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
|
||||
}
|
||||
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
|
||||
}
|
||||
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
|
||||
item.controllerInteraction.openPeer(id, navigationData, nil)
|
||||
case let .join(_, joinHash):
|
||||
item.controllerInteraction.openJoinLink(joinHash)
|
||||
}
|
||||
item.controllerInteraction.openPeer(author.id, navigationData, nil)
|
||||
} else {
|
||||
var webPageContent: TelegramMediaWebpageLoadedContent?
|
||||
for media in item.message.media {
|
||||
@ -342,13 +347,13 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if let author = item.message.author as? TelegramUser, author.botInfo != nil {
|
||||
actionTitle = item.presentationData.strings.Conversation_ViewBot
|
||||
} else if let author = item.message.author as? TelegramChannel, case .group = author.info {
|
||||
if adAttribute.messageId != nil {
|
||||
if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
|
||||
actionTitle = item.presentationData.strings.Conversation_ViewPost
|
||||
} else {
|
||||
actionTitle = item.presentationData.strings.Conversation_ViewGroup
|
||||
}
|
||||
} else {
|
||||
if adAttribute.messageId != nil {
|
||||
if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
|
||||
actionTitle = item.presentationData.strings.Conversation_ViewMessage
|
||||
} else {
|
||||
actionTitle = item.presentationData.strings.Conversation_ViewChannel
|
||||
|
@ -531,6 +531,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
|
@ -157,6 +157,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -149,6 +149,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
||||
|
@ -2241,6 +2241,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -1294,6 +1294,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
Loading…
x
Reference in New Issue
Block a user