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

This commit is contained in:
Ilya Laktyushin 2026-01-06 23:13:11 +04:00
commit 937eae7081
34 changed files with 250 additions and 154 deletions

View file

@ -27,9 +27,9 @@ build:swift_profile --local_cpu_resources=1
build:swift_profile --features=-swift.enable_batch_mode
build:swift_profile --experimental_ui_max_stdouterr_bytes=104857600
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
build:swift_profile --@build_bazel_rules_swift//swift:copt=-debug-time-function-bodies
build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-function-bodies=350
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
build:swift_profile --@build_bazel_rules_swift//swift:copt=-debug-time-expression-type-checking
build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-expression-type-checking=350
common:index_build --experimental_convenience_symlinks=ignore
common:index_build --bes_backend= --bes_results_url=

View file

@ -604,7 +604,7 @@ def remote_build_tart(macos_version, bazel_cache_host, configuration, build_inpu
else:
guest_build_sh += '--cacheHost="$CACHE_HOST" \\'
guest_build_sh += 'build \\'
guest_build_sh += '--lock \\'
#guest_build_sh += '--lock \\'
guest_build_sh += '--buildNumber={} \\'.format(build_number)
guest_build_sh += '--configuration={} \\'.format(configuration)
guest_build_sh += '--configurationPath=$HOME/telegram-build-input/configuration.json \\'

View file

@ -2545,13 +2545,25 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
guard let strongSelf, currentValues.contains(.setupPasskey) else {
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
let controller = strongSelf.context.sharedContext.makePasskeySetupController(context: strongSelf.context, displaySkip: true, navigationController: navigationController, completion: {
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone()
}, dismiss: {
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone()
})
navigationController.pushViewController(controller)
Task { @MainActor [weak strongSelf] in
guard let strongSelf else {
return
}
let passkeysData = await strongSelf.context.engine.auth.passkeysData().get()
if !passkeysData.isEmpty {
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
let controller = strongSelf.context.sharedContext.makePasskeySetupController(context: strongSelf.context, displaySkip: true, navigationController: navigationController, completion: {
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone()
}, dismiss: {
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone()
})
navigationController.pushViewController(controller)
}
}
})

View file

