Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2020-10-23 19:11:42 +04:00
commit 0457354220
20 changed files with 3291 additions and 3136 deletions

View File

@ -65,22 +65,9 @@
"PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@|posted a live location"; "PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@|posted a live location";
"PUSH_CHANNEL_MESSAGE_GIF" = "%1$@|posted a GIF"; "PUSH_CHANNEL_MESSAGE_GIF" = "%1$@|posted a GIF";
"PUSH_PINNED_TEXT" = "%1$@ pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@ pinned a message";
"PUSH_PINNED_PHOTO" = "%1$@ pinned a photo";
"PUSH_PINNED_VIDEO" = "%1$@ pinned a video";
"PUSH_PINNED_ROUND" = "%1$@ pinned a video message";
"PUSH_PINNED_DOC" = "%1$@ pinned a file";
"PUSH_PINNED_STICKER" = "%1$@ pinned a %2$@sticker";
"PUSH_PINNED_AUDIO" = "%1$@ pinned a voice message";
"PUSH_PINNED_GEO" = "%1$@ pinned a map";
"PUSH_PINNED_GEOLIVE" = "%1$@ pinned a live location";
"PUSH_PINNED_GIF" = "%1$@ pinned a GIF";
"PUSH_MESSAGE_GAME" = "%1$@|invited you to play %2$@"; "PUSH_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHANNEL_MESSAGE_GAME" = "%1$@|invited you to play %2$@"; "PUSH_CHANNEL_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@"; "PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@";
"PUSH_PINNED_GAME" = "%1$@ pinned a game";
"PUSH_MESSAGE_TEXT" = "%1$@|%2$@"; "PUSH_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_MESSAGE_NOTEXT" = "%1$@|sent you a message"; "PUSH_MESSAGE_NOTEXT" = "%1$@|sent you a message";
@ -1964,6 +1951,7 @@
"Message.PinnedStickerMessage" = "pinned sticker"; "Message.PinnedStickerMessage" = "pinned sticker";
"Message.PinnedLocationMessage" = "pinned location"; "Message.PinnedLocationMessage" = "pinned location";
"Message.PinnedContactMessage" = "pinned contact"; "Message.PinnedContactMessage" = "pinned contact";
"Message.PinnedGenericMessage" = "%@ pinned a message";
"Notification.PinnedMessage" = "pinned message"; "Notification.PinnedMessage" = "pinned message";
@ -5882,3 +5870,6 @@ Any member of this group will be able to see messages in the channel.";
"Chat.PinnedListPreview.ShowAllMessages" = "Show All Messages"; "Chat.PinnedListPreview.ShowAllMessages" = "Show All Messages";
"Chat.PinnedListPreview.UnpinAllMessages" = "Unpin All Messages"; "Chat.PinnedListPreview.UnpinAllMessages" = "Unpin All Messages";
"Chat.PinnedListPreview.HidePinnedMessages" = "Hide Pinned Messages"; "Chat.PinnedListPreview.HidePinnedMessages" = "Hide Pinned Messages";
"Conversation.PinMessagesForMe" = "Pin for me";
"Conversation.PinMessagesFor" = "Pin for me and %@";

View File

@ -149,6 +149,11 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
} }
if self.currentConfiguration != configuration { if self.currentConfiguration != configuration {
var isCycledJump = false
if let currentConfiguration = self.currentConfiguration, currentConfiguration.count == configuration.count, currentConfiguration.index == 0, currentConfiguration.count > 4, configuration.index == configuration.count - 1 {
isCycledJump = true
}
self.currentConfiguration = configuration self.currentConfiguration = configuration
let defaultVerticalInset: CGFloat = 7.0 let defaultVerticalInset: CGFloat = 7.0
@ -276,6 +281,19 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
} }
transition.updateFrame(node: self.foregroundLineNode, frame: CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset), size: CGSize(width: 2.0, height: segmentHeight)), beginWithCurrentState: true) transition.updateFrame(node: self.foregroundLineNode, frame: CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset), size: CGSize(width: 2.0, height: segmentHeight)), beginWithCurrentState: true)
if transition.isAnimated && isCycledJump {
let duration: Double = 0.18
let maxOffset: CGFloat = -8.0
let offsetAnimation0 = self.layer.makeAnimation(from: 0.0 as NSNumber, to: maxOffset as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, removeOnCompletion: false, additive: true, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
let offsetAnimation1 = strongSelf.layer.makeAnimation(from: maxOffset as NSNumber, to: 0.0 as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, additive: true)
strongSelf.layer.add(offsetAnimation1, forKey: "cycleShake")
})
self.layer.add(offsetAnimation0, forKey: "cycleShake")
}
} }
} }
} }

