mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 03:21:29 +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 final class AnimatedStickerDirectFrameSourceCache {
|
||||||
|
private enum FrameRangeResult {
|
||||||
|
case range(Range<Int>)
|
||||||
|
case notFound
|
||||||
|
case corruptedFile
|
||||||
|
}
|
||||||
|
|
||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let storeQueue: Queue
|
private let storeQueue: Queue
|
||||||
private let file: ManagedFileImpl
|
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 {
|
if index < 0 || index >= self.frameCount {
|
||||||
return nil
|
return .notFound
|
||||||
}
|
}
|
||||||
|
|
||||||
self.file.seek(position: Int64(index * 4 * 2))
|
self.file.seek(position: Int64(index * 4 * 2))
|
||||||
var offset: Int32 = 0
|
var offset: Int32 = 0
|
||||||
var length: Int32 = 0
|
var length: Int32 = 0
|
||||||
if self.file.read(&offset, 4) != 4 {
|
if self.file.read(&offset, 4) != 4 {
|
||||||
return nil
|
return .corruptedFile
|
||||||
}
|
}
|
||||||
if self.file.read(&length, 4) != 4 {
|
if self.file.read(&length, 4) != 4 {
|
||||||
return nil
|
return .corruptedFile
|
||||||
}
|
}
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return nil
|
return .notFound
|
||||||
}
|
}
|
||||||
if length < 0 || offset < 0 {
|
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) {
|
func storeUncompressedRgbFrame(index: Int, rgbData: Data) {
|
||||||
@ -617,9 +627,10 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
|||||||
if index < 0 || index >= self.frameCount {
|
if index < 0 || index >= self.frameCount {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let range = self.readFrameRange(index: index) else {
|
let rangeResult = self.readFrameRange(index: index)
|
||||||
return nil
|
|
||||||
}
|
switch rangeResult {
|
||||||
|
case let .range(range):
|
||||||
self.file.seek(position: Int64(range.lowerBound))
|
self.file.seek(position: Int64(range.lowerBound))
|
||||||
let length = range.upperBound - range.lowerBound
|
let length = range.upperBound - range.lowerBound
|
||||||
let compressedData = self.file.readData(count: length)
|
let compressedData = self.file.readData(count: length)
|
||||||
@ -654,6 +665,14 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return frameData
|
return frameData
|
||||||
|
case .notFound:
|
||||||
|
return nil
|
||||||
|
case .corruptedFile:
|
||||||
|
self.file.truncate(count: 0)
|
||||||
|
self.initializeFrameTable()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -78,14 +78,14 @@ public final class JoinLinkPreviewController: ViewController {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.resolvedState = result
|
strongSelf.resolvedState = result
|
||||||
switch result {
|
switch result {
|
||||||
case let .invite(flags, title, about, photoRepresentation, participantsCount, participants):
|
case let .invite(invite):
|
||||||
if flags.requestNeeded {
|
if invite.flags.requestNeeded {
|
||||||
strongSelf.isRequest = true
|
strongSelf.isRequest = true
|
||||||
strongSelf.isGroup = !flags.isBroadcast
|
strongSelf.isGroup = !invite.flags.isBroadcast
|
||||||
strongSelf.controllerNode.setRequestPeer(image: photoRepresentation, title: title, about: about, memberCount: participantsCount, isGroup: !flags.isBroadcast)
|
strongSelf.controllerNode.setRequestPeer(image: invite.photoRepresentation, title: invite.title, about: invite.about, memberCount: invite.participantsCount, isGroup: !invite.flags.isBroadcast)
|
||||||
} else {
|
} else {
|
||||||
let data = JoinLinkPreviewData(isGroup: participants != nil, isJoined: false)
|
let data = JoinLinkPreviewData(isGroup: invite.participants != nil, isJoined: false)
|
||||||
strongSelf.controllerNode.setInvitePeer(image: photoRepresentation, title: title, memberCount: participantsCount, members: participants?.map({ EnginePeer($0) }) ?? [], data: data)
|
strongSelf.controllerNode.setInvitePeer(image: invite.photoRepresentation, title: invite.title, memberCount: invite.participantsCount, members: invite.participants?.map({ $0 }) ?? [], data: data)
|
||||||
}
|
}
|
||||||
case let .alreadyJoined(peerId):
|
case let .alreadyJoined(peerId):
|
||||||
strongSelf.navigateToPeer(peerId, nil)
|
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(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.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() {
|
func animateOut() {
|
||||||
self.backgroundLayer.animateAlpha(from: CGFloat(self.backgroundLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
|
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.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.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 reaction: ReactionContextItem.Reaction
|
||||||
|
public let appearAnimation: TelegramMediaFile
|
||||||
public let stillAnimation: TelegramMediaFile
|
public let stillAnimation: TelegramMediaFile
|
||||||
public let listAnimation: TelegramMediaFile
|
public let listAnimation: TelegramMediaFile
|
||||||
public let applicationAnimation: TelegramMediaFile
|
public let applicationAnimation: TelegramMediaFile
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
reaction: ReactionContextItem.Reaction,
|
reaction: ReactionContextItem.Reaction,
|
||||||
|
appearAnimation: TelegramMediaFile,
|
||||||
stillAnimation: TelegramMediaFile,
|
stillAnimation: TelegramMediaFile,
|
||||||
listAnimation: TelegramMediaFile,
|
listAnimation: TelegramMediaFile,
|
||||||
applicationAnimation: TelegramMediaFile
|
applicationAnimation: TelegramMediaFile
|
||||||
) {
|
) {
|
||||||
self.reaction = reaction
|
self.reaction = reaction
|
||||||
|
self.appearAnimation = appearAnimation
|
||||||
self.stillAnimation = stillAnimation
|
self.stillAnimation = stillAnimation
|
||||||
self.listAnimation = listAnimation
|
self.listAnimation = listAnimation
|
||||||
self.applicationAnimation = applicationAnimation
|
self.applicationAnimation = applicationAnimation
|
||||||
|
|||||||
@ -148,6 +148,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
|||||||
standaloneReactionAnimation.animateReactionSelection(
|
standaloneReactionAnimation.animateReactionSelection(
|
||||||
context: item.context, theme: item.theme, reaction: ReactionContextItem(
|
context: item.context, theme: item.theme, reaction: ReactionContextItem(
|
||||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||||
|
appearAnimation: reaction.appearAnimation,
|
||||||
stillAnimation: reaction.selectAnimation,
|
stillAnimation: reaction.selectAnimation,
|
||||||
listAnimation: reaction.activateAnimation,
|
listAnimation: reaction.activateAnimation,
|
||||||
applicationAnimation: reaction.effectAnimation
|
applicationAnimation: reaction.effectAnimation
|
||||||
|
|||||||
@ -2,14 +2,17 @@ import Foundation
|
|||||||
import Postbox
|
import Postbox
|
||||||
|
|
||||||
public final class AdMessageAttribute: MessageAttribute {
|
public final class AdMessageAttribute: MessageAttribute {
|
||||||
public let opaqueId: Data
|
public enum MessageTarget {
|
||||||
public let startParam: String?
|
case peer(id: EnginePeer.Id, message: EngineMessage.Id?, startParam: String?)
|
||||||
public let messageId: MessageId?
|
case join(title: String, joinHash: String)
|
||||||
|
}
|
||||||
|
|
||||||
public init(opaqueId: Data, startParam: String?, messageId: MessageId?) {
|
public let opaqueId: Data
|
||||||
|
public let target: MessageTarget
|
||||||
|
|
||||||
|
public init(opaqueId: Data, target: MessageTarget) {
|
||||||
self.opaqueId = opaqueId
|
self.opaqueId = opaqueId
|
||||||
self.startParam = startParam
|
self.target = target
|
||||||
self.messageId = messageId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
|
|||||||
@ -234,6 +234,7 @@ func managedSynchronizeAvailableReactions(postbox: Postbox, network: Network) ->
|
|||||||
|
|
||||||
for reaction in availableReactions.reactions {
|
for reaction in availableReactions.reactions {
|
||||||
resources.append(reaction.staticIcon.resource)
|
resources.append(reaction.staticIcon.resource)
|
||||||
|
resources.append(reaction.appearAnimation.resource)
|
||||||
resources.append(reaction.selectAnimation.resource)
|
resources.append(reaction.selectAnimation.resource)
|
||||||
resources.append(reaction.activateAnimation.resource)
|
resources.append(reaction.activateAnimation.resource)
|
||||||
resources.append(reaction.effectAnimation.resource)
|
resources.append(reaction.effectAnimation.resource)
|
||||||
|
|||||||
@ -22,19 +22,39 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case peer
|
case peer
|
||||||
|
case invite
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Invite: Equatable, Codable {
|
||||||
|
var title: String
|
||||||
|
var joinHash: String
|
||||||
}
|
}
|
||||||
|
|
||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
|
case invite(Invite)
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
|
if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
|
||||||
self = .peer(PeerId(peer))
|
self = .peer(PeerId(peer))
|
||||||
|
} else if let invite = try container.decodeIfPresent(Invite.self, forKey: .invite) {
|
||||||
|
self = .invite(invite)
|
||||||
} else {
|
} else {
|
||||||
throw DecodingError.generic
|
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
|
public let opaqueId: Data
|
||||||
@ -133,7 +153,14 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
func toMessage(peerId: PeerId, transaction: Transaction) -> Message? {
|
func toMessage(peerId: PeerId, transaction: Transaction) -> Message? {
|
||||||
var attributes: [MessageAttribute] = []
|
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 {
|
if !self.textEntities.isEmpty {
|
||||||
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
|
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
|
||||||
attributes.append(attribute)
|
attributes.append(attribute)
|
||||||
@ -153,6 +180,23 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
} else {
|
} else {
|
||||||
return nil
|
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
|
messagePeers[author.id] = author
|
||||||
@ -368,6 +412,36 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
var target: CachedMessage.Target?
|
var target: CachedMessage.Target?
|
||||||
if let fromId = fromId {
|
if let fromId = fromId {
|
||||||
target = .peer(fromId.peerId)
|
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?
|
var messageId: MessageId?
|
||||||
|
|||||||
@ -29,7 +29,8 @@ func apiUpdatesGroups(_ updates: Api.Updates) -> [Api.Chat] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ExternalJoiningChatState {
|
public enum ExternalJoiningChatState {
|
||||||
public struct InviteFlags : Equatable {
|
public struct Invite: Equatable {
|
||||||
|
public struct Flags: Equatable, Codable {
|
||||||
public let isChannel: Bool
|
public let isChannel: Bool
|
||||||
public let isBroadcast: Bool
|
public let isBroadcast: Bool
|
||||||
public let isPublic: Bool
|
public let isPublic: Bool
|
||||||
@ -37,13 +38,21 @@ public enum ExternalJoiningChatState {
|
|||||||
public let requestNeeded: Bool
|
public let requestNeeded: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
case invite(flags: InviteFlags, title: String, about: String?, photoRepresentation: TelegramMediaImageRepresentation?, participantsCount: Int32, participants: [Peer]?)
|
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(Invite)
|
||||||
case alreadyJoined(PeerId)
|
case alreadyJoined(PeerId)
|
||||||
case invalidHash
|
case invalidHash
|
||||||
case peek(PeerId, Int32)
|
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)
|
return account.network.request(Api.functions.messages.importChatInvite(hash: hash), automaticFloodWait: false)
|
||||||
|> mapError { error -> JoinLinkError in
|
|> mapError { error -> JoinLinkError in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
@ -94,8 +103,8 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
|||||||
switch result {
|
switch result {
|
||||||
case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants):
|
case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants):
|
||||||
let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) })
|
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)
|
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(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({TelegramUser(user: $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):
|
case let .chatInviteAlready(chat):
|
||||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||||
return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in
|
return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in
|
||||||
|
|||||||
@ -1015,6 +1015,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
actions.reactionItems.append(ReactionContextItem(
|
actions.reactionItems.append(ReactionContextItem(
|
||||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||||
|
appearAnimation: reaction.appearAnimation,
|
||||||
stillAnimation: reaction.selectAnimation,
|
stillAnimation: reaction.selectAnimation,
|
||||||
listAnimation: reaction.activateAnimation,
|
listAnimation: reaction.activateAnimation,
|
||||||
applicationAnimation: reaction.effectAnimation
|
applicationAnimation: reaction.effectAnimation
|
||||||
@ -1249,6 +1250,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
theme: strongSelf.presentationData.theme,
|
theme: strongSelf.presentationData.theme,
|
||||||
reaction: ReactionContextItem(
|
reaction: ReactionContextItem(
|
||||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||||
|
appearAnimation: reaction.appearAnimation,
|
||||||
stillAnimation: reaction.selectAnimation,
|
stillAnimation: reaction.selectAnimation,
|
||||||
listAnimation: reaction.activateAnimation,
|
listAnimation: reaction.activateAnimation,
|
||||||
applicationAnimation: reaction.effectAnimation
|
applicationAnimation: reaction.effectAnimation
|
||||||
@ -3193,6 +3195,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
])])
|
])])
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
strongSelf.present(actionSheet, in: .window(.root))
|
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
|
}, requestMessageUpdate: { [weak self] id in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
|
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
|
||||||
|
|||||||
@ -129,6 +129,7 @@ public final class ChatControllerInteraction {
|
|||||||
let updateChoosingSticker: (Bool) -> Void
|
let updateChoosingSticker: (Bool) -> Void
|
||||||
let commitEmojiInteraction: (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void
|
let commitEmojiInteraction: (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void
|
||||||
let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void
|
let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void
|
||||||
|
let openJoinLink: (String) -> Void
|
||||||
|
|
||||||
let requestMessageUpdate: (MessageId) -> Void
|
let requestMessageUpdate: (MessageId) -> Void
|
||||||
let cancelInteractiveKeyboardGestures: () -> Void
|
let cancelInteractiveKeyboardGestures: () -> Void
|
||||||
@ -227,6 +228,7 @@ public final class ChatControllerInteraction {
|
|||||||
updateChoosingSticker: @escaping (Bool) -> Void,
|
updateChoosingSticker: @escaping (Bool) -> Void,
|
||||||
commitEmojiInteraction: @escaping (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void,
|
commitEmojiInteraction: @escaping (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void,
|
||||||
openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void,
|
openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void,
|
||||||
|
openJoinLink: @escaping (String) -> Void,
|
||||||
requestMessageUpdate: @escaping (MessageId) -> Void,
|
requestMessageUpdate: @escaping (MessageId) -> Void,
|
||||||
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
||||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||||
@ -311,6 +313,7 @@ public final class ChatControllerInteraction {
|
|||||||
self.updateChoosingSticker = updateChoosingSticker
|
self.updateChoosingSticker = updateChoosingSticker
|
||||||
self.commitEmojiInteraction = commitEmojiInteraction
|
self.commitEmojiInteraction = commitEmojiInteraction
|
||||||
self.openLargeEmojiInfo = openLargeEmojiInfo
|
self.openLargeEmojiInfo = openLargeEmojiInfo
|
||||||
|
self.openJoinLink = openJoinLink
|
||||||
self.requestMessageUpdate = requestMessageUpdate
|
self.requestMessageUpdate = requestMessageUpdate
|
||||||
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
||||||
|
|
||||||
@ -369,6 +372,7 @@ public final class ChatControllerInteraction {
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
|
|||||||
@ -245,7 +245,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
} else {
|
} else {
|
||||||
if result.last?.1 == ChatMessageWebpageBubbleContentNode.self ||
|
if result.last?.1 == ChatMessageWebpageBubbleContentNode.self ||
|
||||||
result.last?.1 == ChatMessagePollBubbleContentNode.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)))
|
result.append((firstMessage, ChatMessageReactionsFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
needReactions = false
|
needReactions = false
|
||||||
} else if result.last?.1 == ChatMessageCommentFooterContentNode.self {
|
} else if result.last?.1 == ChatMessageCommentFooterContentNode.self {
|
||||||
@ -254,11 +256,6 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
result[result.count - 2].1 == ChatMessageContactBubbleContentNode.self {
|
result[result.count - 2].1 == ChatMessageContactBubbleContentNode.self {
|
||||||
result.insert((firstMessage, ChatMessageReactionsFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)), at: result.count - 1)
|
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
|
isReplyThread = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let trailingWidthToMeasure: CGFloat
|
||||||
|
if textLayout.hasRTL {
|
||||||
|
trailingWidthToMeasure = 10000.0
|
||||||
|
} else {
|
||||||
|
trailingWidthToMeasure = textLayout.trailingLineWidth
|
||||||
|
}
|
||||||
|
|
||||||
let dateLayoutInput: ChatMessageDateAndStatusNode.LayoutInput
|
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(
|
statusSuggestedWidthAndContinue = statusLayout(ChatMessageDateAndStatusNode.Arguments(
|
||||||
context: item.context,
|
context: item.context,
|
||||||
|
|||||||
@ -63,18 +63,23 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
self.contentNode.activateAction = { [weak self] in
|
self.contentNode.activateAction = { [weak self] in
|
||||||
if let strongSelf = self, let item = strongSelf.item {
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
if let adAttribute = item.message.adAttribute, let author = item.message.author {
|
if let adAttribute = item.message.adAttribute {
|
||||||
|
switch adAttribute.target {
|
||||||
|
case let .peer(id, messageId, startParam):
|
||||||
let navigationData: ChatControllerInteractionNavigateToPeer
|
let navigationData: ChatControllerInteractionNavigateToPeer
|
||||||
if let bot = author as? TelegramUser, bot.botInfo != nil, let startParam = adAttribute.startParam {
|
if let bot = item.message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam {
|
||||||
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
|
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
|
||||||
} else {
|
} else {
|
||||||
var subject: ChatControllerSubject?
|
var subject: ChatControllerSubject?
|
||||||
if let messageId = adAttribute.messageId {
|
if let messageId = messageId {
|
||||||
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
|
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(author.id, navigationData, nil)
|
item.controllerInteraction.openPeer(id, navigationData, nil)
|
||||||
|
case let .join(_, joinHash):
|
||||||
|
item.controllerInteraction.openJoinLink(joinHash)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var webPageContent: TelegramMediaWebpageLoadedContent?
|
var webPageContent: TelegramMediaWebpageLoadedContent?
|
||||||
for media in item.message.media {
|
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 {
|
if let author = item.message.author as? TelegramUser, author.botInfo != nil {
|
||||||
actionTitle = item.presentationData.strings.Conversation_ViewBot
|
actionTitle = item.presentationData.strings.Conversation_ViewBot
|
||||||
} else if let author = item.message.author as? TelegramChannel, case .group = author.info {
|
} 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
|
actionTitle = item.presentationData.strings.Conversation_ViewPost
|
||||||
} else {
|
} else {
|
||||||
actionTitle = item.presentationData.strings.Conversation_ViewGroup
|
actionTitle = item.presentationData.strings.Conversation_ViewGroup
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if adAttribute.messageId != nil {
|
if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
|
||||||
actionTitle = item.presentationData.strings.Conversation_ViewMessage
|
actionTitle = item.presentationData.strings.Conversation_ViewMessage
|
||||||
} else {
|
} else {
|
||||||
actionTitle = item.presentationData.strings.Conversation_ViewChannel
|
actionTitle = item.presentationData.strings.Conversation_ViewChannel
|
||||||
|
|||||||
@ -531,6 +531,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||||
|
|||||||
@ -157,6 +157,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
|
|||||||
@ -149,6 +149,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
||||||
|
|||||||
@ -2241,6 +2241,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
|
|||||||
@ -1294,6 +1294,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, updateChoosingSticker: { _ in
|
}, updateChoosingSticker: { _ in
|
||||||
}, commitEmojiInteraction: { _, _, _, _ in
|
}, commitEmojiInteraction: { _, _, _, _ in
|
||||||
}, openLargeEmojiInfo: { _, _, _ in
|
}, openLargeEmojiInfo: { _, _, _ in
|
||||||
|
}, openJoinLink: { _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user