@ -788,6 +788,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let topInset = navigationBarHeight
var filtersInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: layout.insets(options: [.input]).bottom, right: 12.0)
if filtersInsets.bottom == 84.0 {
filtersInsets.bottom -= 6.0
}
if layout.insets(options: [.input]).bottom <= 30.0 {
filtersInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.insets(options: [.input]).bottom, innerDiameter: 40.0, sideInset: 32.0)
} else if layout.insets(options: [.input]).bottom <= 84.0 {

View file

@ -15,7 +15,7 @@ final class MutableDeletedMessagesView: MutablePostboxView {
for operation in operations {
switch operation {
case let .Remove(indices):
for (index, _) in indices {
for (index, _, _) in indices {
testMessageIds.append(index.id)
}
default:

View file

@ -32,7 +32,7 @@ final class MutableHistoryTagInfoView: MutablePostboxView {
}
case let .Remove(indicesAndTags):
if self.currentIndex != nil {
for (index, tags) in indicesAndTags {
for (index, tags, _) in indicesAndTags {
if tags.contains(self.tag) {
if index == self.currentIndex {
self.currentIndex = nil

View file

@ -2,7 +2,7 @@ import Foundation
enum MessageHistoryOperation {
case InsertMessage(IntermediateMessage)
case Remove([(MessageIndex, MessageTags)])
case Remove([(MessageIndex, MessageTags, Int64?)])
case UpdateReadState(PeerId, CombinedPeerReadState)
case UpdateEmbeddedMedia(MessageIndex, ReadBuffer)
case UpdateTimestamp(MessageIndex, Int32)

View file

@ -197,19 +197,19 @@ final class MessageHistoryTable: Table {
let buckets = self.continuousIndexIntervalsForRemoving(accumulatedRemoveIndices)
for bucket in buckets {
var indicesWithMetadata: [(MessageIndex, MessageTags)] = []
var indicesWithMetadata: [(MessageIndex, MessageTags, Int64?)] = []
var globalIndicesWithMetadata: [(GlobalMessageTags, MessageIndex)] = []
for index in bucket {
let tagsAndGlobalTags = self.justRemove(index, unsentMessageOperations: &unsentMessageOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
if let (tags, globalTags) = tagsAndGlobalTags {
indicesWithMetadata.append((index, tags))
if let (tags, globalTags, threadId) = tagsAndGlobalTags {
indicesWithMetadata.append((index, tags, threadId))
if !globalTags.isEmpty {
globalIndicesWithMetadata.append((globalTags, index))
}
} else {
indicesWithMetadata.append((index, MessageTags()))
indicesWithMetadata.append((index, MessageTags(), nil))
}
}
assert(bucket.count == indicesWithMetadata.count)
@ -352,8 +352,8 @@ final class MessageHistoryTable: Table {
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:]
if let (message, previousTags) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
outputOperations.append(.Remove([(storeMessage.index, previousTags)]))
if let (message, previousTags, previousThreadId) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
outputOperations.append(.Remove([(storeMessage.index, previousTags, previousThreadId)]))
outputOperations.append(.InsertMessage(message))
if !updatedGroupInfos.isEmpty {
outputOperations.append(.UpdateGroupInfos(updatedGroupInfos))
@ -367,8 +367,8 @@ final class MessageHistoryTable: Table {
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:]
if let (message, previousTags) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
outputOperations.append(.Remove([(index, previousTags)]))
if let (message, previousTags, previousThreadId) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
outputOperations.append(.Remove([(index, previousTags, previousThreadId)]))
outputOperations.append(.InsertMessage(message))
if !updatedGroupInfos.isEmpty {
outputOperations.append(.UpdateGroupInfos(updatedGroupInfos))
@ -944,7 +944,7 @@ final class MessageHistoryTable: Table {
self.storeIntermediateMessage(updatedMessage, sharedKey: self.key(MessageIndex.absoluteLowerBound()))
let operations: [MessageHistoryOperation] = [
.Remove([(index, message.tags)]),
.Remove([(index, message.tags, message.threadId)]),
.InsertMessage(updatedMessage)
]
if operationsByPeerId[message.id.peerId] == nil {
@ -1352,7 +1352,7 @@ final class MessageHistoryTable: Table {
return result
}
private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) -> (MessageTags, GlobalMessageTags)? {
private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) -> (MessageTags, GlobalMessageTags, Int64?)? {
let key = self.key(index)
if let value = self.valueBox.get(self.table, key: key) {
let resultTags: MessageTags
@ -1440,7 +1440,7 @@ final class MessageHistoryTable: Table {
resultGlobalTags = message.globalTags
self.valueBox.remove(self.table, key: key, secure: true)
return (resultTags, resultGlobalTags)
return (resultTags, resultGlobalTags, message.threadId)
} else {
return nil
}
@ -1544,7 +1544,7 @@ final class MessageHistoryTable: Table {
})
}
private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags)? {
private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags, Int64?)? {
if let previousMessage = self.getMessage(index) {
var mediaToUpdate: [Media] = []
@ -2037,7 +2037,7 @@ final class MessageHistoryTable: Table {
self.valueBox.set(self.table, key: self.key(message.index, key: sharedKey), value: sharedBuffer)
let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags)
let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags, previousMessage.threadId)
for media in mediaToUpdate {
if let id = media.id {

View file

@ -676,7 +676,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
}
}
case let .Remove(indicesAndTags):
for (index, _) in indicesAndTags {
for (index, _, _) in indicesAndTags {
if self.namespaces.contains(index.id.namespace) {
if loadedState.remove(index: index) {
hasChanges = true
@ -821,7 +821,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
}
case let .Remove(indices):
if !self.topTaggedMessages.isEmpty {
for (index, _) in indices {
for (index, _, _) in indices {
if let maybeCurrentTopMessage = self.topTaggedMessages[index.id.namespace], let currentTopMessage = maybeCurrentTopMessage, index.id == currentTopMessage.id {
let item: MessageHistoryTopTaggedMessage? = nil
self.topTaggedMessages[index.id.namespace] = item
@ -873,7 +873,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
break findOperation
}
case let .Remove(indices):
for (index, _) in indices {
for (index, _, _) in indices {
if currentIds.contains(index.id) {
updateMessage = true
break findOperation
@ -956,7 +956,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
break outer
}
case let .Remove(indicesWithTags):
for (index, _) in indicesWithTags {
for (index, _, _) in indicesWithTags {
if cachedData.messageIds.contains(index.id) {
updatedCachedPeerDataMessages = true
break outer

View file

@ -18,7 +18,7 @@ final class MutableMessageView {
case let .Remove(indices):
if let message = self.message {
let messageIndex = message.index
for (index, _) in indices {
for (index, _, _) in indices {
if index == messageIndex {
self.message = nil
updated = true

View file

@ -1,7 +1,7 @@
import Foundation
private struct PeerChatTopTaggedUpdateRecord: Equatable, Hashable {
let peerId: PeerId
let peerAndThreadId: PeerAndThreadId
let namespace: MessageId.Namespace
}
@ -10,71 +10,85 @@ final class PeerChatTopTaggedMessageIdsTable: Table {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
}
private var cachedTopIds: [PeerId: [MessageId.Namespace: MessageId?]] = [:]
private var cachedTopIds: [PeerAndThreadId: [MessageId.Namespace: MessageId?]] = [:]
private var updatedPeerIds = Set<PeerChatTopTaggedUpdateRecord>()
private let sharedKey = ValueBoxKey(length: 8 + 4)
private let sharedKeyNoThreadId = ValueBoxKey(length: 8 + 4)
private let sharedKeyWithThreadId = ValueBoxKey(length: 8 + 4 + 8)
private func key(peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
self.sharedKey.setInt64(0, value: peerId.toInt64())
self.sharedKey.setInt32(8, value: namespace)
return self.sharedKey
private func key(combinedId: PeerAndThreadId, namespace: MessageId.Namespace) -> ValueBoxKey {
if let threadId = combinedId.threadId {
self.sharedKeyWithThreadId.setInt64(0, value: combinedId.peerId.toInt64())
self.sharedKeyWithThreadId.setInt32(8, value: namespace)
self.sharedKeyWithThreadId.setInt64(8 + 4, value: threadId)
return self.sharedKeyWithThreadId
} else {
self.sharedKeyNoThreadId.setInt64(0, value: combinedId.peerId.toInt64())
self.sharedKeyNoThreadId.setInt32(8, value: namespace)
return self.sharedKeyNoThreadId
}
}
func get(peerId: PeerId, namespace: MessageId.Namespace) -> MessageId? {
if let cachedDict = self.cachedTopIds[peerId] {
func get(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) -> MessageId? {
let combinedId = PeerAndThreadId(peerId: peerId, threadId: threadId)
if let cachedDict = self.cachedTopIds[combinedId] {
if let maybeCachedId = cachedDict[namespace] {
return maybeCachedId
} else {
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, namespace: namespace)) {
if let value = self.valueBox.get(self.table, key: self.key(combinedId: combinedId, namespace: namespace)) {
var messageIdId: Int32 = 0
value.read(&messageIdId, offset: 0, length: 4)
self.cachedTopIds[peerId]![namespace] = MessageId(peerId: peerId, namespace: namespace, id: messageIdId)
self.cachedTopIds[combinedId]![namespace] = MessageId(peerId: peerId, namespace: namespace, id: messageIdId)
return MessageId(peerId: peerId, namespace: namespace, id: messageIdId)
} else {
let item: MessageId? = nil
self.cachedTopIds[peerId]![namespace] = item
self.cachedTopIds[combinedId]![namespace] = item
return nil
}
}
} else {
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, namespace: namespace)) {
if let value = self.valueBox.get(self.table, key: self.key(combinedId: combinedId, namespace: namespace)) {
var messageIdId: Int32 = 0
value.read(&messageIdId, offset: 0, length: 4)
self.cachedTopIds[peerId] = [namespace: MessageId(peerId: peerId, namespace: namespace, id: messageIdId)]
self.cachedTopIds[combinedId] = [namespace: MessageId(peerId: peerId, namespace: namespace, id: messageIdId)]
return MessageId(peerId: peerId, namespace: namespace, id: messageIdId)
} else {
let item: MessageId? = nil
self.cachedTopIds[peerId] = [namespace: item]
self.cachedTopIds[combinedId] = [namespace: item]
return nil
}
}
}
private func set(peerId: PeerId, namespace: MessageId.Namespace, id: MessageId?) {
if let _ = self.cachedTopIds[peerId] {
self.cachedTopIds[peerId]![namespace] = id
private func set(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, id: MessageId?) {
let combinedId = PeerAndThreadId(peerId: peerId, threadId: threadId)
if let _ = self.cachedTopIds[combinedId] {
self.cachedTopIds[combinedId]![namespace] = id
} else {
self.cachedTopIds[peerId] = [namespace: id]
self.cachedTopIds[combinedId] = [namespace: id]
}
self.updatedPeerIds.insert(PeerChatTopTaggedUpdateRecord(peerId: peerId, namespace: namespace))
self.updatedPeerIds.insert(PeerChatTopTaggedUpdateRecord(peerAndThreadId: combinedId, namespace: namespace))
}
func replay(historyOperationsByPeerId: [PeerId : [MessageHistoryOperation]]) {
func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]]) {
for (_, operations) in historyOperationsByPeerId {
for operation in operations {
switch operation {
case let .InsertMessage(message):
if message.flags.contains(.TopIndexable) {
let currentTopMessageId = self.get(peerId: message.id.peerId, namespace: message.id.namespace)
let currentTopMessageId = self.get(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace)
if currentTopMessageId == nil || currentTopMessageId! < message.id {
self.set(peerId: message.id.peerId, namespace: message.id.namespace, id: message.id)
self.set(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace, id: message.id)
}
}
case let .Remove(indices):
for (index, _) in indices {
if let messageId = self.get(peerId: index.id.peerId, namespace: index.id.namespace), index.id == messageId {
self.set(peerId: index.id.peerId, namespace: index.id.namespace, id: nil)
for (index, _, threadId) in indices {
if let messageId = self.get(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), index.id == messageId {
self.set(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, id: nil)
}
}
default:
@ -92,12 +106,12 @@ final class PeerChatTopTaggedMessageIdsTable: Table {
override func beforeCommit() {
if !self.updatedPeerIds.isEmpty {
for record in self.updatedPeerIds {
if let cachedDict = self.cachedTopIds[record.peerId], let maybeMessageId = cachedDict[record.namespace] {
if let cachedDict = self.cachedTopIds[record.peerAndThreadId], let maybeMessageId = cachedDict[record.namespace] {
if let maybeMessageId = maybeMessageId {
var messageIdId: Int32 = maybeMessageId.id
self.valueBox.set(self.table, key: self.key(peerId: record.peerId, namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false))
self.valueBox.set(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false))
} else {
self.valueBox.remove(self.table, key: self.key(peerId: record.peerId, namespace: record.namespace), secure: false)
self.valueBox.remove(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), secure: false)
}
}
}

View file

@ -247,7 +247,7 @@ final class MutablePeerView: MutablePostboxView {
break outer
}
case let .Remove(indicesWithTags):
for (index, _) in indicesWithTags {
for (index, _, _) in indicesWithTags {
if cachedData.messageIds.contains(index.id) {
updateMessages = true
break outer

View file

@ -3494,20 +3494,20 @@ final class PostboxImpl {
useRootInterfaceStateForThread: Bool
) -> Disposable {
var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:]
var mainPeerIdForTopTaggedMessages: PeerId?
switch peerIds {
var mainPeerIdForTopTaggedMessages: (peerId: PeerId, threadId: Int64?)?
if tag == nil {
switch peerIds {
case let .single(id, threadId):
if threadId == nil {
mainPeerIdForTopTaggedMessages = id
}
mainPeerIdForTopTaggedMessages = (id, threadId)
case let .associated(id, _):
mainPeerIdForTopTaggedMessages = id
mainPeerIdForTopTaggedMessages = (id, nil)
case .external:
mainPeerIdForTopTaggedMessages = nil
}
}
if let peerId = mainPeerIdForTopTaggedMessages {
for namespace in topTaggedMessageIdNamespaces {
if let messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId, namespace: namespace) {
if let messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId.peerId, threadId: peerId.threadId, namespace: namespace) {
if let index = self.messageHistoryIndexTable.getIndex(messageId) {
if let message = self.messageHistoryTable.getMessage(index) {
topTaggedMessages[namespace] = MessageHistoryTopTaggedMessage.intermediate(message)

View file

@ -371,7 +371,7 @@ final class TabBarControllerNode: ASDisplayNode {
}
}
return params.layout.size.height - tabBarFrame.minY - 6.0
return params.layout.size.height - tabBarFrame.minY
}
func frameForControllerTab(at index: Int) -> CGRect? {

View file

@ -2173,7 +2173,7 @@ public final class AccountViewTracker {
fixedCombinedReadStates: .peer([peerId: CombinedPeerReadState(states: [
(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: Int32.max - 1, maxOutgoingReadId: Int32.max - 1, maxKnownId: Int32.max - 1, count: 0, markedUnread: false))
])]),
topTaggedMessageIdNamespaces: [],
topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud],
tag: tag,
appendMessagesFromTheSameGroup: false,
namespaces: .not(Namespaces.Message.allNonRegular),
@ -2201,7 +2201,7 @@ public final class AccountViewTracker {
count: count,
trackHoles: trackHoles,
fixedCombinedReadStates: nil,
topTaggedMessageIdNamespaces: [],
topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud],
tag: tag,
appendMessagesFromTheSameGroup: false,
namespaces: .not(Namespaces.Message.allNonRegular),

View file

@ -479,6 +479,26 @@ private func _internal_clearHistory(transaction: Transaction, postbox: Postbox,
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
} else if let threadId = operation.threadId {
guard let inputPeer = apiInputPeer(peer) else {
return .complete()
}
return network.request(Api.functions.messages.deleteTopicHistory(peer: inputPeer, topMsgId: Int32(clamping: threadId)))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.AffectedHistory?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Void, NoError> in
if let result = result {
switch result {
case let .affectedHistory(pts, ptsCount, _):
stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)])
return .complete()
}
} else {
return .complete()
}
}
} else {
return requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: operation.topMessageId.id, justClear: true, minTimestamp: operation.minTimestamp, maxTimestamp: operation.maxTimestamp, type: operation.type)
}

View file

@ -429,7 +429,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme
panelBackgroundColor: mainBackgroundColor?.withAlphaComponent(0.9),
panelSeparatorColor: mainSeparatorColor,
panelControlAccentColor: accentColor,
panelControlColor: mainSecondaryTextColor?.withAlphaComponent(0.5),
panelControlColor: UIColor(rgb: 0xffffff),
inputBackgroundColor: inputBackgroundColor,
inputStrokeColor: accentColor?.withMultiplied(hue: 1.038, saturation: 0.463, brightness: 0.26),
inputPlaceholderColor: mainSecondaryTextColor?.withAlphaComponent(0.4),
@ -874,7 +874,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
panelBackgroundColorNoWallpaper: accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18),
panelSeparatorColor: mainSeparatorColor,
panelControlAccentColor: accentColor,
panelControlColor: mainSecondaryTextColor.withAlphaComponent(0.5),
panelControlColor: UIColor(rgb: 0xffffff),
panelControlDisabledColor: UIColor(rgb: 0x90979F, alpha: 0.5),
panelControlDestructiveColor: UIColor(rgb: 0xff6767),
inputBackgroundColor: inputBackgroundColor,

View file

@ -59,45 +59,50 @@ public enum ChatHistoryEntry: Identifiable, Comparable {
public var stableId: UInt64 {
switch self {
case let .MessageEntry(message, _, _, _, _, attributes):
let type: UInt64
switch attributes.contentTypeHint {
case .generic:
type = 2
case .largeEmoji:
type = 3
case .animatedEmoji:
type = 4
}
return UInt64(message.stableId) | ((type << 40))
case let .MessageGroupEntry(groupInfo, _, _):
return UInt64(bitPattern: groupInfo) | ((UInt64(2) << 40))
case .UnreadEntry:
return UInt64(4) << 40
case .ReplyCountEntry:
return UInt64(5) << 40
case .ChatInfoEntry:
case let .MessageEntry(message, _, _, _, _, attributes):
let type: UInt64
switch attributes.contentTypeHint {
case .generic:
type = 2
case .largeEmoji:
type = 3
case .animatedEmoji:
type = 4
}
return UInt64(message.stableId) | ((type << 40))
case let .MessageGroupEntry(groupInfo, _, _):
return UInt64(bitPattern: groupInfo) | ((UInt64(2) << 40))
case .UnreadEntry:
return UInt64(4) << 40
case .ReplyCountEntry:
return UInt64(5) << 40
case let .ChatInfoEntry(infoData, _):
switch infoData {
case .newThreadInfo:
return UInt64(7) << 40
default:
return UInt64(6) << 40
}
}
}
public var index: MessageIndex {
switch self {
case let .MessageEntry(message, _, _, _, _, _):
return message.index
case let .MessageGroupEntry(_, messages, _):
return messages[messages.count - 1].0.index
case let .UnreadEntry(index, _):
return index
case let .ReplyCountEntry(index, _, _, _):
return index
case let .ChatInfoEntry(infoData, _):
switch infoData {
case .newThreadInfo:
return MessageIndex.absoluteUpperBound()
default:
return MessageIndex.absoluteLowerBound()
}
case let .MessageEntry(message, _, _, _, _, _):
return message.index
case let .MessageGroupEntry(_, messages, _):
return messages[messages.count - 1].0.index
case let .UnreadEntry(index, _):
return index
case let .ReplyCountEntry(index, _, _, _):
return index
case let .ChatInfoEntry(infoData, _):
switch infoData {
case .newThreadInfo:
return MessageIndex.absoluteUpperBound()
default:
return MessageIndex.absoluteLowerBound()
}
}
}

View file

@ -254,6 +254,7 @@ public final class ChatTitleComponent: Component {
private let contentContainer: UIView
private let title = ComponentView<Empty>()
private var subtitleNode: ChatTitleActivityNode?
private var activityMeasureSubtitleNode: ChatTitleActivityNode?
private var leftIcon: ComponentView<Empty>?
private var rightIcon: ComponentView<Empty>?
private var credibilityIcon: ComponentView<Empty>?
@ -1003,9 +1004,25 @@ public final class ChatTitleComponent: Component {
let _ = subtitleNode.transitionToState(state, animation: transition.animation.isImmediate ? .none : .slide)
let subtitleSize = subtitleNode.updateLayout(CGSize(width: availableSize.width - containerSideInset * 2.0, height: 100.0), alignment: .center)
var minSubtitleWidth: CGFloat?
let activityMeasureSubtitleNode: ChatTitleActivityNode
if let current = self.activityMeasureSubtitleNode {
activityMeasureSubtitleNode = current
} else {
activityMeasureSubtitleNode = ChatTitleActivityNode()
self.activityMeasureSubtitleNode = activityMeasureSubtitleNode
}
let measureTypingTextString = NSAttributedString(string: component.strings.Conversation_typing, font: subtitleFont, textColor: .black)
let _ = activityMeasureSubtitleNode.transitionToState(.typingText(measureTypingTextString, .black), animation: .none)
let activityMeasureSubtitleSize = activityMeasureSubtitleNode.updateLayout(CGSize(width: availableSize.width - containerSideInset * 2.0, height: 100.0), alignment: .center)
minSubtitleWidth = activityMeasureSubtitleSize.width
var contentSize = titleSize
contentSize.width += titleLeftIconsWidth + titleRightIconsWidth
contentSize.width = max(contentSize.width, subtitleSize.width)
if let minSubtitleWidth {
contentSize.width = max(contentSize.width, minSubtitleWidth)
}
contentSize.height += subtitleSize.height
let containerSize = CGSize(width: contentSize.width + containerSideInset * 2.0, height: 44.0)

View file

@ -807,7 +807,7 @@ public extension GlassBackgroundView {
}
}
addShadow(context, true, CGPoint(), 30.0, 0.0, UIColor(white: 0.0, alpha: 0.075), .normal)
addShadow(context, true, CGPoint(), 30.0, 0.0, UIColor(white: 0.0, alpha: 0.045), .normal)
addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.01), .normal)
var a: CGFloat = 0.0

View file

@ -239,13 +239,17 @@ public final class HeaderPanelContainerComponent: Component {
self.panelViews.removeValue(forKey: key)
}
transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size))
self.backgroundContainer.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition)
let backgroundSize = CGSize(width: size.width, height: max(40.0, size.height))
let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: size.width - sideInset * 2.0, height: size.height))
transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: backgroundSize))
self.backgroundContainer.update(size: backgroundSize, isDark: component.theme.overallDarkAppearance, transition: transition)
let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: size.width - sideInset * 2.0, height: backgroundSize.height))
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: 20.0, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
transition.setAlpha(view: self.backgroundContainer, alpha: (component.tabs != nil || !component.panels.isEmpty) ? 1.0 : 0.0)
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
self.contentContainer.layer.cornerRadius = 20.0

View file

@ -182,8 +182,6 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro
self.addSubnode(self.chatController.displayNode)
self.chatController.displayNode.clipsToBounds = true
self.view.addSubview(self.coveringView)
self.chatController.stateUpdated = { [weak self] transition in
guard let self else {
return
@ -276,9 +274,9 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro
public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, navigationHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData)
let fullHeight = navigationHeight + size.height
let fullHeight = size.height - topInset
let chatFrame = CGRect(origin: CGPoint(x: 0.0, y: -navigationHeight), size: CGSize(width: size.width, height: fullHeight))
let chatFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: fullHeight))
if !self.chatController.displayNode.bounds.isEmpty {
if let contextController = self.chatController.visibleContextController as? ContextController {
@ -293,7 +291,7 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro
let combinedBottomInset = bottomInset
transition.updateFrame(node: self.chatController.displayNode, frame: chatFrame)
self.chatController.updateIsScrollingLockedAtTop(isScrollingLockedAtTop: isScrollingLockedAtTop)
self.chatController.containerLayoutUpdated(ContainerViewLayout(size: chatFrame.size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: nil), deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: topInset + navigationHeight, left: sideInset, bottom: combinedBottomInset, right: sideInset), safeInsets: UIEdgeInsets(top: navigationHeight + topInset + 4.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
self.chatController.containerLayoutUpdated(ContainerViewLayout(size: chatFrame.size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: nil), deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), safeInsets: UIEdgeInsets(top: 0.0 + 4.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View file

@ -823,7 +823,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
var scrollOffset: CGFloat = max(0.0, size.height - params.visibleHeight)
let effectiveBottomInset = max(buttonInsets.bottom, bottomInset)
var bottomPanelHeight = effectiveBottomInset + panelButtonSize.height + 8.0
let bottomPanelHeight = effectiveBottomInset + panelButtonSize.height + 8.0
if params.visibleHeight < 110.0 {
scrollOffset -= bottomPanelHeight
}
@ -838,8 +838,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
panelTransition.setFrame(view: panelContentContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - bottomPanelHeight), size: CGSize(width: size.width, height: bottomPanelHeight)))
if self.canManage {
bottomPanelHeight -= 9.0
let panelCheck: ComponentView<Empty>
if let current = self.panelCheck {
panelCheck = current
@ -904,7 +902,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
if panelCheckView.superview == nil {
panelContentContainer.addSubview(panelCheckView)
}
panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: 16.0), size: panelCheckSize)
panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: 16.0 + 16.0), size: panelCheckSize)
}
if let panelButtonView = panelButton.view {
panelButtonView.isHidden = true

View file

@ -4749,12 +4749,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
} else {
backgroundColor = presentationData.theme.list.blocksBackgroundColor
}
let _ = backgroundColor
if self.didUpdateItemsOnce {
/*if self.didUpdateItemsOnce {
ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor)
} else {
self.view.backgroundColor = backgroundColor
}
}*/
} else if case let .peer(_, _, isArchived) = self.scope, self.canManageStories, !isArchived, self.isProfileEmbedded, let items = self.items, items.items.isEmpty, items.count == 0 {
let emptyStateView: ComponentView<Empty>
var emptyStateTransition = ComponentTransition(transition)
@ -4852,7 +4853,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
}
}
if self.isProfileEmbedded, case .botPreview = self.scope {
/*if self.isProfileEmbedded, case .botPreview = self.scope {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
} else if self.isProfileEmbedded, case let .peer(_, _, isArchived) = self.scope, ((self.canManageStories && !isArchived) || !self.currentStoryFolders.isEmpty), self.isProfileEmbedded {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
@ -4860,7 +4861,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
} else {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
}
}*/
} else {
/*if self.isProfileEmbedded, case .botPreview = self.scope {
self.view.backgroundColor = presentationData.theme.list.blocksBackgroundColor

View file

@ -404,7 +404,7 @@ public final class PasskeysScreen: ViewControllerComponentContainer {
public init(context: AccountContext, displaySkip: Bool, initialPasskeysData: [TelegramPasskey]?, passkeysDataUpdated: @escaping ([TelegramPasskey]) -> Void, completion: @escaping () -> Void, cancel: @escaping () -> Void) {
self.context = context
super.init(context: context, component: PasskeysScreenComponent(context: context, displaySkip: displaySkip, initialPasskeysData: initialPasskeysData, passkeysDataUpdated: passkeysDataUpdated, completion: completion, cancel: cancel), navigationBarAppearance: .default)
super.init(context: context, component: PasskeysScreenComponent(context: context, displaySkip: displaySkip, initialPasskeysData: initialPasskeysData, passkeysDataUpdated: passkeysDataUpdated, completion: completion, cancel: cancel), navigationBarAppearance: .transparent)
}
required public init(coder aDecoder: NSCoder) {

View file

@ -357,7 +357,9 @@ extension ChatControllerImpl {
})))
}
items.append(.separator)
if !items.isEmpty {
items.append(.separator)
}
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] action in

View file

@ -37,6 +37,7 @@ func chatHistoryEntriesForView(
currentState: ChatHistoryEntriesForViewState,
context: AccountContext,
location: ChatLocation,
subject: ChatControllerSubject?,
view: MessageHistoryView,
includeUnreadEntry: Bool,
includeEmptyEntry: Bool,
@ -331,7 +332,10 @@ func chatHistoryEntriesForView(
}
}
var attributes = attributes
attributes.displayContinueThreadFooter = true
if let subject, case .pinnedMessages = subject {
} else {
attributes.displayContinueThreadFooter = true
}
entries[i] = .MessageEntry(message, presentationData, isRead, location, selection, attributes)
break outer
default:
@ -507,7 +511,12 @@ func chatHistoryEntriesForView(
entries.insert(.ChatInfoEntry(.botInfo(title: "", text: presentationData.strings.VerificationCodes_DescriptionText, photo: nil, video: nil), presentationData), at: 0)
} else if let cachedPeerData = cachedPeerData as? CachedUserData {
if let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
entries.insert(.ChatInfoEntry(.botInfo(title: presentationData.strings.Bot_DescriptionTitle, text: botInfo.description, photo: botInfo.photo, video: botInfo.video), presentationData), at: 0)
if location.threadId == nil {
if let subject, case .pinnedMessages = subject {
} else {
entries.insert(.ChatInfoEntry(.botInfo(title: presentationData.strings.Bot_DescriptionTitle, text: botInfo.description, photo: botInfo.photo, video: botInfo.video), presentationData), at: 0)
}
}
} else if let peerStatusSettings = cachedPeerData.peerStatusSettings, peerStatusSettings.registrationDate != nil || peerStatusSettings.phoneCountry != nil {
if peerStatusSettings.flags.contains(.canAddContact) || peerStatusSettings.flags.contains(.canReport) || peerStatusSettings.flags.contains(.canBlock) {
@ -681,7 +690,10 @@ func chatHistoryEntriesForView(
}
}
if addBotForumHeader {
entries.append(.ChatInfoEntry(.newThreadInfo, presentationData))
if let subject, case .pinnedMessages = subject {
} else {
entries.append(.ChatInfoEntry(.newThreadInfo, presentationData))
}
}
if includeEmbeddedSavedChatInfo, let peerId = location.peerId {
if !view.isLoading && view.laterId == nil {

View file

@ -2090,6 +2090,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
currentState: previousChatHistoryEntriesForViewState,
context: context,
location: chatLocation,
subject: subject,
view: view,
includeUnreadEntry: mode == .bubbles,
includeEmptyEntry: mode == .bubbles && tag == nil,
@ -2323,6 +2324,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
}
var keyboardButtonsMessage = view.topTaggedMessages.first
if keyboardButtonsMessage != nil && keyboardButtonsMessage?.threadId != chatLocation.threadId {
keyboardButtonsMessage = nil
}
if let keyboardButtonsMessageValue = keyboardButtonsMessage, keyboardButtonsMessageValue.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with({ $0 })) {
keyboardButtonsMessage = nil
}

View file

@ -20,21 +20,21 @@ enum ChatHistoryNavigationButtonType {
class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
let containerNode: ContextExtractedContentContainingNode
let buttonNode: HighlightTrackingButtonNode
private let backgroundView: GlassBackgroundView
let imageView: GlassBackgroundView.ContentImageView
private let badgeBackgroundView: GlassBackgroundView
private let badgeTextNode: ImmediateAnimatedCountLabelNode
private var tapRecognizer: UITapGestureRecognizer?
var tapped: (() -> Void)? {
didSet {
if (oldValue != nil) != (self.tapped != nil) {
if self.tapped != nil {
self.buttonNode.addTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside)
} else {
self.buttonNode.removeTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside)
}
}
self.tapRecognizer?.isEnabled = self.tapped != nil && self.isEnabled
}
}
var isEnabled: Bool = true {
didSet {
self.tapRecognizer?.isEnabled = self.tapped != nil && self.isEnabled
}
}
@ -54,7 +54,6 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
self.type = type
self.containerNode = ContextExtractedContentContainingNode()
self.buttonNode = HighlightTrackingButtonNode()
self.backgroundView = GlassBackgroundView()
@ -80,7 +79,10 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
super.init()
self.targetNodeForActivationProgress = self.buttonNode
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))
self.tapRecognizer = tapRecognizer
self.backgroundView.contentView.addGestureRecognizer(tapRecognizer)
tapRecognizer.isEnabled = false
self.addSubnode(self.containerNode)
@ -89,18 +91,16 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
self.containerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
self.containerNode.contentRect = CGRect(origin: CGPoint(), size: size)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
self.containerNode.contentNode.addSubnode(self.buttonNode)
self.containerNode.contentNode.view.addSubview(self.backgroundView)
self.buttonNode.view.addSubview(self.backgroundView)
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate)
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: .immediate)
self.imageView.tintColor = theme.chat.inputPanel.panelControlColor
self.backgroundView.contentView.addSubview(self.imageView)
self.imageView.frame = CGRect(origin: CGPoint(), size: size)
self.buttonNode.view.addSubview(self.badgeBackgroundView)
self.containerNode.contentNode.view.addSubview(self.badgeBackgroundView)
self.badgeBackgroundView.contentView.addSubview(self.badgeTextNode.view)
self.frame = CGRect(origin: CGPoint(), size: size)
@ -143,9 +143,11 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
self.absoluteRect = (rect, containerSize)
}
@objc func onTap() {
if let tapped = self.tapped {
tapped()
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
if let tapped = self.tapped {
tapped()
}
}
}

View file

@ -183,7 +183,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
if let down = self.directionButtonState.down {
self.downButton.imageView.alpha = down.isEnabled ? 1.0 : 0.5
self.downButton.buttonNode.isEnabled = down.isEnabled
self.downButton.isEnabled = down.isEnabled
mentionsOffset += buttonSize.height + 12.0
upOffset += buttonSize.height + 12.0
@ -203,7 +203,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
if let up = self.directionButtonState.up {
self.upButton.imageView.alpha = up.isEnabled ? 1.0 : 0.5
self.upButton.buttonNode.isEnabled = up.isEnabled
self.upButton.isEnabled = up.isEnabled
mentionsOffset += buttonSize.height + 12.0

View file

@ -69,6 +69,8 @@ swift_library(
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters",
],
visibility = [
"//visibility:public",

View file

@ -1044,7 +1044,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
public private(set) var isDark: Bool?
public var isDarkUpdated: (() -> Void)?
private func updateIsDark(_ isDark: Bool) {
private func updateIsDark(_ isDark: Bool?) {
if self.isDark != isDark {
self.isDark = isDark
self.isDarkUpdated?()
@ -1205,8 +1205,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
self.wallpaperDisposable.set(nil)
if case let .file(file) = wallpaper, file.isPattern {
let intensity = CGFloat(file.settings.intensity ?? 50) / 100.0
self.updateIsDark(intensity < 0)
self.updateIsDark(nil)
} else {
self.updateIsDark(calculateWallpaperBrightness(from: gradientColors) <= 0.3)
}

View file

@ -5,6 +5,8 @@ import Display
import GradientBackground
import EdgeEffect
import SwiftSignalKit
import ComponentFlow
import ComponentDisplayAdapters
final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode {
private struct Params: Equatable {
@ -116,6 +118,7 @@ final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode
return
}
self.contentNode.contents = parentNode.contentNode.contents
self.contentNode.backgroundColor = parentNode.contentNode.backgroundColor
self.contentNode.alpha = parentNode.contentNode.alpha
self.contentNode.isHidden = parentNode.contentNode.isHidden
}
@ -160,8 +163,8 @@ final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode
}
let maskFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: rect.size)
transition.updatePosition(layer: self.maskView.layer, position: maskFrame.center)
transition.updateBounds(layer: self.maskView.layer, bounds: CGRect(origin: CGPoint(), size: maskFrame.size))
ComponentTransition(transition).setPosition(view: self.maskView, position: maskFrame.center)
ComponentTransition(transition).setBounds(view: self.maskView, bounds: CGRect(origin: CGPoint(), size: maskFrame.size))
if case .top = edge.edge {
self.maskView.transform = CGAffineTransformMakeScale(1.0, -1.0)
} else {

View file

@ -1,6 +1,6 @@
{
"app": "12.3.1",
"xcode": "26.0",
"xcode": "26.2",
"bazel": "8.4.2:45e9388abf21d1107e146ea366ad080eb93cb6a5f3a4a3b048f78de0bc3faffa",
"macos": "26"
}