View File

@ -632,7 +632,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 3.0, y: 0.0), size: CGSize(width: 3.0, height: 3.0))) context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 3.0, y: 0.0), size: CGSize(width: 3.0, height: 3.0)))
context.fill(CGRect(x: 1.5, y: 0.0, width: size.width - 3.0, height: 3.0)) context.fill(CGRect(x: 1.5, y: 0.0, width: size.width - 3.0, height: 3.0))
context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0)) context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0))
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 2.5, bottom: 0.0, right: 2.5), resizingMode: .stretch) })?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 3.0, bottom: 0.0, right: 3.0), resizingMode: .stretch)
} }
if isReordering { if isReordering {

View File

@ -86,7 +86,17 @@ final class AccountManagerImpl {
for record in self.legacyRecordTable.getRecords() { for record in self.legacyRecordTable.getRecords() {
legacyRecordDict[record.id] = record legacyRecordDict[record.id] = record
} }
self.currentAtomicState = AccountManagerAtomicState(records: legacyRecordDict, currentRecordId: self.legacyMetadataTable.getCurrentAccountId(), currentAuthRecord: self.legacyMetadataTable.getCurrentAuthAccount()) self.currentAtomicState = AccountManagerAtomicState(records: legacyRecordDict, currentRecordId: self.legacyMetadataTable.getCurrentAccountId(), currentAuthRecord: self.legacyMetadataTable.getCurrentAuthAccount(), accessChallengeData: self.legacyMetadataTable.getAccessChallengeData())
self.syncAtomicStateToFile()
}
let tableAccessChallengeData = self.legacyMetadataTable.getAccessChallengeData()
if self.currentAtomicState.accessChallengeData != .none {
if tableAccessChallengeData == .none {
self.legacyMetadataTable.setAccessChallengeData(self.currentAtomicState.accessChallengeData)
}
} else if tableAccessChallengeData != .none {
self.currentAtomicState.accessChallengeData = tableAccessChallengeData
self.syncAtomicStateToFile() self.syncAtomicStateToFile()
} }
@ -165,7 +175,9 @@ final class AccountManagerImpl {
return self.legacyMetadataTable.getAccessChallengeData() return self.legacyMetadataTable.getAccessChallengeData()
}, setAccessChallengeData: { data in }, setAccessChallengeData: { data in
self.currentUpdatedAccessChallengeData = data self.currentUpdatedAccessChallengeData = data
self.currentAtomicStateUpdated = true
self.legacyMetadataTable.setAccessChallengeData(data) self.legacyMetadataTable.setAccessChallengeData(data)
self.currentAtomicState.accessChallengeData = data
}, getVersion: { }, getVersion: {
return self.legacyMetadataTable.getVersion() return self.legacyMetadataTable.getVersion()
}, setVersion: { version in }, setVersion: { version in

View File

@ -5,16 +5,19 @@ final class AccountManagerAtomicState: Codable {
case records case records
case currentRecordId case currentRecordId
case currentAuthRecord case currentAuthRecord
case accessChallengeData
} }
var records: [AccountRecordId: AccountRecord] var records: [AccountRecordId: AccountRecord]
var currentRecordId: AccountRecordId? var currentRecordId: AccountRecordId?
var currentAuthRecord: AuthAccountRecord? var currentAuthRecord: AuthAccountRecord?
var accessChallengeData: PostboxAccessChallengeData
init(records: [AccountRecordId: AccountRecord] = [:], currentRecordId: AccountRecordId? = nil, currentAuthRecord: AuthAccountRecord? = nil) { init(records: [AccountRecordId: AccountRecord] = [:], currentRecordId: AccountRecordId? = nil, currentAuthRecord: AuthAccountRecord? = nil, accessChallengeData: PostboxAccessChallengeData = .none) {
self.records = records self.records = records
self.currentRecordId = currentRecordId self.currentRecordId = currentRecordId
self.currentAuthRecord = currentAuthRecord self.currentAuthRecord = currentAuthRecord
self.accessChallengeData = accessChallengeData
} }
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
@ -34,6 +37,12 @@ final class AccountManagerAtomicState: Codable {
self.currentRecordId = try container.decodeIfPresent(AccountRecordId.self, forKey: .currentRecordId) self.currentRecordId = try container.decodeIfPresent(AccountRecordId.self, forKey: .currentRecordId)
} }
self.currentAuthRecord = try container.decodeIfPresent(AuthAccountRecord.self, forKey: .currentAuthRecord) self.currentAuthRecord = try container.decodeIfPresent(AuthAccountRecord.self, forKey: .currentAuthRecord)
if let accessChallengeData = try? container.decodeIfPresent(PostboxAccessChallengeData.self, forKey: .accessChallengeData) {
self.accessChallengeData = accessChallengeData
} else {
self.accessChallengeData = .none
}
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -43,5 +52,6 @@ final class AccountManagerAtomicState: Codable {
let currentRecordIdString: String? = self.currentRecordId.flatMap({ "\($0.rawValue)" }) let currentRecordIdString: String? = self.currentRecordId.flatMap({ "\($0.rawValue)" })
try container.encodeIfPresent(currentRecordIdString, forKey: .currentRecordId) try container.encodeIfPresent(currentRecordIdString, forKey: .currentRecordId)
try container.encodeIfPresent(self.currentAuthRecord, forKey: .currentAuthRecord) try container.encodeIfPresent(self.currentAuthRecord, forKey: .currentAuthRecord)
try container.encode(self.accessChallengeData, forKey: .accessChallengeData)
} }
} }

