mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-16 16:21:11 +00:00
Fix monoforum user unread counters
This commit is contained in:
parent
8f7006281c
commit
3cad999c75
@ -14382,3 +14382,17 @@ Sorry for the inconvenience.";
|
||||
"ChannelMessages.SwitchTitle" = "Allow Channel Messages";
|
||||
"ChannelMessages.PriceSectionTitle" = "PRICE FOR EACH MESSAGE";
|
||||
"ChannelMessages.PriceSectionFooter" = "Charge users for the ability to suggest one post for your channel. You're not required to publish any suggestions by charging this. You'll receive 85% of the selected fee for each incoming suggestion.";
|
||||
|
||||
"ChatList.MonoforumLabel" = "MESSAGES";
|
||||
"ChatList.MonoforumEmptyText" = "No messages here yet...";
|
||||
|
||||
"Chat.InlineTopicMenu.Reorder" = "Reorder";
|
||||
"Chat.InlineTopicMenu.Edit" = "Edit";
|
||||
"Chat.InlineTopicMenu.AllTab" = "All";
|
||||
"Chat.ChannelMessagesHint" = "Tap here to send a message";
|
||||
"Chat.ChannelMessagesHintBadge" = "NEW";
|
||||
|
||||
"PeerInfo.AllowChannelMessages" = "Allow Channel Messages";
|
||||
"PeerInfo.AllowChannelMessages.On" = "On";
|
||||
"PeerInfo.AllowChannelMessages.Off" = "Off";
|
||||
"PeerInfo.ChannelMessages" = "Channel Messages";
|
||||
|
@ -632,8 +632,7 @@ public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, thr
|
||||
})))
|
||||
|
||||
if isPinned, let reorder {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_InlineTopicMenu_Reorder, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c?.dismiss(completion: {
|
||||
})
|
||||
reorder()
|
||||
@ -664,8 +663,7 @@ public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, thr
|
||||
}
|
||||
|
||||
if threadId != 1, canOpenClose, let customEdit {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { c, f in
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_InlineTopicMenu_Edit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { c, f in
|
||||
if let c = c as? ContextController {
|
||||
customEdit(c)
|
||||
} else {
|
||||
|
@ -3014,8 +3014,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
textColor = theme.titleColor
|
||||
}
|
||||
if case let .channel(channel) = itemPeer.peer, channel.flags.contains(.isMonoforum) {
|
||||
//TODO:localize
|
||||
titleBadgeText = "MESSAGES"
|
||||
titleBadgeText = item.presentationData.strings.ChatList_MonoforumLabel
|
||||
}
|
||||
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: textColor)
|
||||
}
|
||||
|
@ -185,7 +185,31 @@ final class ChatListIndexTable: Table {
|
||||
var updatedPeerTags: [PeerId: (previous: PeerSummaryCounterTags, updated: PeerSummaryCounterTags)] = [:]
|
||||
var updatedIsThreadBasedUnreadCountCalculation: [PeerId: Bool] = [:]
|
||||
|
||||
var upatedPeerMap: [PeerId: (Peer?, Peer)] = [:]
|
||||
|
||||
for (previous, updated) in updatedPeers {
|
||||
var needsAssociatedPeers = false
|
||||
if let previous, previous.0.associatedPeerId != nil {
|
||||
needsAssociatedPeers = true
|
||||
} else if updated.0.associatedPeerId != nil {
|
||||
needsAssociatedPeers = true
|
||||
}
|
||||
|
||||
if needsAssociatedPeers && upatedPeerMap.isEmpty {
|
||||
for (previous, updated) in updatedPeers {
|
||||
upatedPeerMap[updated.0.id] = (previous?.0, updated.0)
|
||||
}
|
||||
}
|
||||
|
||||
var previousAssociatedPeer: Peer?
|
||||
var updatedAssociatedPeer: Peer?
|
||||
if let previous, let previousAssociatedPeerId = previous.0.associatedPeerId {
|
||||
previousAssociatedPeer = upatedPeerMap[previousAssociatedPeerId]?.0 ?? postbox.peerTable.get(previousAssociatedPeerId)
|
||||
}
|
||||
if let updatedAssociatedPeerId = updated.0.associatedPeerId {
|
||||
updatedAssociatedPeer = upatedPeerMap[updatedAssociatedPeerId]?.1 ?? postbox.peerTable.get(updatedAssociatedPeerId)
|
||||
}
|
||||
|
||||
let previousTags: PeerSummaryCounterTags
|
||||
if let (previous, previousIsContact) = previous {
|
||||
previousTags = postbox.seedConfiguration.peerSummaryCounterTags(previous, previousIsContact)
|
||||
@ -198,13 +222,13 @@ final class ChatListIndexTable: Table {
|
||||
}
|
||||
|
||||
if let previous = previous {
|
||||
var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(updated.0).value
|
||||
var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(updated.0, updatedAssociatedPeer).value
|
||||
if let cachedData = updatedCachedPeerData[updated.0.id]?.1, postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) {
|
||||
isThreadBasedUnreadCalculation = false
|
||||
}
|
||||
|
||||
var wasThreadBasedUnreadCalculation = false
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(previous.0).value {
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(previous.0, previousAssociatedPeer).value {
|
||||
if let cachedData = postbox.cachedPeerDataTable.get(previous.0.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) {
|
||||
} else {
|
||||
wasThreadBasedUnreadCalculation = true
|
||||
@ -223,14 +247,15 @@ final class ChatListIndexTable: Table {
|
||||
guard let peer = postbox.peerTable.get(peerId) else {
|
||||
continue
|
||||
}
|
||||
let associatedPeer = peer.associatedPeerId.flatMap(postbox.peerTable.get)
|
||||
|
||||
var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||
var isThreadBasedUnreadCalculation = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, associatedPeer).value
|
||||
if postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedDataUpdate.1) {
|
||||
isThreadBasedUnreadCalculation = false
|
||||
}
|
||||
|
||||
var wasThreadBasedUnreadCalculation = false
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer, associatedPeer).value {
|
||||
if let previousCachedData = cachedDataUpdate.0, postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(previousCachedData) {
|
||||
} else {
|
||||
wasThreadBasedUnreadCalculation = true
|
||||
@ -439,7 +464,7 @@ final class ChatListIndexTable: Table {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value, !displayAsRegularChat {
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value, !displayAsRegularChat {
|
||||
let previousCount: Int32
|
||||
if let previousSummary = alteredInitialPeerThreadsSummaries[peerId] {
|
||||
previousCount = previousSummary.effectiveUnreadCount
|
||||
@ -458,7 +483,7 @@ final class ChatListIndexTable: Table {
|
||||
}
|
||||
|
||||
let currentReadState: CombinedPeerReadState?
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value, !displayAsRegularChat {
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value, !displayAsRegularChat {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
currentReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
@ -691,7 +716,7 @@ final class ChatListIndexTable: Table {
|
||||
}
|
||||
|
||||
let combinedState: CombinedPeerReadState?
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
@ -788,7 +813,7 @@ final class ChatListIndexTable: Table {
|
||||
}
|
||||
|
||||
let combinedState: CombinedPeerReadState?
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
|
@ -714,7 +714,7 @@ final class MutableChatListView {
|
||||
let renderedPeer = RenderedPeer(peerId: peer.id, peers: peers, associatedMedia: renderAssociatedMediaForPeers(postbox: postbox, peers: peers))
|
||||
|
||||
let isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
let hasUnmutedUnread = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.hasUnmutedUnread ?? false
|
||||
isUnread = hasUnmutedUnread
|
||||
} else {
|
||||
@ -846,7 +846,7 @@ final class MutableChatListView {
|
||||
displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData)
|
||||
}
|
||||
|
||||
if let peer = groupEntries[i].renderedPeers[j].peer.peer, postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value, !displayAsRegularChat {
|
||||
if let peer = groupEntries[i].renderedPeers[j].peer.peer, postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value, !displayAsRegularChat {
|
||||
isUnread = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.hasUnmutedUnread ?? false
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(groupEntries[i].renderedPeers[j].peer.peerId)?.isUnread ?? false
|
||||
@ -942,7 +942,7 @@ final class MutableChatListView {
|
||||
var isThreadBased = false
|
||||
var threadsArePeers = false
|
||||
if let peer = renderedPeer.peer {
|
||||
let value = postbox.seedConfiguration.peerSummaryIsThreadBased(peer)
|
||||
let value = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get))
|
||||
isThreadBased = value.value
|
||||
threadsArePeers = value.threadsArePeers
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -439,7 +439,7 @@ private final class ChatListViewSpaceState {
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -577,7 +577,7 @@ private final class ChatListViewSpaceState {
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer, entryPeer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -630,7 +630,7 @@ private final class ChatListViewSpaceState {
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer, mainPeer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -816,7 +816,7 @@ private final class ChatListViewSpaceState {
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer, entryPeer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -878,7 +878,7 @@ private final class ChatListViewSpaceState {
|
||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value
|
||||
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer, mainPeer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
if !isThreadBased {
|
||||
displayAsRegularChat = true
|
||||
}
|
||||
@ -978,7 +978,7 @@ private final class ChatListViewSpaceState {
|
||||
}
|
||||
let isThreadBased: Bool
|
||||
if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId) {
|
||||
isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||
isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value
|
||||
} else {
|
||||
isThreadBased = false
|
||||
}
|
||||
@ -1620,7 +1620,7 @@ struct ChatListViewState {
|
||||
var isThreadBased = false
|
||||
var threadsArePeers = false
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId) {
|
||||
let value = postbox.seedConfiguration.peerSummaryIsThreadBased(peer)
|
||||
let value = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get))
|
||||
isThreadBased = value.value
|
||||
threadsArePeers = value.threadsArePeers
|
||||
}
|
||||
|
@ -3187,7 +3187,6 @@ final class PostboxImpl {
|
||||
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal(userInteractive: true, { subscriber, transaction in
|
||||
let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: false)
|
||||
|
||||
//TODO:release jump to unread
|
||||
var anchor: HistoryViewInputAnchor = .upperBound
|
||||
switch peerIds {
|
||||
case let .single(peerId, threadId):
|
||||
|
@ -66,7 +66,7 @@ public final class SeedConfiguration {
|
||||
public let existingGlobalMessageTags: GlobalMessageTags
|
||||
public let peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace]
|
||||
public let peerSummaryCounterTags: (Peer, Bool) -> PeerSummaryCounterTags
|
||||
public let peerSummaryIsThreadBased: (Peer) -> (value: Bool, threadsArePeers: Bool)
|
||||
public let peerSummaryIsThreadBased: (Peer, Peer?) -> (value: Bool, threadsArePeers: Bool)
|
||||
public let additionalChatListIndexNamespace: MessageId.Namespace?
|
||||
public let messageNamespacesRequiringGroupStatsValidation: Set<MessageId.Namespace>
|
||||
public let defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState]
|
||||
@ -97,7 +97,7 @@ public final class SeedConfiguration {
|
||||
existingGlobalMessageTags: GlobalMessageTags,
|
||||
peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace],
|
||||
peerSummaryCounterTags: @escaping (Peer, Bool) -> PeerSummaryCounterTags,
|
||||
peerSummaryIsThreadBased: @escaping (Peer) -> (value: Bool, threadsArePeers: Bool),
|
||||
peerSummaryIsThreadBased: @escaping (Peer, Peer?) -> (value: Bool, threadsArePeers: Bool),
|
||||
additionalChatListIndexNamespace: MessageId.Namespace?,
|
||||
messageNamespacesRequiringGroupStatsValidation: Set<MessageId.Namespace>,
|
||||
defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState],
|
||||
|
@ -64,7 +64,7 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
case let .totalInGroup(groupId):
|
||||
return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId))
|
||||
case let .peer(peerId, handleThreads):
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
var count: Int32 = 0
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: peerId) {
|
||||
count = summary.totalUnreadCount
|
||||
@ -113,7 +113,7 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
}
|
||||
}
|
||||
case let .peer(peerId, handleThreads, _):
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
if transaction.updatedPeerThreadsSummaries.contains(peerId) {
|
||||
var count: Int32 = 0
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: peerId) {
|
||||
@ -143,7 +143,7 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
case let .totalInGroup(groupId):
|
||||
return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId))
|
||||
case let .peer(peerId, handleThreads):
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
var count: Int32 = 0
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: peerId) {
|
||||
count = summary.totalUnreadCount
|
||||
@ -240,7 +240,7 @@ final class MutableCombinedReadStateView: MutablePostboxView {
|
||||
var updated = false
|
||||
|
||||
if transaction.alteredInitialPeerCombinedReadStates[self.peerId] != nil || transaction.updatedPeerThreadCombinedStates.contains(self.peerId) {
|
||||
if self.handleThreads, let peer = postbox.peerTable.get(self.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if self.handleThreads, let peer = postbox.peerTable.get(self.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
var count: Int32 = 0
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: peerId) {
|
||||
count = summary.totalUnreadCount
|
||||
@ -260,14 +260,14 @@ final class MutableCombinedReadStateView: MutablePostboxView {
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
let state: CombinedPeerReadState?
|
||||
if handleThreads, let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value {
|
||||
if self.handleThreads, let peer = postbox.peerTable.get(self.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get)).value {
|
||||
var count: Int32 = 0
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: peerId) {
|
||||
if let summary = postbox.peerThreadsSummaryTable.get(peerId: self.peerId) {
|
||||
count = summary.totalUnreadCount
|
||||
}
|
||||
state = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 1, maxKnownId: 1, count: count, markedUnread: false))])
|
||||
} else {
|
||||
state = postbox.readStateTable.getCombinedState(peerId)
|
||||
state = postbox.readStateTable.getCombinedState(self.peerId)
|
||||
}
|
||||
|
||||
if state != self.state {
|
||||
|
@ -73,7 +73,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
||||
return .nonContact
|
||||
}
|
||||
},
|
||||
peerSummaryIsThreadBased: { peer in
|
||||
peerSummaryIsThreadBased: { peer, associatedPeer in
|
||||
if let channel = peer as? TelegramChannel {
|
||||
if channel.flags.contains(.isForum) {
|
||||
if channel.flags.contains(.displayForumAsTabs) {
|
||||
@ -82,6 +82,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
||||
return (true, false)
|
||||
}
|
||||
} else if channel.flags.contains(.isMonoforum) {
|
||||
if let associatedPeer = associatedPeer as? TelegramChannel, associatedPeer.hasPermission(.sendSomething) {
|
||||
return (true, true)
|
||||
} else {
|
||||
return (false, false)
|
||||
@ -89,6 +90,9 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
||||
} else {
|
||||
return (false, false)
|
||||
}
|
||||
} else {
|
||||
return (false, false)
|
||||
}
|
||||
},
|
||||
additionalChatListIndexNamespace: Namespaces.Message.Cloud,
|
||||
messageNamespacesRequiringGroupStatsValidation: [Namespaces.Message.Cloud],
|
||||
|
@ -140,8 +140,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
} else {
|
||||
if forChatList {
|
||||
if isMonoforum {
|
||||
//TODO:Localize
|
||||
attributedString = NSAttributedString(string: "No messages here yet...", font: titleFont, textColor: primaryTextColor)
|
||||
attributedString = NSAttributedString(string: strings.ChatList_MonoforumEmptyText, font: titleFont, textColor: primaryTextColor)
|
||||
} else {
|
||||
attributedString = NSAttributedString(string: strings.Notification_CreatedGroup, font: titleFont, textColor: primaryTextColor)
|
||||
}
|
||||
|
@ -526,14 +526,29 @@ public final class AsyncListComponent: Component {
|
||||
|
||||
var scrollToItem: ListViewScrollToItem?
|
||||
if let resetScrollingRequest = component.externalStateValue.resetScrollingRequest, previousComponent?.externalStateValue.resetScrollingRequest != component.externalStateValue.resetScrollingRequest {
|
||||
//TODO:release calculate direction hint
|
||||
if let index = entries.firstIndex(where: { $0.id == resetScrollingRequest.id }) {
|
||||
var directionHint: ListViewScrollToItemDirectionHint = .Down
|
||||
var didSelectDirection = false
|
||||
self.listNode.forEachItemNode { itemNode in
|
||||
if didSelectDirection {
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode as? ListItemNodeImpl, let itemIndex = itemNode.index {
|
||||
if itemIndex <= index {
|
||||
directionHint = .Up
|
||||
} else {
|
||||
directionHint = .Down
|
||||
}
|
||||
didSelectDirection = true
|
||||
}
|
||||
}
|
||||
|
||||
scrollToItem = ListViewScrollToItem(
|
||||
index: index,
|
||||
position: .visible,
|
||||
animated: animateTransition,
|
||||
curve: updateSizeAndInsets.curve,
|
||||
directionHint: .Down
|
||||
directionHint: directionHint
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -548,13 +563,25 @@ public final class AsyncListComponent: Component {
|
||||
|
||||
transactionOptions.insert(.Synchronous)
|
||||
|
||||
self.listNode.transaction(
|
||||
deleteIndices: [],
|
||||
insertIndicesAndItems: [],
|
||||
updateIndicesAndItems: [],
|
||||
options: transactionOptions,
|
||||
scrollToItem: nil,
|
||||
updateSizeAndInsets: updateSizeAndInsets,
|
||||
stationaryItemRange: nil,
|
||||
updateOpaqueState: nil,
|
||||
completion: { _ in }
|
||||
)
|
||||
|
||||
self.listNode.transaction(
|
||||
deleteIndices: deletions,
|
||||
insertIndicesAndItems: insertions,
|
||||
updateIndicesAndItems: updates,
|
||||
options: transactionOptions,
|
||||
scrollToItem: scrollToItem,
|
||||
updateSizeAndInsets: updateSizeAndInsets,
|
||||
updateSizeAndInsets: nil,
|
||||
stationaryItemRange: nil,
|
||||
updateOpaqueState: nil,
|
||||
completion: { _ in }
|
||||
|
@ -381,14 +381,13 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let _ = presentationData
|
||||
//TODO:localize
|
||||
let text: String = "Tap here to send a message"
|
||||
let text: String = presentationData.strings.Chat_ChannelMessagesHint
|
||||
|
||||
let tooltipController = TooltipScreen(
|
||||
account: context.account,
|
||||
sharedContext: context.sharedContext,
|
||||
text: .plain(text: text),
|
||||
textBadge: "NEW",
|
||||
textBadge: presentationData.strings.Chat_ChannelMessagesHintBadge,
|
||||
balancedTextLayout: false,
|
||||
style: .wide,
|
||||
arrowStyle: .small,
|
||||
|
@ -619,8 +619,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
private let shadowNode: ChatMessageShadowNode
|
||||
private var clippingNode: ChatMessageBubbleClippingNode
|
||||
|
||||
private var suggestedPostInfoNode: ChatMessageSuggestedPostInfoNode?
|
||||
|
||||
override public var extractedBackgroundNode: ASDisplayNode? {
|
||||
return self.shadowNode
|
||||
}
|
||||
@ -1427,8 +1425,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
|
||||
let weakSelf = Weak(self)
|
||||
|
||||
let makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout = ChatMessageSuggestedPostInfoNode.asyncLayout(self.suggestedPostInfoNode)
|
||||
|
||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
||||
return ChatMessageBubbleItemNode.beginLayout(
|
||||
@ -1451,7 +1447,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
unlockButtonLayout: unlockButtonLayout,
|
||||
mediaInfoLayout: mediaInfoLayout,
|
||||
mosaicStatusLayout: mosaicStatusLayout,
|
||||
makeSuggestedPostInfoNodeLayout: makeSuggestedPostInfoNodeLayout,
|
||||
layoutConstants: layoutConstants,
|
||||
currentItem: currentItem,
|
||||
currentForwardInfo: currentForwardInfo,
|
||||
@ -1480,7 +1475,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
unlockButtonLayout: (ChatMessageUnlockMediaNode.Arguments) -> (CGSize, (Bool) -> ChatMessageUnlockMediaNode),
|
||||
mediaInfoLayout: (ChatMessageStarsMediaInfoNode.Arguments) -> (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode),
|
||||
mosaicStatusLayout: (ChatMessageDateAndStatusNode.Arguments) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode)),
|
||||
makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout,
|
||||
layoutConstants: ChatMessageItemLayoutConstants,
|
||||
currentItem: ChatMessageItem?,
|
||||
currentForwardInfo: (Peer?, String?)?,
|
||||
@ -3000,7 +2994,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
var totalContentNodesHeight: CGFloat = 0.0
|
||||
var currentContainerGroupOverlap: CGFloat = 0.0
|
||||
var detachedContentNodesHeight: CGFloat = 0.0
|
||||
var additionalTopHeight: CGFloat = 0.0
|
||||
let additionalTopHeight: CGFloat = 0.0
|
||||
|
||||
var mosaicStatusOrigin: CGPoint?
|
||||
var unlockButtonPosition: CGPoint?
|
||||
@ -3177,13 +3171,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
reactionButtonsSizeAndApply = reactionButtonsFinalize(maxContentWidth)
|
||||
}
|
||||
|
||||
var suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)?
|
||||
suggestedPostInfoNodeLayout = nil
|
||||
|
||||
if let suggestedPostInfoNodeLayout {
|
||||
additionalTopHeight += 4.0 + suggestedPostInfoNodeLayout.0.height + 8.0
|
||||
}
|
||||
|
||||
let minimalContentSize: CGSize
|
||||
if hideBackground {
|
||||
minimalContentSize = CGSize(width: 1.0, height: 1.0)
|
||||
@ -3320,7 +3307,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
avatarOffset: avatarOffset,
|
||||
hidesHeaders: hidesHeaders,
|
||||
disablesComments: disablesComments,
|
||||
suggestedPostInfoNodeLayout: suggestedPostInfoNodeLayout,
|
||||
alignment: alignment,
|
||||
isSidePanelOpen: isSidePanelOpen
|
||||
)
|
||||
@ -3384,7 +3370,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
avatarOffset: CGFloat?,
|
||||
hidesHeaders: Bool,
|
||||
disablesComments: Bool,
|
||||
suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)?,
|
||||
alignment: ChatMessageBubbleContentAlignment,
|
||||
isSidePanelOpen: Bool
|
||||
) -> Void {
|
||||
@ -3468,22 +3453,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
|
||||
strongSelf.backgroundNode.backgroundFrame = backgroundFrame
|
||||
|
||||
if let (suggestedPostInfoSize, suggestedPostInfoApply) = suggestedPostInfoNodeLayout {
|
||||
let suggestedPostInfoNode = suggestedPostInfoApply()
|
||||
if suggestedPostInfoNode !== strongSelf.suggestedPostInfoNode {
|
||||
strongSelf.suggestedPostInfoNode?.removeFromSupernode()
|
||||
strongSelf.suggestedPostInfoNode = suggestedPostInfoNode
|
||||
strongSelf.mainContextSourceNode.contentNode.addSubnode(suggestedPostInfoNode)
|
||||
|
||||
let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize)
|
||||
suggestedPostInfoNode.frame = suggestedPostInfoFrame
|
||||
//animation.animator.updateFrame(layer: suggestedPostInfoNode.layer, frame: suggestedPostInfoFrame, completion: nil)
|
||||
}
|
||||
} else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode {
|
||||
strongSelf.suggestedPostInfoNode = nil
|
||||
suggestedPostInfoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let avatarOffset {
|
||||
strongSelf.updateAttachedAvatarNodeOffset(offset: avatarOffset, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
|
@ -1,169 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import WallpaperBackgroundNode
|
||||
import ChatMessageItem
|
||||
import TelegramStringFormatting
|
||||
|
||||
public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
||||
private var titleNode: TextNode?
|
||||
private var priceLabelNode: TextNode?
|
||||
private var priceValueNode: TextNode?
|
||||
private var timeLabelNode: TextNode?
|
||||
private var timeValueNode: TextNode?
|
||||
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public typealias AsyncLayout = (ChatMessageItem, CGFloat) -> (CGSize, () -> ChatMessageSuggestedPostInfoNode)
|
||||
|
||||
public static func asyncLayout(_ node: ChatMessageSuggestedPostInfoNode?) -> (ChatMessageItem, CGFloat) -> (CGSize, () -> ChatMessageSuggestedPostInfoNode) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(node?.titleNode)
|
||||
let makePriceLabelLayout = TextNode.asyncLayout(node?.priceLabelNode)
|
||||
let makePriceValueLayout = TextNode.asyncLayout(node?.priceValueNode)
|
||||
let makeTimeLabelLayout = TextNode.asyncLayout(node?.timeLabelNode)
|
||||
let makeTimeValueLayout = TextNode.asyncLayout(node?.timeValueNode)
|
||||
|
||||
return { item, maxWidth in
|
||||
let insets = UIEdgeInsets(
|
||||
top: 12.0,
|
||||
left: 12.0,
|
||||
bottom: 12.0,
|
||||
right: 12.0
|
||||
)
|
||||
|
||||
let titleSpacing: CGFloat = 8.0
|
||||
let labelSpacing: CGFloat = 8.0
|
||||
let valuesVerticalSpacing: CGFloat = 2.0
|
||||
|
||||
var amount: Int64 = 0
|
||||
var timestamp: Int32?
|
||||
|
||||
if "".isEmpty {
|
||||
amount = 0
|
||||
timestamp = nil
|
||||
}
|
||||
|
||||
/*for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? OutgoingSuggestedPostMessageAttribute {
|
||||
amount = attribute.price.value
|
||||
timestamp = attribute.timestamp
|
||||
}
|
||||
}*/
|
||||
|
||||
//TODO:localize
|
||||
let amountString: String
|
||||
if amount == 0 {
|
||||
amountString = "Free"
|
||||
} else if amount == 1 {
|
||||
amountString = "1 Star"
|
||||
} else {
|
||||
amountString = "\(amount) Stars"
|
||||
}
|
||||
|
||||
var timestampString: String
|
||||
if let timestamp {
|
||||
timestampString = humanReadableStringForTimestamp(strings: item.presentationData.strings, dateTimeFormat: PresentationDateTimeFormat(), timestamp: timestamp, alwaysShowTime: true).string
|
||||
if timestampString.count > 1 {
|
||||
timestampString = String(timestampString[timestampString.startIndex]).capitalized + timestampString[timestampString.index(after: timestampString.startIndex)...]
|
||||
}
|
||||
} else {
|
||||
timestampString = "Anytime"
|
||||
}
|
||||
|
||||
let serviceColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||
|
||||
//TODO:localize
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "You suggest to post\nthis message.", font: Font.regular(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let priceLabelLayout = makePriceLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Price", font: Font.regular(13.0), textColor: serviceColor.primaryText.withMultipliedAlpha(0.5)), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let timeLabelLayout = makeTimeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Time", font: Font.regular(13.0), textColor: serviceColor.primaryText.withMultipliedAlpha(0.5)), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let priceValueLayout = makePriceValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: amountString, font: Font.semibold(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let timeValueLayout = makeTimeValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: timestampString, font: Font.semibold(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var maxContentWidth: CGFloat = 0.0
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
maxContentWidth = max(maxContentWidth, titleLayout.0.size.width)
|
||||
|
||||
contentHeight += titleLayout.0.size.height
|
||||
contentHeight += titleSpacing
|
||||
|
||||
maxContentWidth = max(maxContentWidth, priceLabelLayout.0.size.width + labelSpacing + priceValueLayout.0.size.width)
|
||||
contentHeight += priceLabelLayout.0.size.height + valuesVerticalSpacing
|
||||
|
||||
maxContentWidth = max(maxContentWidth, timeLabelLayout.0.size.width + labelSpacing + timeValueLayout.0.size.width)
|
||||
contentHeight += timeLabelLayout.0.size.height
|
||||
|
||||
let size = CGSize(width: insets.left + insets.right + maxContentWidth, height: insets.top + insets.bottom + contentHeight)
|
||||
|
||||
return (size, {
|
||||
let node = node ?? ChatMessageSuggestedPostInfoNode()
|
||||
|
||||
if node.backgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
node.backgroundNode = backgroundNode
|
||||
backgroundNode.layer.masksToBounds = true
|
||||
backgroundNode.layer.cornerRadius = 15.0
|
||||
node.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
if let backgroundNode = node.backgroundNode {
|
||||
backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
|
||||
let titleNode = titleLayout.1()
|
||||
if node.titleNode !== titleNode {
|
||||
node.titleNode = titleNode
|
||||
node.addSubnode(titleNode)
|
||||
}
|
||||
let priceLabelNode = priceLabelLayout.1()
|
||||
if node.priceLabelNode !== priceLabelNode {
|
||||
node.priceLabelNode = priceLabelNode
|
||||
node.addSubnode(priceLabelNode)
|
||||
}
|
||||
let priceValueNode = priceValueLayout.1()
|
||||
if node.priceValueNode !== priceValueNode {
|
||||
node.priceValueNode = priceValueNode
|
||||
node.addSubnode(priceValueNode)
|
||||
}
|
||||
let timeLabelNode = timeLabelLayout.1()
|
||||
if node.timeLabelNode !== timeLabelNode {
|
||||
node.timeLabelNode = timeLabelNode
|
||||
node.addSubnode(timeLabelNode)
|
||||
}
|
||||
let timeValueNode = timeValueLayout.1()
|
||||
if node.timeValueNode !== timeValueNode {
|
||||
node.timeValueNode = timeValueNode
|
||||
node.addSubnode(timeValueNode)
|
||||
}
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.0.size.width) * 0.5), y: insets.top), size: titleLayout.0.size)
|
||||
titleNode.frame = titleFrame
|
||||
|
||||
let priceLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: titleFrame.maxY + titleSpacing), size: priceLabelLayout.0.size)
|
||||
priceLabelNode.frame = priceLabelFrame
|
||||
priceValueNode.frame = CGRect(origin: CGPoint(x: priceLabelFrame.maxX + labelSpacing, y: priceLabelFrame.minY), size: priceValueLayout.0.size)
|
||||
|
||||
let timeLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: priceLabelFrame.maxY + valuesVerticalSpacing), size: timeLabelLayout.0.size)
|
||||
timeLabelNode.frame = timeLabelFrame
|
||||
timeValueNode.frame = CGRect(origin: CGPoint(x: timeLabelFrame.maxX + labelSpacing, y: timeLabelFrame.minY), size: timeValueLayout.0.size)
|
||||
|
||||
return node
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -139,8 +139,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
||||
}
|
||||
|
||||
if case let .channel(channel) = peer, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = firstMessage.peers[linkedMonoforumId] {
|
||||
//TODO:localize
|
||||
title = authorString + "@" + EnginePeer(mainChannel).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + " Messages"
|
||||
title = authorString + "@" + EnginePeer(mainChannel).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder)
|
||||
} else {
|
||||
if let threadData = item.threadData {
|
||||
title = "\(authorString) → \(threadData.info.title)"
|
||||
|
@ -417,8 +417,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
if let threadData = component.item.item.threadData {
|
||||
titleText = threadData.info.title
|
||||
} else {
|
||||
//TODO:localize
|
||||
titleText = "General"
|
||||
titleText = " "
|
||||
}
|
||||
} else {
|
||||
titleText = component.item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
||||
@ -847,8 +846,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
if let threadData = component.item.item.threadData {
|
||||
titleText = threadData.info.title
|
||||
} else {
|
||||
//TODO:localize
|
||||
titleText = "General"
|
||||
titleText = " "
|
||||
}
|
||||
} else {
|
||||
titleText = component.item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
||||
@ -1220,8 +1218,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
//TODO:localize
|
||||
let titleText: String = "All"
|
||||
let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
@ -1348,8 +1345,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
let leftInset: CGFloat = 6.0
|
||||
let rightInset: CGFloat = 12.0
|
||||
|
||||
//TODO:localize
|
||||
let titleText: String = "All"
|
||||
let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
|
@ -2219,8 +2219,7 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt
|
||||
interaction.editingOpenDiscussionGroupSetup()
|
||||
}))
|
||||
|
||||
//TODO:localize
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text(channel.linkedMonoforumId == nil ? "Off" : "On"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Allow Channel Messages", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: {
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text(channel.linkedMonoforumId == nil ? presentationData.strings.PeerInfo_AllowChannelMessages_Off : presentationData.strings.PeerInfo_AllowChannelMessages_On), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_AllowChannelMessages, icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: {
|
||||
interaction.editingOpenPostSuggestionsSetup()
|
||||
}))
|
||||
}
|
||||
@ -2357,8 +2356,7 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt
|
||||
}))
|
||||
|
||||
if channel.linkedMonoforumId != nil {
|
||||
//TODO:localize
|
||||
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemChannelMessages, label: .none, text: "Channel Messages", icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: {
|
||||
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemChannelMessages, label: .none, text: presentationData.strings.PeerInfo_ChannelMessages, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: {
|
||||
interaction.openChannelMessages()
|
||||
}))
|
||||
}
|
||||
|
@ -566,8 +566,6 @@ extension ChatControllerImpl {
|
||||
let initTimestamp = self.initTimestamp
|
||||
#endif
|
||||
|
||||
//TODO:release ready must include chat node
|
||||
|
||||
self.ready.set(combineLatest(queue: .mainQueue(), [
|
||||
self.contentDataReady.get(),
|
||||
self.wallpaperReady.get(),
|
||||
|
@ -547,7 +547,6 @@ extension ChatControllerImpl {
|
||||
strongSelf.state.chatTitleContent = .custom(strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
|
||||
} else if let channel = peer as? TelegramChannel, channel.isMonoForum {
|
||||
if let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] {
|
||||
//TODO:localize
|
||||
strongSelf.state.chatTitleContent = .custom(mainPeer.debugDisplayTitle, nil, false)
|
||||
} else {
|
||||
strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, false)
|
||||
|
@ -49,486 +49,6 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
||||
}
|
||||
}
|
||||
|
||||
/*private final class ItemView: UIView {
|
||||
private let context: AccountContext
|
||||
private let action: () -> Void
|
||||
|
||||
private let extractedContainerNode: ContextExtractedContentContainingNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
|
||||
private let containerButton: HighlightTrackingButton
|
||||
|
||||
private var icon: ComponentView<Empty>?
|
||||
private var avatarNode: AvatarNode?
|
||||
private let title = ComponentView<Empty>()
|
||||
private var badge: ComponentView<Empty>?
|
||||
|
||||
init(context: AccountContext, action: @escaping (() -> Void), contextGesture: @escaping (ContextGesture, ContextExtractedContentContainingNode) -> Void) {
|
||||
self.context = context
|
||||
self.action = action
|
||||
|
||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.containerButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.extractedContainerNode.contentNode.view.addSubview(self.containerButton)
|
||||
|
||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
|
||||
self.addSubview(self.containerNode.view)
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.containerButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let self, self.bounds.width > 0.0 {
|
||||
let topScale: CGFloat = (self.bounds.width - 1.0) / self.bounds.width
|
||||
let maxScale: CGFloat = (self.bounds.width + 1.0) / self.bounds.width
|
||||
|
||||
if highlighted {
|
||||
self.layer.removeAnimation(forKey: "opacity")
|
||||
self.layer.removeAnimation(forKey: "sublayerTransform")
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateTransformScale(layer: self.layer, scale: topScale)
|
||||
} else {
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateTransformScale(layer: self.layer, scale: 1.0)
|
||||
|
||||
self.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.action()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
var mappedPoint = point
|
||||
if self.bounds.insetBy(dx: -8.0, dy: -4.0).contains(point) {
|
||||
mappedPoint = self.bounds.center
|
||||
}
|
||||
return super.hitTest(mappedPoint, with: event)
|
||||
}
|
||||
|
||||
func update(context: AccountContext, item: Item, isSelected: Bool, theme: PresentationTheme, height: CGFloat, transition: ComponentTransition) -> CGSize {
|
||||
let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25)
|
||||
|
||||
let spacing: CGFloat = 3.0
|
||||
let badgeSpacing: CGFloat = 4.0
|
||||
|
||||
let iconSize = CGSize(width: 18.0, height: 18.0)
|
||||
|
||||
var avatarIconContent: EmojiStatusComponent.Content?
|
||||
if case let .forum(topicId) = item.item.id {
|
||||
if topicId != 1, let threadData = item.item.threadData {
|
||||
if let fileId = threadData.info.icon, fileId != 0 {
|
||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: iconSize, placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
||||
} else {
|
||||
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
|
||||
}
|
||||
} else {
|
||||
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme), tintColor: theme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
if let avatarIconContent {
|
||||
let avatarIconComponent = EmojiStatusComponent(
|
||||
context: context,
|
||||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: avatarIconContent,
|
||||
isVisibleForAnimations: false,
|
||||
action: nil
|
||||
)
|
||||
let icon: ComponentView<Empty>
|
||||
if let current = self.icon {
|
||||
icon = current
|
||||
} else {
|
||||
icon = ComponentView()
|
||||
self.icon = icon
|
||||
}
|
||||
let _ = icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(avatarIconComponent),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 18.0, height: 18.0)
|
||||
)
|
||||
} else if let icon = self.icon {
|
||||
self.icon = nil
|
||||
icon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let titleText: String
|
||||
if case let .forum(topicId) = item.item.id {
|
||||
let _ = topicId
|
||||
if let threadData = item.item.threadData {
|
||||
titleText = threadData.info.title
|
||||
} else {
|
||||
//TODO:localize
|
||||
titleText = "General"
|
||||
}
|
||||
} else {
|
||||
titleText = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
||||
}
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: isSelected ? theme.rootController.navigationBar.accentTextColor : theme.rootController.navigationBar.secondaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
var badgeSize: CGSize?
|
||||
if let readCounters = item.item.readCounters, readCounters.count > 0 {
|
||||
let badge: ComponentView<Empty>
|
||||
var badgeTransition = transition
|
||||
if let current = self.badge {
|
||||
badge = current
|
||||
} else {
|
||||
badgeTransition = .immediate
|
||||
badge = ComponentView<Empty>()
|
||||
self.badge = badge
|
||||
}
|
||||
|
||||
badgeSize = badge.update(
|
||||
transition: badgeTransition,
|
||||
component: AnyComponent(TextBadgeComponent(
|
||||
text: countString(Int64(readCounters.count)),
|
||||
font: Font.regular(12.0),
|
||||
background: item.item.isMuted ? theme.chatList.unreadBadgeInactiveBackgroundColor : theme.chatList.unreadBadgeActiveBackgroundColor,
|
||||
foreground: item.item.isMuted ? theme.chatList.unreadBadgeInactiveTextColor : theme.chatList.unreadBadgeActiveTextColor,
|
||||
insets: UIEdgeInsets(top: 1.0, left: 5.0, bottom: 2.0, right: 5.0)
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
}
|
||||
|
||||
var contentSize: CGFloat = iconSize.width + spacing + titleSize.width
|
||||
if let badgeSize {
|
||||
contentSize += badgeSpacing + badgeSize.width
|
||||
}
|
||||
|
||||
let size = CGSize(width: contentSize, height: height)
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize)
|
||||
|
||||
if let icon = self.icon {
|
||||
if let iconView = icon.view {
|
||||
if iconView.superview == nil {
|
||||
iconView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(iconView)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
if let avatarNode = self.avatarNode {
|
||||
self.avatarNode = nil
|
||||
avatarNode.view.removeFromSuperview()
|
||||
}
|
||||
} else {
|
||||
let avatarNode: AvatarNode
|
||||
if let current = self.avatarNode {
|
||||
avatarNode = current
|
||||
} else {
|
||||
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 7.0))
|
||||
self.avatarNode = avatarNode
|
||||
self.containerButton.addSubview(avatarNode.view)
|
||||
}
|
||||
avatarNode.frame = iconFrame
|
||||
avatarNode.updateSize(size: iconFrame.size)
|
||||
|
||||
if let peer = item.item.renderedPeer.chatMainPeer {
|
||||
if peer.smallProfileImage != nil {
|
||||
avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||
} else {
|
||||
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
if let badgeSize, let badge = self.badge {
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + badgeSpacing, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - badgeSize.height) * 0.5)), size: badgeSize)
|
||||
|
||||
if let badgeView = badge.view {
|
||||
if badgeView.superview == nil {
|
||||
badgeView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(badgeView)
|
||||
badgeView.frame = badgeFrame
|
||||
badgeView.alpha = 0.0
|
||||
}
|
||||
transition.setPosition(view: badgeView, position: badgeFrame.center)
|
||||
transition.setBounds(view: badgeView, bounds: CGRect(origin: CGPoint(), size: badgeFrame.size))
|
||||
transition.setScale(view: badgeView, scale: 1.0)
|
||||
alphaTransition.setAlpha(view: badgeView, alpha: 1.0)
|
||||
}
|
||||
} else if let badge = self.badge {
|
||||
self.badge = nil
|
||||
if let badgeView = badge.view {
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + badgeSpacing, y: titleFrame.minX + floorToScreenPixels((titleFrame.height - badgeView.bounds.height) * 0.5)), size: badgeView.bounds.size)
|
||||
transition.setPosition(view: badgeView, position: badgeFrame.center)
|
||||
transition.setScale(view: badgeView, scale: 0.001)
|
||||
alphaTransition.setAlpha(view: badgeView, alpha: 0.0, completion: { [weak badgeView] _ in
|
||||
badgeView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.containerButton, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
private final class TabItemView: UIView {
|
||||
private let context: AccountContext
|
||||
private let action: () -> Void
|
||||
|
||||
private let extractedContainerNode: ContextExtractedContentContainingNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
|
||||
private let containerButton: HighlightTrackingButton
|
||||
|
||||
private let icon = ComponentView<Empty>()
|
||||
|
||||
init(context: AccountContext, action: @escaping (() -> Void)) {
|
||||
self.context = context
|
||||
self.action = action
|
||||
|
||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.containerButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.extractedContainerNode.contentNode.view.addSubview(self.containerButton)
|
||||
|
||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
|
||||
self.addSubview(self.containerNode.view)
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.containerButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let self, self.bounds.width > 0.0 {
|
||||
let topScale: CGFloat = (self.bounds.width - 1.0) / self.bounds.width
|
||||
let maxScale: CGFloat = (self.bounds.width + 1.0) / self.bounds.width
|
||||
|
||||
if highlighted {
|
||||
self.layer.removeAnimation(forKey: "opacity")
|
||||
self.layer.removeAnimation(forKey: "sublayerTransform")
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateTransformScale(layer: self.layer, scale: topScale)
|
||||
} else {
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateTransformScale(layer: self.layer, scale: 1.0)
|
||||
|
||||
self.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.action()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
var mappedPoint = point
|
||||
if self.bounds.insetBy(dx: -8.0, dy: -4.0).contains(point) {
|
||||
mappedPoint = self.bounds.center
|
||||
}
|
||||
return super.hitTest(mappedPoint, with: event)
|
||||
}
|
||||
|
||||
func update(context: AccountContext, theme: PresentationTheme, height: CGFloat, transition: ComponentTransition) -> CGSize {
|
||||
let iconSize = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BundleIconComponent(
|
||||
name: "Chat/Title Panels/SidebarIcon",
|
||||
tintColor: theme.rootController.navigationBar.secondaryTextColor,
|
||||
maxSize: nil,
|
||||
scaleFactor: 1.0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
let contentSize: CGFloat = iconSize.width
|
||||
let size = CGSize(width: contentSize, height: height)
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
iconView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(iconView)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.containerButton, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
private final class AllItemView: UIView {
|
||||
private let context: AccountContext
|
||||
private let action: () -> Void
|
||||
|
||||
private let extractedContainerNode: ContextExtractedContentContainingNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
|
||||
private let containerButton: HighlightTrackingButton
|
||||
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
init(context: AccountContext, action: @escaping (() -> Void)) {
|
||||
self.context = context
|
||||
self.action = action
|
||||
|
||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.containerButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.extractedContainerNode.contentNode.view.addSubview(self.containerButton)
|
||||
|
||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
|
||||
self.addSubview(self.containerNode.view)
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.containerButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let self, self.bounds.width > 0.0 {
|
||||
let topScale: CGFloat = (self.bounds.width - 1.0) / self.bounds.width
|
||||
let maxScale: CGFloat = (self.bounds.width + 1.0) / self.bounds.width
|
||||
|
||||
if highlighted {
|
||||
self.layer.removeAnimation(forKey: "opacity")
|
||||
self.layer.removeAnimation(forKey: "sublayerTransform")
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateTransformScale(layer: self.layer, scale: topScale)
|
||||
} else {
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateTransformScale(layer: self.layer, scale: 1.0)
|
||||
|
||||
self.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.action()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
var mappedPoint = point
|
||||
if self.bounds.insetBy(dx: -8.0, dy: -4.0).contains(point) {
|
||||
mappedPoint = self.bounds.center
|
||||
}
|
||||
return super.hitTest(mappedPoint, with: event)
|
||||
}
|
||||
|
||||
func update(context: AccountContext, isSelected: Bool, theme: PresentationTheme, height: CGFloat, transition: ComponentTransition) -> CGSize {
|
||||
//TODO:localize
|
||||
let titleText: String = "All"
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: isSelected ? theme.rootController.navigationBar.accentTextColor : theme.rootController.navigationBar.secondaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
let contentSize: CGFloat = titleSize.width
|
||||
let size = CGSize(width: contentSize, height: height)
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize)
|
||||
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.containerButton, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}*/
|
||||
|
||||
private var params: Params?
|
||||
|
||||
private let context: AccountContext
|
||||
@ -618,234 +138,11 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
||||
}
|
||||
transition.updateFrame(view: panelView, frame: panelFrame)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 16.0, bottom: 0.0, right: params.rightInset + 16.0)
|
||||
let itemSpacing: CGFloat = 24.0
|
||||
|
||||
var leftContentInset: CGFloat = containerInsets.left + 8.0
|
||||
|
||||
do {
|
||||
var itemTransition = transition
|
||||
var animateIn = false
|
||||
let itemView: TabItemView
|
||||
if let current = self.tabItemView {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
animateIn = true
|
||||
itemView = TabItemView(context: self.context, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.interfaceInteraction?.toggleChatSidebarMode()
|
||||
})
|
||||
self.tabItemView = itemView
|
||||
self.view.addSubview(itemView)
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(context: self.context, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: leftContentInset, y: -5.0), size: itemSize)
|
||||
|
||||
itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center)
|
||||
itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
if animateIn && transition.isAnimated {
|
||||
itemView.layer.animateAlpha(from: 0.0, to: itemView.alpha, duration: 0.15)
|
||||
transition.animateTransformScale(view: itemView, from: 0.001)
|
||||
}
|
||||
|
||||
leftContentInset += itemSize.width + 8.0
|
||||
}
|
||||
|
||||
var contentSize = CGSize(width: itemSpacing - 8.0, height: panelHeight)
|
||||
|
||||
var validIds: [Item.Id] = []
|
||||
var isFirst = true
|
||||
var selectedItemFrame: CGRect?
|
||||
|
||||
do {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
contentSize.width += itemSpacing
|
||||
}
|
||||
|
||||
var itemTransition = transition
|
||||
var animateIn = false
|
||||
let itemView: AllItemView
|
||||
if let current = self.allItemView {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
animateIn = true
|
||||
itemView = AllItemView(context: self.context, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.interfaceInteraction?.updateChatLocationThread(nil, .left)
|
||||
})
|
||||
self.allItemView = itemView
|
||||
self.scrollView.addSubview(itemView)
|
||||
}
|
||||
|
||||
var isSelected = false
|
||||
if params.interfaceState.chatLocation.threadId == nil {
|
||||
isSelected = true
|
||||
}
|
||||
let itemSize = itemView.update(context: self.context, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize)
|
||||
|
||||
if isSelected {
|
||||
selectedItemFrame = itemFrame
|
||||
}
|
||||
|
||||
itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center)
|
||||
itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
if animateIn && transition.isAnimated {
|
||||
itemView.layer.animateAlpha(from: 0.0, to: itemView.alpha, duration: 0.15)
|
||||
transition.animateTransformScale(view: itemView, from: 0.001)
|
||||
}
|
||||
|
||||
contentSize.width += itemSize.width
|
||||
}
|
||||
|
||||
for item in self.items {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
contentSize.width += itemSpacing
|
||||
}
|
||||
let itemId = item.id
|
||||
validIds.append(itemId)
|
||||
|
||||
var itemTransition = transition
|
||||
var animateIn = false
|
||||
let itemView: ItemView
|
||||
if let current = self.itemViews[itemId] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
animateIn = true
|
||||
let chatListItem = item.item
|
||||
itemView = ItemView(context: self.context, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let topicId: Int64
|
||||
if case let .forum(topicIdValue) = chatListItem.id {
|
||||
topicId = topicIdValue
|
||||
} else {
|
||||
topicId = chatListItem.renderedPeer.peerId.toInt64()
|
||||
}
|
||||
|
||||
var direction = true
|
||||
if let params = self.params, let lhsIndex = self.topicIndex(threadId: params.interfaceState.chatLocation.threadId), let rhsIndex = self.topicIndex(threadId: topicId) {
|
||||
direction = lhsIndex < rhsIndex
|
||||
}
|
||||
|
||||
self.interfaceInteraction?.updateChatLocationThread(topicId, direction ? .right : .left)
|
||||
}, contextGesture: { gesture, sourceNode in
|
||||
})
|
||||
self.itemViews[itemId] = itemView
|
||||
self.scrollView.addSubview(itemView)
|
||||
}
|
||||
|
||||
var isSelected = false
|
||||
if case let .forum(topicId) = item.item.id {
|
||||
isSelected = params.interfaceState.chatLocation.threadId == topicId
|
||||
} else {
|
||||
isSelected = params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64()
|
||||
}
|
||||
let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize)
|
||||
|
||||
if isSelected {
|
||||
selectedItemFrame = itemFrame
|
||||
}
|
||||
|
||||
itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center)
|
||||
itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
if animateIn && transition.isAnimated {
|
||||
itemView.layer.animateAlpha(from: 0.0, to: itemView.alpha, duration: 0.15)
|
||||
transition.animateTransformScale(view: itemView, from: 0.001)
|
||||
}
|
||||
|
||||
contentSize.width += itemSize.width
|
||||
}
|
||||
var removedIds: [Item.Id] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removedIds.append(id)
|
||||
|
||||
if transition.isAnimated {
|
||||
itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak itemView] _ in
|
||||
itemView?.removeFromSuperview()
|
||||
})
|
||||
transition.updateTransformScale(layer: itemView.layer, scale: 0.001)
|
||||
} else {
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removedIds {
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if let selectedItemFrame {
|
||||
let lineFrame = CGRect(origin: CGPoint(x: selectedItemFrame.minX, y: panelHeight - 4.0), size: CGSize(width: selectedItemFrame.width + 4.0, height: 4.0))
|
||||
if self.selectedLineView.isHidden {
|
||||
self.selectedLineView.isHidden = false
|
||||
self.selectedLineView.frame = lineFrame
|
||||
} else {
|
||||
transition.updateFrame(view: self.selectedLineView, frame: lineFrame)
|
||||
}
|
||||
} else {
|
||||
self.selectedLineView.isHidden = true
|
||||
}
|
||||
|
||||
contentSize.width += containerInsets.right
|
||||
|
||||
let scrollSize = CGSize(width: params.width - leftContentInset, height: contentSize.height)
|
||||
self.scrollViewContainer.frame = CGRect(origin: CGPoint(x: leftContentInset, y: 0.0), size: scrollSize)
|
||||
self.scrollViewMask.frame = CGRect(origin: CGPoint(), size: scrollSize)
|
||||
|
||||
if self.scrollView.bounds.size != scrollSize {
|
||||
self.scrollView.frame = CGRect(origin: CGPoint(), size: scrollSize)
|
||||
}
|
||||
if self.scrollView.contentSize != contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
|
||||
let scrollToId: ScrollId
|
||||
if let threadId = params.interfaceState.chatLocation.threadId {
|
||||
scrollToId = .topic(threadId)
|
||||
} else {
|
||||
scrollToId = .all
|
||||
}
|
||||
if self.appliedScrollToId != scrollToId {
|
||||
if case let .topic(threadId) = scrollToId {
|
||||
if let itemView = self.itemViews[.forum(threadId)] {
|
||||
self.appliedScrollToId = scrollToId
|
||||
self.scrollView.scrollRectToVisible(itemView.frame.insetBy(dx: -46.0, dy: 0.0), animated: hadItemViews)
|
||||
}
|
||||
} else if case .all = scrollToId {
|
||||
self.appliedScrollToId = scrollToId
|
||||
self.scrollView.scrollRectToVisible(CGRect(origin: CGPoint(), size: CGSize(width: 1.0, height: 1.0)), animated: hadItemViews)
|
||||
} else {
|
||||
self.appliedScrollToId = scrollToId
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public func updateGlobalOffset(globalOffset: CGFloat, transition: ComponentTransition) {
|
||||
if let panelView = self.panel.view as? ChatSideTopicsPanel.View {
|
||||
panelView.updateGlobalOffset(globalOffset: globalOffset, transition: transition)
|
||||
//transition.setTransform(view: tabItemView, transform: CATransform3DMakeTranslation(0.0, -globalOffset, 0.0))
|
||||
}
|
||||
}
|
||||
|
||||
@ -855,23 +152,5 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*if let threadId {
|
||||
if let value = self.items.firstIndex(where: { item in
|
||||
if item.id == .chatList(PeerId(threadId)) {
|
||||
return true
|
||||
} else if item.id == .forum(threadId) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
return value + 1
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user