View File

@ -12,7 +12,12 @@ public struct AccessChallengeAttempts: Equatable {
} }
} }
public enum PostboxAccessChallengeData: PostboxCoding, Equatable { public enum PostboxAccessChallengeData: PostboxCoding, Equatable, Codable {
enum CodingKeys: String, CodingKey {
case numericalPassword
case plaintextPassword
}
case none case none
case numericalPassword(value: String) case numericalPassword(value: String)
case plaintextPassword(value: String) case plaintextPassword(value: String)
@ -44,6 +49,29 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
} }
} }
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? container.decode(String.self, forKey: .numericalPassword) {
self = .numericalPassword(value: value)
} else if let value = try? container.decode(String.self, forKey: .plaintextPassword) {
self = .plaintextPassword(value: value)
} else {
self = .none
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .none:
break
case let .numericalPassword(value):
try container.encode(value, forKey: .numericalPassword)
case let .plaintextPassword(value):
try container.encode(value, forKey: .plaintextPassword)
}
}
public var isLockable: Bool { public var isLockable: Bool {
if case .none = self { if case .none = self {
return false return false

View File

@ -11,7 +11,7 @@ public enum UpdatePinnedMessageError {
} }
public enum PinnedMessageUpdate { public enum PinnedMessageUpdate {
case pin(id: MessageId, silent: Bool) case pin(id: MessageId, silent: Bool, forThisPeerOnlyIfPossible: Bool)
case clear(id: MessageId) case clear(id: MessageId)
} }
@ -20,7 +20,6 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId)) return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId))
} }
|> mapError { _ -> UpdatePinnedMessageError in |> mapError { _ -> UpdatePinnedMessageError in
return .generic
} }
|> mapToSignal { peer, cachedPeerData -> Signal<Void, UpdatePinnedMessageError> in |> mapToSignal { peer, cachedPeerData -> Signal<Void, UpdatePinnedMessageError> in
guard let peer = peer, let inputPeer = apiInputPeer(peer) else { guard let peer = peer, let inputPeer = apiInputPeer(peer) else {
@ -52,11 +51,14 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
var flags: Int32 = 0 var flags: Int32 = 0
let messageId: Int32 let messageId: Int32
switch update { switch update {
case let .pin(id, silent): case let .pin(id, silent, forThisPeerOnlyIfPossible):
messageId = id.id messageId = id.id
if silent { if silent {
flags |= (1 << 0) flags |= (1 << 0)
} }
if forThisPeerOnlyIfPossible {
flags |= (1 << 2)
}
case let .clear(id): case let .clear(id):
messageId = id.id messageId = id.id
flags |= 1 << 1 flags |= 1 << 1
@ -77,7 +79,7 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
let messageId: MessageId let messageId: MessageId
switch update { switch update {
case let .pin(id, _): case let .pin(id, _, _):
messageId = id messageId = id
case let .clear(id): case let .clear(id):
messageId = id messageId = id
@ -103,7 +105,6 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
} }
} }
|> mapError { _ -> UpdatePinnedMessageError in |> mapError { _ -> UpdatePinnedMessageError in
return .generic
} }
} }
} }
@ -114,7 +115,6 @@ public func requestUnpinAllMessages(account: Account, peerId: PeerId) -> Signal<
return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId)) return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId))
} }
|> mapError { _ -> UpdatePinnedMessageError in |> mapError { _ -> UpdatePinnedMessageError in
return .generic
} }
|> mapToSignal { peer, cachedPeerData -> Signal<Never, UpdatePinnedMessageError> in |> mapToSignal { peer, cachedPeerData -> Signal<Never, UpdatePinnedMessageError> in
guard let peer = peer, let inputPeer = apiInputPeer(peer) else { guard let peer = peer, let inputPeer = apiInputPeer(peer) else {

View File

@ -217,7 +217,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
let textWithRanges: (String, [(Int, NSRange)]) let textWithRanges: (String, [(Int, NSRange)])
if clippedText.isEmpty { if clippedText.isEmpty {
textWithRanges = strings.PUSH_PINNED_NOTEXT(authorName) textWithRanges = strings.Message_PinnedGenericMessage(authorName)
} else { } else {
textWithRanges = strings.Notification_PinnedTextMessage(authorName, clippedText) textWithRanges = strings.Notification_PinnedTextMessage(authorName, clippedText)
} }
@ -250,7 +250,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedQuizMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedQuizMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
} }
case .deleted: case .deleted:
attributedString = addAttributesToStringWithRanges(strings.PUSH_PINNED_NOTEXT(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Message_PinnedGenericMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
} }
case .joinedByLink: case .joinedByLink:
attributedString = addAttributesToStringWithRanges(strings.Notification_JoinedGroupByLink(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_JoinedGroupByLink(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))

View File

@ -2230,7 +2230,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return items return items
} }
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: items, reactionItems: [], gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController) strongSelf.presentInGlobalOverlay(contextController)
}) })
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in }, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
@ -2387,7 +2387,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return items return items
} }
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: items, reactionItems: [], gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController) strongSelf.presentInGlobalOverlay(contextController)
} }
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)! chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)!
@ -3387,13 +3387,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> restartIfError |> restartIfError
} }
struct TopMessage {
var message: Message
var index: Int
}
let topMessage = pinnedHistorySignal(anchorMessageId: nil, count: 3) let topMessage = pinnedHistorySignal(anchorMessageId: nil, count: 3)
|> map { update -> Message? in |> map { update -> TopMessage? in
switch update { switch update {
case .Loading: case .Loading:
return nil return nil
case let .HistoryView(viewValue, _, _, _, _, _, _): case let .HistoryView(viewValue, _, _, _, _, _, _):
return viewValue.entries.last?.message if let entry = viewValue.entries.last {
let index: Int
if let location = entry.location {
index = location.index
} else {
index = viewValue.entries.count - 1
}
return TopMessage(
message: entry.message,
index: index
)
} else {
return nil
}
} }
} }
@ -3529,7 +3548,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if pinnedMessages.messages.isEmpty { if pinnedMessages.messages.isEmpty {
return nil return nil
} }
topMessageId = topMessage?.id ?? pinnedMessages.messages[pinnedMessages.messages.count - 1].message.id topMessageId = topMessage?.message.id ?? pinnedMessages.messages[pinnedMessages.messages.count - 1].message.id
if let referenceMessage = referenceMessage, referenceMessage.isScrolled, !pinnedMessages.messages.isEmpty, referenceMessage.id == pinnedMessages.messages[0].message.id, let topMessage = topMessage {
var index = topMessage.index
for message in pinnedMessages.messages {
if message.message.id == topMessage.message.id {
index = message.index
break
}
}
return ChatPinnedMessage(message: topMessage.message, index: index, totalCount: pinnedMessages.totalCount, topMessageId: topMessageId)
}
//print("reference: \(String(describing: referenceMessage?.id.id)) entries: \(view.entries.map(\.index.id.id))") //print("reference: \(String(describing: referenceMessage?.id.id)) entries: \(view.entries.map(\.index.id.id))")
for i in 0 ..< pinnedMessages.messages.count { for i in 0 ..< pinnedMessages.messages.count {
let entry = pinnedMessages.messages[i] let entry = pinnedMessages.messages[i]
@ -3646,7 +3678,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let pinnedMessageId = pinnedMessageId { if let pinnedMessageId = pinnedMessageId {
if let cachedDataMessages = combinedInitialData.cachedDataMessages { if let cachedDataMessages = combinedInitialData.cachedDataMessages {
if let message = cachedDataMessages[pinnedMessageId] { if let message = cachedDataMessages[pinnedMessageId] {
pinnedMessage = ChatPinnedMessage(message: message, index: 1, totalCount: 1, topMessageId: message.id) pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id)
} }
} }
} }
@ -3798,7 +3830,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if let pinnedMessageId = pinnedMessageId { if let pinnedMessageId = pinnedMessageId {
if let message = messages?[pinnedMessageId] { if let message = messages?[pinnedMessageId] {
pinnedMessage = ChatPinnedMessage(message: message, index: 1, totalCount: 1, topMessageId: message.id) pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id)
} }
} }
case .peer: case .peer:
@ -5080,29 +5112,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}, unblockPeer: { [weak self] in }, unblockPeer: { [weak self] in
self?.unblockPeer() self?.unblockPeer()
}, pinMessage: { [weak self] messageId in }, pinMessage: { [weak self] messageId, contextController in
if let strongSelf = self, case let .peer(currentPeerId) = strongSelf.chatLocation { if let strongSelf = self, case let .peer(currentPeerId) = strongSelf.chatLocation {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
var canManagePin = false if strongSelf.canManagePin() {
if let channel = peer as? TelegramChannel { let pinAction: (Bool, Bool) -> Void = { notify, forThisPeerOnlyIfPossible in
canManagePin = channel.hasPermission(.pinMessages)
} else if let group = peer as? TelegramGroup {
switch group.role {
case .creator, .admin:
canManagePin = true
default:
if let defaultBannedRights = group.defaultBannedRights {
canManagePin = !defaultBannedRights.flags.contains(.banPinMessages)
} else {
canManagePin = true
}
}
} else if let _ = peer as? TelegramUser, strongSelf.presentationInterfaceState.explicitelyCanPinMessages {
canManagePin = true
}
if canManagePin {
let pinAction: (Bool) -> Void = { notify in
if let strongSelf = self { if let strongSelf = self {
let disposable: MetaDisposable let disposable: MetaDisposable
if let current = strongSelf.unpinMessageDisposable { if let current = strongSelf.unpinMessageDisposable {
@ -5111,60 +5125,94 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
disposable = MetaDisposable() disposable = MetaDisposable()
strongSelf.unpinMessageDisposable = disposable strongSelf.unpinMessageDisposable = disposable
} }
disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: currentPeerId, update: .pin(id: messageId, silent: !notify)).start()) disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: currentPeerId, update: .pin(id: messageId, silent: !notify, forThisPeerOnlyIfPossible: forThisPeerOnlyIfPossible)).start())
} }
} }
var pinImmediately = false if let peer = peer as? TelegramUser, peer.id != strongSelf.context.account.peerId, let contextController = contextController {
if let channel = peer as? TelegramChannel, case .broadcast = channel.info { var contextItems: [ContextMenuItem] = []
pinImmediately = true
} else if let _ = peer as? TelegramUser {
pinImmediately = true
}
if pinImmediately {
pinAction(true)
} else {
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = strongSelf.topPinnedMessageSignal(latest: true)
|> take(1)
let _ = (topPinnedMessage contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessagesFor(peer.compactDisplayTitle).0, textColor: .primary, icon: { _ in nil }, action: { c, _ in
|> deliverOnMainQueue).start(next: { value in c.dismiss(completion: {
pinAction(true, false)
})
})))
contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessagesForMe, textColor: .primary, icon: { _ in nil }, action: { c, _ in
c.dismiss(completion: {
pinAction(true, true)
})
})))
contextController.setItems(.single(contextItems))
return
} else {
let continueAction: () -> Void = {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let title: String? var pinImmediately = false
let text: String if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
let actionLayout: TextAlertContentActionLayout pinImmediately = true
let actions: [TextAlertAction] } else if let _ = peer as? TelegramUser {
if let value = value, value.message.id > messageId { pinImmediately = true
title = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertTitle
text = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertText
actionLayout = .vertical
actions = [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlertPin, action: {
pinAction(false)
}),
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
})
]
} else {
title = nil
text = strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup
actionLayout = .horizontal
actions = [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: {
pinAction(false)
}),
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
pinAction(true)
})
]
} }
strongSelf.present(textAlertController(context: strongSelf.context, title: title, text: text, actions: actions, actionLayout: actionLayout), in: .window(.root)) if pinImmediately {
}) pinAction(true, false)
} else {
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = strongSelf.topPinnedMessageSignal(latest: true)
|> take(1)
let _ = (topPinnedMessage
|> deliverOnMainQueue).start(next: { value in
guard let strongSelf = self else {
return
}
let title: String?
let text: String
let actionLayout: TextAlertContentActionLayout
let actions: [TextAlertAction]
if let value = value, value.message.id > messageId {
title = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertTitle
text = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertText
actionLayout = .vertical
actions = [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlertPin, action: {
pinAction(false, false)
}),
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
})
]
} else {
title = nil
text = strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup
actionLayout = .horizontal
actions = [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: {
pinAction(false, false)
}),
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
pinAction(true, false)
})
]
}
strongSelf.present(textAlertController(context: strongSelf.context, title: title, text: text, actions: actions, actionLayout: actionLayout), in: .window(.root))
})
}
}
if let contextController = contextController {
contextController.dismiss(completion: {
continueAction()
})
} else {
continueAction()
}
} }
} else { } else {
if let topPinnedMessageId = strongSelf.presentationInterfaceState.pinnedMessage?.topMessageId { if let topPinnedMessageId = strongSelf.presentationInterfaceState.pinnedMessage?.topMessageId {
@ -5758,7 +5806,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .pinnedMessages(id: nil), botStart: nil, mode: .standard(previewing: true)) let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .pinnedMessages(id: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false) chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(items), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController) strongSelf.presentInGlobalOverlay(contextController)
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get())) }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get()))
@ -11016,11 +11064,12 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
let navigationController: NavigationController? = nil let navigationController: NavigationController? = nil
let passthroughTouches: Bool = false let passthroughTouches: Bool
init(controller: ViewController, sourceNode: ASDisplayNode?) { init(controller: ViewController, sourceNode: ASDisplayNode?, passthroughTouches: Bool) {
self.controller = controller self.controller = controller
self.sourceNode = sourceNode self.sourceNode = sourceNode
self.passthroughTouches = passthroughTouches
} }
func transitionInfo() -> ContextControllerTakeControllerInfo? { func transitionInfo() -> ContextControllerTakeControllerInfo? {

View File

@ -344,6 +344,16 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
canPin = false canPin = false
} }
if let peer = messages[0].peers[messages[0].id.peerId] {
if peer.isDeleted {
canPin = false
}
if !(peer is TelegramSecretChat) && messages[0].id.namespace != Namespaces.Message.Cloud {
canPin = false
canReply = false
}
}
var loadStickerSaveStatusSignal: Signal<Bool?, NoError> = .single(nil) var loadStickerSaveStatusSignal: Signal<Bool?, NoError> = .single(nil)
if loadStickerSaveStatus != nil { if loadStickerSaveStatus != nil {
loadStickerSaveStatusSignal = context.account.postbox.transaction { transaction -> Bool? in loadStickerSaveStatusSignal = context.account.postbox.transaction { transaction -> Bool? in
@ -682,9 +692,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
} else { } else {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { c, _ in
interfaceInteraction.pinMessage(messages[0].id) interfaceInteraction.pinMessage(messages[0].id, c)
f(.dismissWithoutContent)
}))) })))
} }
} }

View File

@ -60,7 +60,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func update(presentationData: ChatPresentationData, chatLocation: ChatLocation, message: Message, account: Account) -> CGSize { func update(presentationData: ChatPresentationData, chatLocation: ChatLocation, subject: ChatControllerSubject?, message: Message, account: Account) -> CGSize {
var isReplies = false var isReplies = false
var replyCount = 0 var replyCount = 0
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
@ -84,7 +84,9 @@ class ChatMessageShareButton: HighlightableButtonNode {
let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
var updatedShareButtonBackground: UIImage? var updatedShareButtonBackground: UIImage?
var updatedIconImage: UIImage? var updatedIconImage: UIImage?
if isReplies { if case .pinnedMessages = subject {
updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage
} else if isReplies {
updatedShareButtonBackground = PresentationResourcesChat.chatFreeCommentButtonBackground(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) updatedShareButtonBackground = PresentationResourcesChat.chatFreeCommentButtonBackground(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) { } else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
@ -607,7 +609,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
var needShareButton = false var needShareButton = false
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { if case .pinnedMessages = item.associatedData.subject {
needShareButton = true
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
needShareButton = false needShareButton = false
} else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
for attribute in item.content.firstMessage.attributes { for attribute in item.content.firstMessage.attributes {
@ -941,7 +945,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
strongSelf.addSubnode(updatedShareButtonNode) strongSelf.addSubnode(updatedShareButtonNode)
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
} }
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account) let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0), size: buttonSize) updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0), size: buttonSize)
} else if let shareButtonNode = strongSelf.shareButtonNode { } else if let shareButtonNode = strongSelf.shareButtonNode {
shareButtonNode.removeFromSupernode() shareButtonNode.removeFromSupernode()
@ -1266,6 +1270,11 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
@objc func shareButtonPressed() { @objc func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
return
}
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let _ = attribute as? ReplyThreadMessageAttribute { if let _ = attribute as? ReplyThreadMessageAttribute {

View File

@ -162,7 +162,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
if hasDiscussion { if hasDiscussion {
var canComment = false var canComment = false
if firstMessage.id.namespace == Namespaces.Message.Local { if case .pinnedMessages = item.associatedData.subject {
canComment = false
} else if firstMessage.id.namespace == Namespaces.Message.Local {
canComment = true canComment = true
} else { } else {
for attribute in firstMessage.attributes { for attribute in firstMessage.attributes {

View File

@ -216,7 +216,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
var needShareButton = false var needShareButton = false
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { if case .pinnedMessages = item.associatedData.subject {
needShareButton = true
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
needShareButton = false needShareButton = false
} }
else if item.message.id.peerId == item.context.account.peerId { else if item.message.id.peerId == item.context.account.peerId {
@ -473,7 +475,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
strongSelf.addSubnode(updatedShareButtonNode) strongSelf.addSubnode(updatedShareButtonNode)
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
} }
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account) let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: videoFrame.maxX - 7.0, y: videoFrame.maxY - 24.0 - buttonSize.height), size: buttonSize) updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: videoFrame.maxX - 7.0, y: videoFrame.maxY - 24.0 - buttonSize.height), size: buttonSize)
} else if let shareButtonNode = strongSelf.shareButtonNode { } else if let shareButtonNode = strongSelf.shareButtonNode {
shareButtonNode.removeFromSupernode() shareButtonNode.removeFromSupernode()
@ -745,6 +747,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
@objc func shareButtonPressed() { @objc func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
return
}
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let _ = attribute as? ReplyThreadMessageAttribute { if let _ = attribute as? ReplyThreadMessageAttribute {

View File

@ -279,7 +279,9 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
var needShareButton = false var needShareButton = false
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { if case .pinnedMessages = item.associatedData.subject {
needShareButton = true
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
needShareButton = false needShareButton = false
} else if item.message.id.peerId == item.context.account.peerId { } else if item.message.id.peerId == item.context.account.peerId {
for attribute in item.content.firstMessage.attributes { for attribute in item.content.firstMessage.attributes {
@ -568,7 +570,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
strongSelf.addSubnode(updatedShareButtonNode) strongSelf.addSubnode(updatedShareButtonNode)
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
} }
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account) let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
let shareButtonFrame = CGRect(origin: CGPoint(x: baseShareButtonFrame.minX, y: baseShareButtonFrame.maxY - buttonSize.height), size: buttonSize) let shareButtonFrame = CGRect(origin: CGPoint(x: baseShareButtonFrame.minX, y: baseShareButtonFrame.maxY - buttonSize.height), size: buttonSize)
transition.updateFrame(node: updatedShareButtonNode, frame: shareButtonFrame) transition.updateFrame(node: updatedShareButtonNode, frame: shareButtonFrame)
} else if let shareButtonNode = strongSelf.shareButtonNode { } else if let shareButtonNode = strongSelf.shareButtonNode {
@ -811,6 +813,11 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
@objc func shareButtonPressed() { @objc func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
return
}
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let _ = attribute as? ReplyThreadMessageAttribute { if let _ = attribute as? ReplyThreadMessageAttribute {

View File

@ -94,7 +94,7 @@ final class ChatPanelInterfaceInteraction {
let setupMessageAutoremoveTimeout: () -> Void let setupMessageAutoremoveTimeout: () -> Void
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let unblockPeer: () -> Void let unblockPeer: () -> Void
let pinMessage: (MessageId) -> Void let pinMessage: (MessageId, ContextController?) -> Void
let unpinMessage: (MessageId, Bool) -> Void let unpinMessage: (MessageId, Bool) -> Void
let unpinAllMessages: () -> Void let unpinAllMessages: () -> Void
let openPinnedList: (MessageId) -> Void let openPinnedList: (MessageId) -> Void
@ -173,7 +173,7 @@ final class ChatPanelInterfaceInteraction {
setupMessageAutoremoveTimeout: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void,
sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
unblockPeer: @escaping () -> Void, unblockPeer: @escaping () -> Void,
pinMessage: @escaping (MessageId) -> Void, pinMessage: @escaping (MessageId, ContextController?) -> Void,
unpinMessage: @escaping (MessageId, Bool) -> Void, unpinMessage: @escaping (MessageId, Bool) -> Void,
unpinAllMessages: @escaping () -> Void, unpinAllMessages: @escaping () -> Void,
openPinnedList: @escaping (MessageId) -> Void, openPinnedList: @escaping (MessageId) -> Void,

View File

@ -311,7 +311,13 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
var imageDimensions: CGSize? var imageDimensions: CGSize?
var titleStrings: [AnimatedCountLabelNode.Segment] = [] var titleStrings: [AnimatedCountLabelNode.Segment] = []
if pinnedMessage.totalCount > 1 { if pinnedMessage.totalCount == 2 {
if pinnedMessage.index == 0 {
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedPreviousMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
} else {
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
}
} else if pinnedMessage.totalCount > 1 {
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))

View File

@ -99,7 +99,7 @@ final class ChatRecentActionsController: TelegramBaseController {
}, sendSticker: { _, _, _ in }, sendSticker: { _, _, _ in
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _ in }, pinMessage: { _, _ in
}, unpinMessage: { _, _ in }, unpinMessage: { _, _ in
}, unpinAllMessages: { }, unpinAllMessages: {
}, openPinnedList: { _ in }, openPinnedList: { _ in

View File

@ -405,7 +405,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, sendSticker: { _, _, _ in }, sendSticker: { _, _, _ in
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _ in }, pinMessage: { _, _ in
}, unpinMessage: { _, _ in }, unpinMessage: { _, _ in
}, unpinAllMessages: { }, unpinAllMessages: {
}, openPinnedList: { _ in }, openPinnedList: { _ in