mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-07 08:01:10 +00:00
Various improvements
This commit is contained in:
parent
1b410e9d0c
commit
51fcc024d6
@ -10939,6 +10939,7 @@ Sorry for the inconvenience.";
|
||||
"Chat.ReactionContextMenu.RemoveTag" = "Remove Tag";
|
||||
|
||||
"Chat.EmptyStateMessagingRestrictedToPremium.Text" = "Subscribe to **Premium**\nto message **%@**.";
|
||||
"Chat.EmptyStateMessagingRestrictedToPremiumDisabled.Text" = "Subscribe to **Premium**\nto message **%@**.";
|
||||
"Chat.EmptyStateMessagingRestrictedToPremium.Action" = "Get Premium";
|
||||
|
||||
"Chat.ContextMenuReadDate.ReadAvailablePrefix" = "read";
|
||||
@ -11041,3 +11042,5 @@ Sorry for the inconvenience.";
|
||||
"Chat.BottomSearchPanel.DisplayModeFormat" = "Show as %@";
|
||||
"Chat.BottomSearchPanel.DisplayModeChat" = "Chat";
|
||||
"Chat.BottomSearchPanel.DisplayModeList" = "List";
|
||||
|
||||
"Conversation.SendMessageErrorNonPremiumForbidden" = "Only Premium users can message %@";
|
||||
|
@ -151,6 +151,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
||||
previousItemNode.listNode.setPeerThreadPinned = nil
|
||||
previousItemNode.listNode.setPeerThreadHidden = nil
|
||||
previousItemNode.listNode.peerSelected = nil
|
||||
previousItemNode.listNode.disabledPeerSelected = nil
|
||||
previousItemNode.listNode.groupSelected = nil
|
||||
previousItemNode.listNode.updatePeerGrouping = nil
|
||||
previousItemNode.listNode.contentOffsetChanged = nil
|
||||
@ -205,6 +206,9 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
||||
itemNode.listNode.peerSelected = { [weak self] peerId, threadId, animated, activateInput, promoInfo in
|
||||
self?.peerSelected?(peerId, threadId, animated, activateInput, promoInfo)
|
||||
}
|
||||
itemNode.listNode.disabledPeerSelected = { [weak self] peerId, threadId, reason in
|
||||
self?.disabledPeerSelected?(peerId, threadId, reason)
|
||||
}
|
||||
itemNode.listNode.groupSelected = { [weak self] groupId in
|
||||
self?.groupSelected?(groupId)
|
||||
}
|
||||
@ -390,6 +394,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
||||
var setPeerThreadPinned: ((EnginePeer.Id, Int64, Bool) -> Void)?
|
||||
var setPeerThreadHidden: ((EnginePeer.Id, Int64, Bool) -> Void)?
|
||||
public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
public var disabledPeerSelected: ((EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void)?
|
||||
var groupSelected: ((EngineChatList.Group) -> Void)?
|
||||
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
|
||||
var contentOffset: ListViewVisibleContentOffset?
|
||||
|
@ -1754,7 +1754,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
let foundThreads: Signal<[EngineChatList.Item], NoError>
|
||||
if case let .forum(peerId) = location, (key == .topics || key == .chats) {
|
||||
foundThreads = chatListViewForLocation(chatListLocation: location, location: .initial(count: 1000, filter: nil), account: context.account)
|
||||
foundThreads = chatListViewForLocation(chatListLocation: location, location: .initial(count: 1000, filter: nil), account: context.account, shouldLoadCanMessagePeer: true)
|
||||
|> map { view -> [EngineChatList.Item] in
|
||||
var filteredItems: [EngineChatList.Item] = []
|
||||
let queryTokens = stringIndexTokens(finalQuery, transliteration: .combined)
|
||||
|
@ -588,6 +588,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
peerMode: .generalSearch(isSavedMessages: false),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
|
||||
enabled: enabled,
|
||||
selection: selectable ? .selectable(selected: selected) : .none,
|
||||
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
@ -601,9 +602,9 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
|
||||
}
|
||||
}
|
||||
}, disabledAction: isForum && editing ? nil : { _ in
|
||||
}, disabledAction: (isForum && editing) && !peerEntry.requiresPremiumForMessaging ? nil : { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId, .generic)
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId, peerEntry.requiresPremiumForMessaging ? .premiumRequired : .generic)
|
||||
}
|
||||
},
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
@ -627,6 +628,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
peerMode: .generalSearch(isSavedMessages: false),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
|
||||
enabled: true,
|
||||
selection: .none,
|
||||
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
@ -636,7 +638,11 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.peerSelected(chatPeer, nil, nil, nil)
|
||||
}
|
||||
}, disabledAction: nil,
|
||||
}, disabledAction: peerEntry.requiresPremiumForMessaging ? { _ in
|
||||
if let chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, nil, .premiumRequired)
|
||||
}
|
||||
} : nil,
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
animationRenderer: nodeInteraction.animationRenderer
|
||||
), directionHint: entry.directionHint)
|
||||
@ -912,6 +918,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
peerMode: .generalSearch(isSavedMessages: false),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
|
||||
enabled: enabled,
|
||||
selection: selectable ? .selectable(selected: selected) : .none,
|
||||
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
@ -925,9 +932,9 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
|
||||
}
|
||||
}
|
||||
}, disabledAction: isForum && editing ? nil : { _ in
|
||||
}, disabledAction: (isForum && editing) && !peerEntry.requiresPremiumForMessaging ? nil : { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId, .generic)
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId, peerEntry.requiresPremiumForMessaging ? .premiumRequired : .generic)
|
||||
}
|
||||
},
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
@ -951,6 +958,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
peerMode: .generalSearch(isSavedMessages: false),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
|
||||
enabled: true,
|
||||
selection: .none,
|
||||
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
@ -960,7 +968,11 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.peerSelected(chatPeer, nil, nil, nil)
|
||||
}
|
||||
}, disabledAction: nil,
|
||||
}, disabledAction: peerEntry.requiresPremiumForMessaging ? { _ in
|
||||
if let chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, nil, .premiumRequired)
|
||||
}
|
||||
} : nil,
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
animationRenderer: nodeInteraction.animationRenderer
|
||||
), directionHint: entry.directionHint)
|
||||
@ -1758,10 +1770,17 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
|
||||
let shouldLoadCanMessagePeer: Bool
|
||||
if case .peers = mode {
|
||||
shouldLoadCanMessagePeer = true
|
||||
} else {
|
||||
shouldLoadCanMessagePeer = false
|
||||
}
|
||||
|
||||
let chatListViewUpdate = self.chatListLocation.get()
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { listLocation -> Signal<(ChatListNodeViewUpdate, ChatListFilter?), NoError> in
|
||||
return chatListViewForLocation(chatListLocation: location, location: listLocation, account: context.account)
|
||||
return chatListViewForLocation(chatListLocation: location, location: listLocation, account: context.account, shouldLoadCanMessagePeer: shouldLoadCanMessagePeer)
|
||||
|> map { update in
|
||||
return (update, listLocation.filter)
|
||||
}
|
||||
@ -3719,7 +3738,15 @@ public final class ChatListNode: ListView {
|
||||
guard case let .chatList(groupId) = self.location else {
|
||||
return
|
||||
}
|
||||
let _ = (chatListViewForLocation(chatListLocation: .chatList(groupId: groupId), location: .initial(count: 10, filter: filter), account: self.context.account)
|
||||
|
||||
let shouldLoadCanMessagePeer: Bool
|
||||
if case .peers = self.mode {
|
||||
shouldLoadCanMessagePeer = true
|
||||
} else {
|
||||
shouldLoadCanMessagePeer = false
|
||||
}
|
||||
|
||||
let _ = (chatListViewForLocation(chatListLocation: .chatList(groupId: groupId), location: .initial(count: 10, filter: filter), account: self.context.account, shouldLoadCanMessagePeer: shouldLoadCanMessagePeer)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { update in
|
||||
let items = update.list.items
|
||||
|
@ -714,7 +714,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||
)
|
||||
},
|
||||
requiresPremiumForMessaging: false,
|
||||
requiresPremiumForMessaging: entry.isPremiumRequiredToMessage,
|
||||
displayAsTopicList: entry.displayAsTopicList
|
||||
))
|
||||
|
||||
|
@ -113,7 +113,7 @@ public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: E
|
||||
})
|
||||
}
|
||||
|
||||
func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account) -> Signal<ChatListNodeViewUpdate, NoError> {
|
||||
func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account, shouldLoadCanMessagePeer: Bool) -> Signal<ChatListNodeViewUpdate, NoError> {
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
switch chatListLocation {
|
||||
@ -128,7 +128,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
|
||||
switch location {
|
||||
case let .initial(count, _):
|
||||
let signal: Signal<(ChatListView, ViewUpdateType), NoError>
|
||||
signal = account.viewTracker.tailChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, count: count)
|
||||
signal = account.viewTracker.tailChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, count: count, shouldLoadCanMessagePeer: shouldLoadCanMessagePeer)
|
||||
return signal
|
||||
|> map { view, updateType -> ChatListNodeViewUpdate in
|
||||
return ChatListNodeViewUpdate(list: EngineChatList(view, accountPeerId: accountPeerId), type: updateType, scrollPosition: nil)
|
||||
@ -138,7 +138,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
|
||||
return .never()
|
||||
}
|
||||
var first = true
|
||||
return account.viewTracker.aroundChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, index: index, count: 80)
|
||||
return account.viewTracker.aroundChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, index: index, count: 80, shouldLoadCanMessagePeer: shouldLoadCanMessagePeer)
|
||||
|> map { view, updateType -> ChatListNodeViewUpdate in
|
||||
let genericType: ViewUpdateType
|
||||
if first {
|
||||
@ -157,7 +157,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
|
||||
let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > .chatList(index) ? .Down : .Up
|
||||
let chatScrollPosition: ChatListNodeViewScrollPosition = .index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated)
|
||||
var first = true
|
||||
return account.viewTracker.aroundChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, index: index, count: 80)
|
||||
return account.viewTracker.aroundChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, index: index, count: 80, shouldLoadCanMessagePeer: shouldLoadCanMessagePeer)
|
||||
|> map { view, updateType -> ChatListNodeViewUpdate in
|
||||
let genericType: ViewUpdateType
|
||||
let scrollPosition: ChatListNodeViewScrollPosition? = first ? chatScrollPosition : nil
|
||||
@ -295,7 +295,8 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
|
||||
isContact: false,
|
||||
autoremoveTimeout: nil,
|
||||
storyStats: nil,
|
||||
displayAsTopicList: false
|
||||
displayAsTopicList: false,
|
||||
isPremiumRequiredToMessage: false
|
||||
))
|
||||
}
|
||||
|
||||
@ -372,7 +373,8 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
|
||||
isContact: false,
|
||||
autoremoveTimeout: nil,
|
||||
storyStats: nil,
|
||||
displayAsTopicList: false
|
||||
displayAsTopicList: false,
|
||||
isPremiumRequiredToMessage: false
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,7 @@ public enum ChatListEntry: Comparable {
|
||||
public var isContact: Bool
|
||||
public var autoremoveTimeout: Int32?
|
||||
public var storyStats: PeerStoryStats?
|
||||
public var extractedCachedData: AnyHashable?
|
||||
|
||||
public init(
|
||||
index: ChatListIndex,
|
||||
@ -141,7 +142,8 @@ public enum ChatListEntry: Comparable {
|
||||
hasFailed: Bool,
|
||||
isContact: Bool,
|
||||
autoremoveTimeout: Int32?,
|
||||
storyStats: PeerStoryStats?
|
||||
storyStats: PeerStoryStats?,
|
||||
extractedCachedData: AnyHashable?
|
||||
) {
|
||||
self.index = index
|
||||
self.messages = messages
|
||||
@ -157,6 +159,7 @@ public enum ChatListEntry: Comparable {
|
||||
self.isContact = isContact
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
self.storyStats = storyStats
|
||||
self.extractedCachedData = extractedCachedData
|
||||
}
|
||||
|
||||
public static func ==(lhs: MessageEntryData, rhs: MessageEntryData) -> Bool {
|
||||
@ -218,6 +221,9 @@ public enum ChatListEntry: Comparable {
|
||||
if lhs.storyStats != rhs.storyStats {
|
||||
return false
|
||||
}
|
||||
if lhs.extractedCachedData != rhs.extractedCachedData {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -308,6 +314,7 @@ enum MutableChatListEntry: Equatable {
|
||||
var isContact: Bool
|
||||
var autoremoveTimeout: Int32?
|
||||
var storyStats: PeerStoryStats?
|
||||
var extractedCachedData: AnyHashable?
|
||||
|
||||
init(
|
||||
index: ChatListIndex,
|
||||
@ -325,7 +332,8 @@ enum MutableChatListEntry: Equatable {
|
||||
hasFailedMessages: Bool,
|
||||
isContact: Bool,
|
||||
autoremoveTimeout: Int32?,
|
||||
storyStats: PeerStoryStats?
|
||||
storyStats: PeerStoryStats?,
|
||||
extractedCachedData: AnyHashable?
|
||||
) {
|
||||
self.index = index
|
||||
self.messages = messages
|
||||
@ -343,6 +351,7 @@ enum MutableChatListEntry: Equatable {
|
||||
self.isContact = isContact
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
self.storyStats = storyStats
|
||||
self.extractedCachedData = extractedCachedData
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,6 +562,7 @@ final class MutableChatListView {
|
||||
let filterPredicate: ChatListFilterPredicate?
|
||||
private let aroundIndex: ChatListIndex
|
||||
private let summaryComponents: ChatListEntrySummaryComponents
|
||||
private let extractCachedData: ((CachedPeerData) -> AnyHashable?)?
|
||||
fileprivate var groupEntries: [ChatListGroupReferenceEntry]
|
||||
private var count: Int
|
||||
|
||||
@ -569,11 +579,16 @@ final class MutableChatListView {
|
||||
private let displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey
|
||||
private(set) var displaySavedMessagesAsTopicList: PreferencesEntry?
|
||||
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?, aroundIndex: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents) {
|
||||
private let accountPeerId: PeerId?
|
||||
private(set) var accountPeer: Peer?
|
||||
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?, aroundIndex: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents, extractCachedData: ((CachedPeerData) -> AnyHashable?)?, accountPeerId: PeerId?) {
|
||||
self.groupId = groupId
|
||||
self.filterPredicate = filterPredicate
|
||||
self.aroundIndex = aroundIndex
|
||||
self.summaryComponents = summaryComponents
|
||||
self.extractCachedData = extractCachedData
|
||||
self.accountPeerId = accountPeerId
|
||||
|
||||
self.currentHiddenPeerIds = postbox.hiddenChatIds
|
||||
|
||||
@ -595,7 +610,7 @@ final class MutableChatListView {
|
||||
spaces.append(.group(groupId: self.groupId, pinned: .includePinned, predicate: filterPredicate))
|
||||
}
|
||||
self.spaces = spaces
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: aroundIndex, summaryComponents: self.summaryComponents, halfLimit: count)
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: aroundIndex, summaryComponents: self.summaryComponents, extractCachedData: self.extractCachedData, halfLimit: count)
|
||||
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
|
||||
|
||||
self.count = count
|
||||
@ -619,6 +634,8 @@ final class MutableChatListView {
|
||||
}
|
||||
|
||||
self.displaySavedMessagesAsTopicList = postbox.preferencesTable.get(key: self.displaySavedMessagesAsTopicListPreferencesKey)
|
||||
|
||||
self.accountPeer = self.accountPeerId.flatMap(postbox.peerTable.get)
|
||||
}
|
||||
|
||||
private func reloadGroups(postbox: PostboxImpl) {
|
||||
@ -698,17 +715,20 @@ final class MutableChatListView {
|
||||
}
|
||||
|
||||
self.displaySavedMessagesAsTopicList = postbox.preferencesTable.get(key: self.displaySavedMessagesAsTopicListPreferencesKey)
|
||||
|
||||
self.accountPeer = self.accountPeerId.flatMap(postbox.peerTable.get)
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl, currentTransaction: Transaction) -> Bool {
|
||||
var updated = false
|
||||
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, extractCachedData: self.extractCachedData, halfLimit: self.count)
|
||||
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
|
||||
updated = true
|
||||
|
||||
let currentGroupEntries = self.groupEntries
|
||||
let currentDisplaySavedMessagesAsTopicList = self.displaySavedMessagesAsTopicList
|
||||
let currentAccountPeer = self.accountPeer
|
||||
|
||||
self.reloadGroups(postbox: postbox)
|
||||
|
||||
@ -718,6 +738,9 @@ final class MutableChatListView {
|
||||
if self.displaySavedMessagesAsTopicList != currentDisplaySavedMessagesAsTopicList {
|
||||
updated = true
|
||||
}
|
||||
if !arePeersEqual(self.accountPeer, currentAccountPeer) {
|
||||
updated = true
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
@ -733,11 +756,11 @@ final class MutableChatListView {
|
||||
}
|
||||
|
||||
if transaction.updatedGlobalNotificationSettings && self.filterPredicate != nil {
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, extractCachedData: self.extractCachedData, halfLimit: self.count)
|
||||
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
|
||||
hasChanges = true
|
||||
} else if hasFilterChanges {
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
|
||||
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, extractCachedData: self.extractCachedData, halfLimit: self.count)
|
||||
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
|
||||
hasChanges = true
|
||||
} else {
|
||||
@ -761,6 +784,14 @@ final class MutableChatListView {
|
||||
}
|
||||
}
|
||||
|
||||
if let accountPeerId = self.accountPeerId, transaction.currentUpdatedPeers[accountPeerId] != nil {
|
||||
let accountPeer = self.accountPeerId.flatMap(postbox.peerTable.get)
|
||||
if !arePeersEqual(self.accountPeer, accountPeer) {
|
||||
self.accountPeer = accountPeer
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
if case .root = self.groupId, self.filterPredicate == nil {
|
||||
var invalidatedGroups = false
|
||||
for (groupId, groupOperations) in operations {
|
||||
@ -926,6 +957,11 @@ final class MutableChatListView {
|
||||
|
||||
let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId)
|
||||
|
||||
var extractedCachedData: AnyHashable?
|
||||
if let extractCachedData = self.extractCachedData {
|
||||
extractedCachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId).flatMap(extractCachedData)
|
||||
}
|
||||
|
||||
return .MessageEntry(MutableChatListEntry.MessageEntryData(
|
||||
index: index,
|
||||
messages: renderedMessages,
|
||||
@ -942,7 +978,8 @@ final class MutableChatListView {
|
||||
hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId),
|
||||
isContact: isContact,
|
||||
autoremoveTimeout: autoremoveTimeout,
|
||||
storyStats: storyStats
|
||||
storyStats: storyStats,
|
||||
extractedCachedData: extractedCachedData
|
||||
))
|
||||
default:
|
||||
return nil
|
||||
@ -966,6 +1003,7 @@ public final class ChatListView {
|
||||
public let earlierIndex: ChatListIndex?
|
||||
public let laterIndex: ChatListIndex?
|
||||
public let displaySavedMessagesAsTopicList: PreferencesEntry?
|
||||
public let accountPeer: Peer?
|
||||
|
||||
init(_ mutableView: MutableChatListView) {
|
||||
self.groupId = mutableView.groupId
|
||||
@ -988,7 +1026,8 @@ public final class ChatListView {
|
||||
hasFailed: entryData.hasFailedMessages,
|
||||
isContact: entryData.isContact,
|
||||
autoremoveTimeout: entryData.autoremoveTimeout,
|
||||
storyStats: entryData.storyStats
|
||||
storyStats: entryData.storyStats,
|
||||
extractedCachedData: entryData.extractedCachedData
|
||||
)))
|
||||
case let .HoleEntry(hole):
|
||||
entries.append(.HoleEntry(hole))
|
||||
@ -1022,7 +1061,8 @@ public final class ChatListView {
|
||||
hasFailed: entryData.hasFailedMessages,
|
||||
isContact: entryData.isContact,
|
||||
autoremoveTimeout: entryData.autoremoveTimeout,
|
||||
storyStats: entryData.storyStats
|
||||
storyStats: entryData.storyStats,
|
||||
extractedCachedData: entryData.extractedCachedData
|
||||
)),
|
||||
info: entry.info
|
||||
))
|
||||
@ -1035,5 +1075,6 @@ public final class ChatListView {
|
||||
|
||||
self.additionalItemEntries = additionalItemEntries
|
||||
self.displaySavedMessagesAsTopicList = mutableView.displaySavedMessagesAsTopicList
|
||||
self.accountPeer = mutableView.accountPeer
|
||||
}
|
||||
}
|
||||
|
@ -141,14 +141,16 @@ private final class ChatListViewSpaceState {
|
||||
private let space: ChatListViewSpace
|
||||
private let anchorIndex: MutableChatListEntryIndex
|
||||
private let summaryComponents: ChatListEntrySummaryComponents
|
||||
private let extractCachedData: ((CachedPeerData) -> AnyHashable?)?
|
||||
private let halfLimit: Int
|
||||
|
||||
var orderedEntries: OrderedChatListViewEntries
|
||||
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, space: ChatListViewSpace, anchorIndex: MutableChatListEntryIndex, summaryComponents: ChatListEntrySummaryComponents, halfLimit: Int) {
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, space: ChatListViewSpace, anchorIndex: MutableChatListEntryIndex, summaryComponents: ChatListEntrySummaryComponents, extractCachedData: ((CachedPeerData) -> AnyHashable?)?, halfLimit: Int) {
|
||||
self.space = space
|
||||
self.anchorIndex = anchorIndex
|
||||
self.summaryComponents = summaryComponents
|
||||
self.extractCachedData = extractCachedData
|
||||
self.halfLimit = halfLimit
|
||||
self.orderedEntries = OrderedChatListViewEntries(anchorIndex: anchorIndex.index, lowerOrAtAnchor: [], higherThanAnchor: [])
|
||||
self.fillSpace(postbox: postbox, currentTransaction: currentTransaction)
|
||||
@ -913,7 +915,7 @@ private final class ChatListViewSpaceState {
|
||||
}
|
||||
}
|
||||
|
||||
if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty || cachedPeerDataUpdated || !transaction.currentStoryTopItemEvents.isEmpty || !transaction.storyPeerStatesEvents.isEmpty {
|
||||
if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty || cachedPeerDataUpdated || !transaction.currentStoryTopItemEvents.isEmpty || !transaction.storyPeerStatesEvents.isEmpty || !transaction.currentUpdatedCachedPeerData.isEmpty {
|
||||
if self.orderedEntries.mutableScan({ entry in
|
||||
switch entry {
|
||||
case let .MessageEntry(entryData):
|
||||
@ -988,6 +990,14 @@ private final class ChatListViewSpaceState {
|
||||
didUpdateSummaryInfo = true
|
||||
}
|
||||
|
||||
var extractedCachedData: AnyHashable?
|
||||
if let extractCachedData = self.extractCachedData {
|
||||
extractedCachedData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId).flatMap(extractCachedData)
|
||||
}
|
||||
if entryData.extractedCachedData != extractedCachedData {
|
||||
didUpdateSummaryInfo = true
|
||||
}
|
||||
|
||||
if didUpdateSummaryInfo {
|
||||
var entryData = entryData
|
||||
entryData.readState = updatedReadState
|
||||
@ -995,6 +1005,7 @@ private final class ChatListViewSpaceState {
|
||||
entryData.autoremoveTimeout = updatedAutoremoveTimeout
|
||||
entryData.storyStats = storyStats
|
||||
entryData.displayAsRegularChat = displayAsRegularChat
|
||||
entryData.extractedCachedData = extractedCachedData
|
||||
return .MessageEntry(entryData)
|
||||
} else {
|
||||
return nil
|
||||
@ -1382,16 +1393,18 @@ final class ChatListViewSample {
|
||||
struct ChatListViewState {
|
||||
private let anchorIndex: MutableChatListEntryIndex
|
||||
private let summaryComponents: ChatListEntrySummaryComponents
|
||||
private let extractCachedData: ((CachedPeerData) -> AnyHashable?)?
|
||||
private let halfLimit: Int
|
||||
private var stateBySpace: [ChatListViewSpace: ChatListViewSpaceState] = [:]
|
||||
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, spaces: [ChatListViewSpace], anchorIndex: ChatListIndex, summaryComponents: ChatListEntrySummaryComponents, halfLimit: Int) {
|
||||
init(postbox: PostboxImpl, currentTransaction: Transaction, spaces: [ChatListViewSpace], anchorIndex: ChatListIndex, summaryComponents: ChatListEntrySummaryComponents, extractCachedData: ((CachedPeerData) -> AnyHashable?)?, halfLimit: Int) {
|
||||
self.anchorIndex = MutableChatListEntryIndex(index: anchorIndex, isMessage: true)
|
||||
self.summaryComponents = summaryComponents
|
||||
self.extractCachedData = extractCachedData
|
||||
self.halfLimit = halfLimit
|
||||
|
||||
for space in spaces {
|
||||
self.stateBySpace[space] = ChatListViewSpaceState(postbox: postbox, currentTransaction: currentTransaction, space: space, anchorIndex: self.anchorIndex, summaryComponents: summaryComponents, halfLimit: halfLimit)
|
||||
self.stateBySpace[space] = ChatListViewSpaceState(postbox: postbox, currentTransaction: currentTransaction, space: space, anchorIndex: self.anchorIndex, summaryComponents: summaryComponents, extractCachedData: extractCachedData, halfLimit: halfLimit)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1641,6 +1654,11 @@ struct ChatListViewState {
|
||||
|
||||
let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId)
|
||||
|
||||
var extractedCachedData: AnyHashable?
|
||||
if let extractCachedData = self.extractCachedData {
|
||||
extractedCachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId).flatMap(extractCachedData)
|
||||
}
|
||||
|
||||
let updatedEntry: MutableChatListEntry = .MessageEntry(MutableChatListEntry.MessageEntryData(
|
||||
index: index,
|
||||
messages: renderedMessages,
|
||||
@ -1657,7 +1675,8 @@ struct ChatListViewState {
|
||||
hasFailedMessages: false,
|
||||
isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId),
|
||||
autoremoveTimeout: autoremoveTimeout,
|
||||
storyStats: storyStats
|
||||
storyStats: storyStats,
|
||||
extractedCachedData: extractedCachedData
|
||||
))
|
||||
if directionIndex == 0 {
|
||||
self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry)
|
||||
|
@ -3458,13 +3458,13 @@ final class PostboxImpl {
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public func tailChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, count: Int, summaryComponents: ChatListEntrySummaryComponents) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return self.aroundChatListView(groupId: groupId, filterPredicate: filterPredicate, index: ChatListIndex.absoluteUpperBound, count: count, summaryComponents: summaryComponents, userInteractive: true)
|
||||
public func tailChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?, count: Int, summaryComponents: ChatListEntrySummaryComponents, extractCachedData: ((CachedPeerData) -> AnyHashable?)?, accountPeerId: PeerId?) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return self.aroundChatListView(groupId: groupId, filterPredicate: filterPredicate, index: ChatListIndex.absoluteUpperBound, count: count, summaryComponents: summaryComponents, userInteractive: true, extractCachedData: extractCachedData, accountPeerId: accountPeerId)
|
||||
}
|
||||
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents, userInteractive: Bool = false) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents, userInteractive: Bool = false, extractCachedData: ((CachedPeerData) -> AnyHashable?)?, accountPeerId: PeerId?) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return self.transactionSignal(userInteractive: userInteractive, { subscriber, transaction in
|
||||
let mutableView = MutableChatListView(postbox: self, currentTransaction: transaction, groupId: groupId, filterPredicate: filterPredicate, aroundIndex: index, count: count, summaryComponents: summaryComponents)
|
||||
let mutableView = MutableChatListView(postbox: self, currentTransaction: transaction, groupId: groupId, filterPredicate: filterPredicate, aroundIndex: index, count: count, summaryComponents: summaryComponents, extractCachedData: extractCachedData, accountPeerId: accountPeerId)
|
||||
mutableView.render(postbox: self)
|
||||
|
||||
let (index, signal) = self.viewTracker.addChatListView(mutableView)
|
||||
@ -4568,7 +4568,9 @@ public class Postbox {
|
||||
groupId: PeerGroupId,
|
||||
filterPredicate: ChatListFilterPredicate? = nil,
|
||||
count: Int,
|
||||
summaryComponents: ChatListEntrySummaryComponents
|
||||
summaryComponents: ChatListEntrySummaryComponents,
|
||||
extractCachedData: ((CachedPeerData) -> AnyHashable?)? = nil,
|
||||
accountPeerId: PeerId? = nil
|
||||
) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -4578,7 +4580,9 @@ public class Postbox {
|
||||
groupId: groupId,
|
||||
filterPredicate: filterPredicate,
|
||||
count: count,
|
||||
summaryComponents: summaryComponents
|
||||
summaryComponents: summaryComponents,
|
||||
extractCachedData: extractCachedData,
|
||||
accountPeerId: accountPeerId
|
||||
).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
|
||||
}
|
||||
|
||||
@ -4592,7 +4596,9 @@ public class Postbox {
|
||||
index: ChatListIndex,
|
||||
count: Int,
|
||||
summaryComponents: ChatListEntrySummaryComponents,
|
||||
userInteractive: Bool = false
|
||||
userInteractive: Bool = false,
|
||||
extractCachedData: ((CachedPeerData) -> AnyHashable?)? = nil,
|
||||
accountPeerId: PeerId? = nil
|
||||
) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -4604,7 +4610,9 @@ public class Postbox {
|
||||
index: index,
|
||||
count: count,
|
||||
summaryComponents: summaryComponents,
|
||||
userInteractive: userInteractive
|
||||
userInteractive: userInteractive,
|
||||
extractCachedData: extractCachedData,
|
||||
accountPeerId: accountPeerId
|
||||
).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
|
||||
}
|
||||
|
||||
|
@ -1225,7 +1225,16 @@ public final class ShareController: ViewController {
|
||||
}
|
||||
return true
|
||||
}
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: (self.environment is ShareControllerAppEnvironment) ? presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
|
||||
|
||||
var hasAction = false
|
||||
if let context = self.currentContext as? ShareControllerAppAccountContext {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.context.currentAppConfiguration.with { $0 })
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
hasAction = true
|
||||
}
|
||||
}
|
||||
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: hasAction ? presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
|
||||
}), elevatedLayout: false, animateInAsReplacement: false, action: { [weak self] action in
|
||||
guard let self, let parentNavigationController = self.parentNavigationController, let context = self.currentContext as? ShareControllerAppAccountContext else {
|
||||
return false
|
||||
|
@ -1459,7 +1459,8 @@ private func threadList(accountPeerId: EnginePeer.Id, postbox: Postbox, peerId:
|
||||
isContact: false,
|
||||
autoremoveTimeout: nil,
|
||||
storyStats: nil,
|
||||
displayAsTopicList: false
|
||||
displayAsTopicList: false,
|
||||
isPremiumRequiredToMessage: false
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
||||
isRemoteAudioMuted: false,
|
||||
localVideo: nil,
|
||||
remoteVideo: nil,
|
||||
isRemoteBatteryLow: false
|
||||
isRemoteBatteryLow: false,
|
||||
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency
|
||||
)
|
||||
if let peer = call.peer {
|
||||
self.updatePeer(peer: peer)
|
||||
|
@ -1137,7 +1137,8 @@ public func _internal_searchForumTopics(account: Account, peerId: EnginePeer.Id,
|
||||
isContact: false,
|
||||
autoremoveTimeout: nil,
|
||||
storyStats: nil,
|
||||
displayAsTopicList: false
|
||||
displayAsTopicList: false,
|
||||
isPremiumRequiredToMessage: false
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -1966,14 +1966,26 @@ public final class AccountViewTracker {
|
||||
if let account = self.account {
|
||||
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>
|
||||
if let peerId = chatLocation.peerId, let threadId = chatLocation.threadId, tag == nil {
|
||||
signal = account.postbox.transaction { transaction -> MessageHistoryThreadData? in
|
||||
return transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self)
|
||||
signal = account.postbox.transaction { transaction -> (MessageHistoryThreadData?, MessageIndex?) in
|
||||
let interfaceState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId)
|
||||
|
||||
return (
|
||||
transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self),
|
||||
interfaceState?.historyScrollMessageIndex
|
||||
)
|
||||
}
|
||||
|> mapToSignal { threadInfo -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
|
||||
|> mapToSignal { threadInfo, scrollRestorationIndex -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
|
||||
if peerId == account.peerId {
|
||||
let anchor: HistoryViewInputAnchor
|
||||
if let scrollRestorationIndex {
|
||||
anchor = .index(scrollRestorationIndex)
|
||||
} else {
|
||||
anchor = .upperBound
|
||||
}
|
||||
|
||||
return account.postbox.aroundMessageHistoryViewForLocation(
|
||||
chatLocation,
|
||||
anchor: .upperBound,
|
||||
anchor: anchor,
|
||||
ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange,
|
||||
count: count,
|
||||
fixedCombinedReadStates: .peer([peerId: CombinedPeerReadState(states: [
|
||||
@ -2374,7 +2386,7 @@ public final class AccountViewTracker {
|
||||
})
|
||||
}
|
||||
|
||||
public func tailChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
public func tailChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, count: Int, shouldLoadCanMessagePeer: Bool = false) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
if let account = self.account {
|
||||
return self.wrappedChatListView(signal: account.postbox.tailChatListView(
|
||||
groupId: groupId,
|
||||
@ -2397,14 +2409,16 @@ public final class AccountViewTracker {
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
extractCachedData: shouldLoadCanMessagePeer ? extractCachedDataIsPremiumRequiredToMessage : nil,
|
||||
accountPeerId: shouldLoadCanMessagePeer ? account.peerId : nil
|
||||
))
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int, shouldLoadCanMessagePeer: Bool = false) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
if let account = self.account {
|
||||
return self.wrappedChatListView(signal: account.postbox.aroundChatListView(
|
||||
groupId: groupId,
|
||||
@ -2428,7 +2442,9 @@ public final class AccountViewTracker {
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
extractCachedData: shouldLoadCanMessagePeer ? extractCachedDataIsPremiumRequiredToMessage : nil,
|
||||
accountPeerId: shouldLoadCanMessagePeer ? account.peerId : nil
|
||||
))
|
||||
} else {
|
||||
return .never()
|
||||
@ -2483,3 +2499,26 @@ public final class AccountViewTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ExtractedChatListItemCachedData: Hashable {
|
||||
public let isPremiumRequiredToMessage: Bool
|
||||
|
||||
public init(isPremiumRequiredToMessage: Bool) {
|
||||
self.isPremiumRequiredToMessage = isPremiumRequiredToMessage
|
||||
}
|
||||
|
||||
public static func ==(lhs: ExtractedChatListItemCachedData, rhs: ExtractedChatListItemCachedData) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(self.isPremiumRequiredToMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private func extractCachedDataIsPremiumRequiredToMessage(_ cachedData: CachedPeerData) -> AnyHashable? {
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
return ExtractedChatListItemCachedData(isPremiumRequiredToMessage: cachedData.flags.contains(.premiumRequired))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ public enum PendingMessageFailureReason {
|
||||
case tooMuchScheduled
|
||||
case voiceMessagesForbidden
|
||||
case sendingTooFast
|
||||
case nonPremiumMessagesForbidden
|
||||
}
|
||||
|
||||
func sendMessageReasonForError(_ error: String) -> PendingMessageFailureReason? {
|
||||
@ -75,6 +76,8 @@ func sendMessageReasonForError(_ error: String) -> PendingMessageFailureReason?
|
||||
return .tooMuchScheduled
|
||||
} else if error.hasPrefix("VOICE_MESSAGES_FORBIDDEN") {
|
||||
return .voiceMessagesForbidden
|
||||
} else if error.hasPrefix("PRIVACY_PREMIUM_REQUIRED") {
|
||||
return .nonPremiumMessagesForbidden
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -130,6 +130,7 @@ public final class EngineChatList: Equatable {
|
||||
public let autoremoveTimeout: Int32?
|
||||
public let storyStats: StoryStats?
|
||||
public let displayAsTopicList: Bool
|
||||
public let isPremiumRequiredToMessage: Bool
|
||||
|
||||
public init(
|
||||
id: Id,
|
||||
@ -149,7 +150,8 @@ public final class EngineChatList: Equatable {
|
||||
isContact: Bool,
|
||||
autoremoveTimeout: Int32?,
|
||||
storyStats: StoryStats?,
|
||||
displayAsTopicList: Bool
|
||||
displayAsTopicList: Bool,
|
||||
isPremiumRequiredToMessage: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.index = index
|
||||
@ -169,6 +171,7 @@ public final class EngineChatList: Equatable {
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
self.storyStats = storyStats
|
||||
self.displayAsTopicList = displayAsTopicList
|
||||
self.isPremiumRequiredToMessage = isPremiumRequiredToMessage
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
@ -226,6 +229,9 @@ public final class EngineChatList: Equatable {
|
||||
if lhs.displayAsTopicList != rhs.displayAsTopicList {
|
||||
return false
|
||||
}
|
||||
if lhs.isPremiumRequiredToMessage != rhs.isPremiumRequiredToMessage {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -424,8 +430,21 @@ public extension EngineChatList.RelativePosition {
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateIsPremiumRequiredToMessage(isPremium: Bool, targetPeer: Peer, cachedIsPremiumRequired: Bool) -> Bool {
|
||||
if isPremium {
|
||||
return false
|
||||
}
|
||||
guard let targetPeer = targetPeer as? TelegramUser else {
|
||||
return false
|
||||
}
|
||||
if !targetPeer.flags.contains(.requirePremium) {
|
||||
return false
|
||||
}
|
||||
return cachedIsPremiumRequired
|
||||
}
|
||||
|
||||
extension EngineChatList.Item {
|
||||
convenience init?(_ entry: ChatListEntry, displayAsTopicList: Bool) {
|
||||
convenience init?(_ entry: ChatListEntry, isPremium: Bool, displayAsTopicList: Bool) {
|
||||
switch entry {
|
||||
case let .MessageEntry(entryData):
|
||||
let index = entryData.index
|
||||
@ -442,6 +461,11 @@ extension EngineChatList.Item {
|
||||
let isContact = entryData.isContact
|
||||
let autoremoveTimeout = entryData.autoremoveTimeout
|
||||
|
||||
var isPremiumRequiredToMessage = false
|
||||
if let targetPeer = renderedPeer.chatMainPeer, let extractedData = entryData.extractedCachedData?.base as? ExtractedChatListItemCachedData {
|
||||
isPremiumRequiredToMessage = calculateIsPremiumRequiredToMessage(isPremium: isPremium, targetPeer: targetPeer, cachedIsPremiumRequired: extractedData.isPremiumRequiredToMessage)
|
||||
}
|
||||
|
||||
var draft: EngineChatList.Draft?
|
||||
if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp {
|
||||
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
||||
@ -511,7 +535,8 @@ extension EngineChatList.Item {
|
||||
isContact: isContact,
|
||||
autoremoveTimeout: autoremoveTimeout,
|
||||
storyStats: entryData.storyStats,
|
||||
displayAsTopicList: displayAsTopicList
|
||||
displayAsTopicList: displayAsTopicList,
|
||||
isPremiumRequiredToMessage: isPremiumRequiredToMessage
|
||||
)
|
||||
case .HoleEntry:
|
||||
return nil
|
||||
@ -551,7 +576,7 @@ extension EngineChatList.AdditionalItem.PromoInfo {
|
||||
|
||||
extension EngineChatList.AdditionalItem {
|
||||
convenience init?(_ entry: ChatListAdditionalItemEntry) {
|
||||
guard let item = EngineChatList.Item(entry.entry, displayAsTopicList: false) else {
|
||||
guard let item = EngineChatList.Item(entry.entry, isPremium: false, displayAsTopicList: false) else {
|
||||
return nil
|
||||
}
|
||||
guard let promoInfo = (entry.info as? PromoChatListItem).flatMap(EngineChatList.AdditionalItem.PromoInfo.init) else {
|
||||
@ -570,11 +595,13 @@ public extension EngineChatList {
|
||||
displaySavedMessagesAsTopicList = value.value
|
||||
}
|
||||
|
||||
let isPremium = view.accountPeer?.isPremium ?? false
|
||||
|
||||
var items: [EngineChatList.Item] = []
|
||||
loop: for entry in view.entries {
|
||||
switch entry {
|
||||
case .MessageEntry:
|
||||
if let item = EngineChatList.Item(entry, displayAsTopicList: entry.index.messageIndex.id.peerId == accountPeerId ? displaySavedMessagesAsTopicList : false) {
|
||||
if let item = EngineChatList.Item(entry, isPremium: isPremium, displayAsTopicList: entry.index.messageIndex.id.peerId == accountPeerId ? displaySavedMessagesAsTopicList : false) {
|
||||
items.append(item)
|
||||
}
|
||||
case .HoleEntry:
|
||||
|
@ -97,6 +97,7 @@ final class CallBackgroundLayer: MetalEngineSubjectLayer, MetalEngineSubject {
|
||||
private let colorSets: [ColorSet]
|
||||
private let colorTransition: AnimatedProperty<ColorSet>
|
||||
private var stateIndex: Int = 0
|
||||
private var isEnergySavingEnabled: Bool = false
|
||||
private let phaseAcceleration = AnimatedProperty<CGFloat>(0.0)
|
||||
|
||||
override init() {
|
||||
@ -169,7 +170,9 @@ final class CallBackgroundLayer: MetalEngineSubjectLayer, MetalEngineSubject {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(stateIndex: Int, transition: Transition) {
|
||||
func update(stateIndex: Int, isEnergySavingEnabled: Bool, transition: Transition) {
|
||||
self.isEnergySavingEnabled = isEnergySavingEnabled
|
||||
|
||||
if self.stateIndex != stateIndex {
|
||||
self.stateIndex = stateIndex
|
||||
if !transition.animation.isImmediate {
|
||||
@ -187,7 +190,7 @@ final class CallBackgroundLayer: MetalEngineSubjectLayer, MetalEngineSubject {
|
||||
return
|
||||
}
|
||||
|
||||
let phase = self.phase
|
||||
let phase = self.isEnergySavingEnabled ? 0.0 : self.phase
|
||||
|
||||
for i in 0 ..< 2 {
|
||||
let isBlur = i == 1
|
||||
|
@ -79,7 +79,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
public var localVideo: VideoSource?
|
||||
public var remoteVideo: VideoSource?
|
||||
public var isRemoteBatteryLow: Bool
|
||||
public var displaySnowEffect: Bool
|
||||
public var isEnergySavingEnabled: Bool
|
||||
|
||||
public init(
|
||||
strings: PresentationStrings,
|
||||
@ -93,7 +93,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
localVideo: VideoSource?,
|
||||
remoteVideo: VideoSource?,
|
||||
isRemoteBatteryLow: Bool,
|
||||
displaySnowEffect: Bool = false
|
||||
isEnergySavingEnabled: Bool
|
||||
) {
|
||||
self.strings = strings
|
||||
self.lifecycleState = lifecycleState
|
||||
@ -106,7 +106,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
self.localVideo = localVideo
|
||||
self.remoteVideo = remoteVideo
|
||||
self.isRemoteBatteryLow = isRemoteBatteryLow
|
||||
self.displaySnowEffect = displaySnowEffect
|
||||
self.isEnergySavingEnabled = isEnergySavingEnabled
|
||||
}
|
||||
|
||||
public static func ==(lhs: State, rhs: State) -> Bool {
|
||||
@ -143,7 +143,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
if lhs.isRemoteBatteryLow != rhs.isRemoteBatteryLow {
|
||||
return false
|
||||
}
|
||||
if lhs.displaySnowEffect != rhs.displaySnowEffect {
|
||||
if lhs.isEnergySavingEnabled != rhs.isEnergySavingEnabled {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -696,7 +696,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
case .terminated:
|
||||
backgroundStateIndex = 0
|
||||
}
|
||||
self.backgroundLayer.update(stateIndex: backgroundStateIndex, transition: transition)
|
||||
self.backgroundLayer.update(stateIndex: backgroundStateIndex, isEnergySavingEnabled: params.state.isEnergySavingEnabled, transition: transition)
|
||||
|
||||
transition.setFrame(view: self.buttonGroupView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||
|
||||
@ -1220,7 +1220,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
titleString = params.state.strings.Call_StatusMissed
|
||||
}
|
||||
default:
|
||||
displayAudioLevelBlob = !params.state.isRemoteAudioMuted
|
||||
displayAudioLevelBlob = !params.state.isRemoteAudioMuted && !params.state.isEnergySavingEnabled
|
||||
|
||||
self.titleView.contentMode = .scaleToFill
|
||||
titleString = params.state.name
|
||||
@ -1375,24 +1375,6 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*if params.state.displaySnowEffect {
|
||||
let snowEffectView: SnowEffectView
|
||||
if let current = self.snowEffectView {
|
||||
snowEffectView = current
|
||||
} else {
|
||||
snowEffectView = SnowEffectView(frame: CGRect())
|
||||
self.snowEffectView = snowEffectView
|
||||
self.maskContents.addSubview(snowEffectView)
|
||||
}
|
||||
transition.setFrame(view: snowEffectView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||
snowEffectView.update(size: params.size)
|
||||
} else {
|
||||
if let snowEffectView = self.snowEffectView {
|
||||
self.snowEffectView = nil
|
||||
snowEffectView.removeFromSuperview()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.edited = edited
|
||||
self.impressionCount = impressionCount
|
||||
self.impressionCount = impressionCount == 0 ? nil : impressionCount
|
||||
self.dateText = dateText
|
||||
self.type = type
|
||||
self.layoutInput = layoutInput
|
||||
|
@ -78,6 +78,7 @@ private let TitleNodeStateExpanded = 1
|
||||
|
||||
final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
private var context: AccountContext
|
||||
private let isPremiumDisabled: Bool
|
||||
private weak var controller: PeerInfoScreenImpl?
|
||||
private var presentationData: PresentationData?
|
||||
private var state: PeerInfoState?
|
||||
@ -186,6 +187,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.forumTopicThreadId = forumTopicThreadId
|
||||
self.chatLocation = chatLocation
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
self.isPremiumDisabled = premiumConfiguration.isPremiumDisabled
|
||||
|
||||
self.avatarClippingNode = SparseNode()
|
||||
self.avatarClippingNode.alpha = 0.996
|
||||
self.avatarClippingNode.clipsToBounds = true
|
||||
@ -1249,7 +1253,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||
|
||||
if let statusData, statusData.isHiddenStatus {
|
||||
if let statusData, statusData.isHiddenStatus, !self.isPremiumDisabled {
|
||||
let subtitleBadgeView: PeerInfoSubtitleBadgeView
|
||||
if let current = self.subtitleBadgeView {
|
||||
subtitleBadgeView = current
|
||||
|
@ -253,6 +253,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
self.chatListNode?.disabledPeerSelected = { [weak self] peer, threadId, reason in
|
||||
self?.requestOpenDisabledPeer?(peer, threadId, reason)
|
||||
}
|
||||
self.mainContainerNode?.disabledPeerSelected = { [weak self] peer, threadId, reason in
|
||||
self?.requestOpenDisabledPeer?(peer, threadId, reason)
|
||||
}
|
||||
|
||||
self.chatListNode?.contentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -1748,6 +1748,14 @@ public func preloadStoryMedia(context: AccountContext, info: StoryPreloadInfo) -
|
||||
}
|
||||
}
|
||||
|
||||
if let representation = file.previewRepresentations.first {
|
||||
signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(info.peer.id), userContentType: .story, reference: .media(media: .story(peer: info.peer, id: info.storyId, media: selectedMedia._asMedia()), resource: representation.resource), range: nil)
|
||||
|> ignoreValues
|
||||
|> `catch` { _ -> Signal<Never, NoError> in
|
||||
return .complete()
|
||||
})
|
||||
}
|
||||
|
||||
signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(info.peer.id), userContentType: .story, reference: .media(media: .story(peer: info.peer, id: info.storyId, media: selectedMedia._asMedia()), resource: file.resource), range: fetchRange)
|
||||
|> ignoreValues
|
||||
|> `catch` { _ -> Signal<Never, NoError> in
|
||||
|
@ -164,8 +164,18 @@ final class StoryItemImageView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
self.disposable = (context.account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedVideoFirstFrameRepresentation(), complete: true, fetch: true, attemptSynchronously: false)
|
||||
|> map { result -> UIImage? in
|
||||
let fullSize = context.account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedVideoFirstFrameRepresentation(), complete: true, fetch: true, attemptSynchronously: false)
|
||||
var previewSize: Signal<MediaResourceData?, NoError> = .single(nil)
|
||||
if let representation = file.previewRepresentations.first {
|
||||
previewSize = context.account.postbox.mediaBox.resourceData(representation.resource, option: .complete(waitUntilFetchStatus: false))
|
||||
|> map(Optional.init)
|
||||
}
|
||||
|
||||
self.disposable = (combineLatest(
|
||||
fullSize,
|
||||
previewSize
|
||||
)
|
||||
|> map { result, previewResult -> UIImage? in
|
||||
if result.complete {
|
||||
if #available(iOS 15.0, *) {
|
||||
if let image = UIImage(contentsOfFile: result.path)?.preparingForDisplay() {
|
||||
@ -180,6 +190,20 @@ final class StoryItemImageView: UIView {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else if let previewResult, previewResult.complete {
|
||||
if #available(iOS 15.0, *) {
|
||||
if let image = UIImage(contentsOfFile: previewResult.path)?.preparingForDisplay() {
|
||||
return image
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if let image = UIImage(contentsOfFile: previewResult.path)?.precomposed() {
|
||||
return image
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -577,7 +577,14 @@ func moveReplyMessageToAnotherChat(selfController: ChatControllerImpl, replySubj
|
||||
}
|
||||
return true
|
||||
}
|
||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action, timeout: nil, linkAction: { _ in
|
||||
|
||||
var hasAction = false
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: selfController.context.currentAppConfiguration.with { $0 })
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
hasAction = true
|
||||
}
|
||||
|
||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: hasAction ? presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
|
||||
}), elevatedLayout: false, animateInAsReplacement: true, action: { [weak selfController, weak controller] action in
|
||||
guard let selfController, let controller else {
|
||||
return false
|
||||
|
@ -11230,6 +11230,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .voiceMessagesForbidden:
|
||||
strongSelf.interfaceInteraction?.displayRestrictedInfo(.premiumVoiceMessages, .alert)
|
||||
return
|
||||
case .nonPremiumMessagesForbidden:
|
||||
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer {
|
||||
text = strongSelf.presentationData.strings.Conversation_SendMessageErrorNonPremiumForbidden(EnginePeer(peer).compactDisplayTitle).string
|
||||
moreInfo = false
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
let actions: [TextAlertAction]
|
||||
if moreInfo {
|
||||
@ -11990,7 +11997,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if replyThreadMessage.peerId == self.context.account.peerId && replyThreadMessage.threadId == self.context.account.peerId.toInt64() {
|
||||
peerId = replyThreadMessage.peerId
|
||||
threadId = nil
|
||||
includeScrollState = false
|
||||
includeScrollState = true
|
||||
|
||||
let scrollState = self.chatDisplayNode.historyNode.immediateScrollState()
|
||||
let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: replyThreadMessage.threadId, { current in
|
||||
return current.withUpdatedHistoryScrollState(scrollState)
|
||||
}).startStandalone()
|
||||
} else {
|
||||
peerId = replyThreadMessage.peerId
|
||||
threadId = replyThreadMessage.threadId
|
||||
@ -12001,7 +12013,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
var interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp)
|
||||
if includeScrollState && threadId == nil {
|
||||
if includeScrollState {
|
||||
let scrollState = self.chatDisplayNode.historyNode.immediateScrollState()
|
||||
interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState)
|
||||
}
|
||||
|
@ -70,7 +70,14 @@ extension ChatControllerImpl {
|
||||
}
|
||||
return true
|
||||
}
|
||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action, timeout: nil, linkAction: { _ in
|
||||
|
||||
var hasAction = false
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
hasAction = true
|
||||
}
|
||||
|
||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: hasAction ? presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
|
||||
}), elevatedLayout: false, animateInAsReplacement: true, action: { [weak controller] action in
|
||||
guard let self, let controller else {
|
||||
return false
|
||||
|
@ -898,6 +898,7 @@ final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeContent,
|
||||
}
|
||||
|
||||
final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNodeContent {
|
||||
private let isPremiumDisabled: Bool
|
||||
private let interaction: ChatPanelInterfaceInteraction?
|
||||
|
||||
private let iconBackground: SimpleLayer
|
||||
@ -910,7 +911,10 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
private var currentTheme: PresentationTheme?
|
||||
private var currentStrings: PresentationStrings?
|
||||
|
||||
init(interaction: ChatPanelInterfaceInteraction?) {
|
||||
init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?) {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
self.isPremiumDisabled = premiumConfiguration.isPremiumDisabled
|
||||
|
||||
self.interaction = interaction
|
||||
|
||||
self.iconBackground = SimpleLayer()
|
||||
@ -927,6 +931,7 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
self.layer.addSublayer(self.iconBackground)
|
||||
self.view.addSubview(self.icon)
|
||||
|
||||
if !self.isPremiumDisabled {
|
||||
self.view.addSubview(self.button)
|
||||
|
||||
self.button.addSubnode(self.buttonStarsNode)
|
||||
@ -945,6 +950,7 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
}
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
if let interaction = self.interaction {
|
||||
@ -954,13 +960,6 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
|
||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||
/*if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_EmptyTopicPlaceholder_Title, font: titleFont, textColor: serviceColor.primaryText)
|
||||
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_EmptyTopicPlaceholder_Text, font: messageFont, textColor: serviceColor.primaryText)
|
||||
}*/
|
||||
|
||||
let maxWidth = min(200.0, size.width)
|
||||
|
||||
@ -978,7 +977,12 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
peerTitle = " "
|
||||
}
|
||||
|
||||
let text = interfaceState.strings.Chat_EmptyStateMessagingRestrictedToPremium_Text(peerTitle).string
|
||||
let text: String
|
||||
if self.isPremiumDisabled {
|
||||
text = interfaceState.strings.Chat_EmptyStateMessagingRestrictedToPremiumDisabled_Text(peerTitle).string
|
||||
} else {
|
||||
text = interfaceState.strings.Chat_EmptyStateMessagingRestrictedToPremium_Text(peerTitle).string
|
||||
}
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BalancedTextComponent(
|
||||
@ -1010,7 +1014,10 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
var contentsWidth: CGFloat = 0.0
|
||||
contentsWidth = max(contentsWidth, iconBackgroundSize + sideInset * 2.0)
|
||||
contentsWidth = max(contentsWidth, textSize.width + sideInset * 2.0)
|
||||
|
||||
if !self.isPremiumDisabled {
|
||||
contentsWidth = max(contentsWidth, buttonSize.width + sideInset * 2.0)
|
||||
}
|
||||
|
||||
var contentsHeight: CGFloat = 0.0
|
||||
contentsHeight += topInset
|
||||
@ -1036,6 +1043,10 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
textView.frame = textFrame
|
||||
}
|
||||
contentsHeight += textSize.height
|
||||
|
||||
if self.isPremiumDisabled {
|
||||
contentsHeight += bottomInset
|
||||
} else {
|
||||
contentsHeight += textButtonSpacing
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: floor((contentsWidth - buttonSize.width) * 0.5), y: contentsHeight), size: buttonSize)
|
||||
@ -1052,6 +1063,8 @@ final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatEmptyNod
|
||||
self.buttonStarsNode.frame = CGRect(origin: CGPoint(), size: buttonSize)
|
||||
contentsHeight += buttonSize.height
|
||||
contentsHeight += bottomInset
|
||||
}
|
||||
|
||||
|
||||
return CGSize(width: contentsWidth, height: contentsHeight)
|
||||
}
|
||||
@ -1218,7 +1231,7 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
case .topic:
|
||||
node = ChatEmptyNodeTopicChatContent(context: self.context)
|
||||
case .premiumRequired:
|
||||
node = ChatEmptyNodePremiumRequiredChatContent(interaction: self.interaction)
|
||||
node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction)
|
||||
}
|
||||
self.content = (contentType, node)
|
||||
self.addSubnode(node)
|
||||
|
@ -89,7 +89,6 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess
|
||||
requestAroundId = true
|
||||
}
|
||||
if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId {
|
||||
requestAroundId = true
|
||||
preFixedReadState = .peer([:])
|
||||
}
|
||||
|
||||
@ -114,7 +113,11 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess
|
||||
|
||||
let canScrollToRead: Bool
|
||||
if case let .replyThread(message) = chatLocation, !message.isForumPost {
|
||||
if message.peerId == context.account.peerId {
|
||||
canScrollToRead = false
|
||||
} else {
|
||||
canScrollToRead = true
|
||||
}
|
||||
} else if view.isAddedToChatList {
|
||||
canScrollToRead = true
|
||||
} else {
|
||||
|
@ -175,7 +175,7 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: EngineCo
|
||||
return false
|
||||
}
|
||||
|
||||
private func canViewReadStats(message: Message, participantCount: Int?, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
|
||||
private func canViewReadStats(message: Message, participantCount: Int?, isMessageRead: Bool, isPremium: Bool, appConfig: AppConfiguration) -> Bool {
|
||||
guard let peer = message.peers[message.id.peerId] else {
|
||||
return false
|
||||
}
|
||||
@ -251,6 +251,13 @@ private func canViewReadStats(message: Message, participantCount: Int?, isMessag
|
||||
if user.flags.contains(.isSupport) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !isPremium {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: appConfig)
|
||||
if premiumConfiguration.isPremiumDisabled {
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@ -1731,7 +1738,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
let canViewStats: Bool
|
||||
if let messageReadStatsAreHidden = infoSummaryData.messageReadStatsAreHidden, !messageReadStatsAreHidden {
|
||||
canViewStats = canViewReadStats(message: message, participantCount: infoSummaryData.participantCount, isMessageRead: isMessageRead, appConfig: appConfig)
|
||||
canViewStats = canViewReadStats(message: message, participantCount: infoSummaryData.participantCount, isMessageRead: isMessageRead, isPremium: isPremium, appConfig: appConfig)
|
||||
} else {
|
||||
canViewStats = false
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import PlainButtonComponent
|
||||
import ComponentDisplayAdapters
|
||||
import AccountContext
|
||||
|
||||
private let labelFont = Font.regular(15.0)
|
||||
|
||||
@ -101,18 +102,24 @@ final class ChatPremiumRequiredInputPanelNode: ChatInputPanelNode {
|
||||
let buttonTitle: String = params.interfaceState.strings.Chat_MessagingRestrictedPlaceholder(peerTitle).string
|
||||
let buttonSubtitle: String = params.interfaceState.strings.Chat_MessagingRestrictedPlaceholderAction
|
||||
|
||||
var buttonContents: [AnyComponentWithIdentity<Empty>] = []
|
||||
buttonContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: buttonTitle, font: Font.regular(13.0), textColor: params.interfaceState.theme.rootController.navigationBar.secondaryTextColor))
|
||||
))))
|
||||
if let context = self.context {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
buttonContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: buttonSubtitle, font: Font.regular(13.0), textColor: params.interfaceState.theme.rootController.navigationBar.accentTextColor))
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
let size = CGSize(width: params.width - params.additionalSideInsets.left * 2.0 - params.leftInset * 2.0, height: height)
|
||||
let buttonSize = self.button.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: buttonTitle, font: Font.regular(13.0), textColor: params.interfaceState.theme.rootController.navigationBar.secondaryTextColor))
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: buttonSubtitle, font: Font.regular(13.0), textColor: params.interfaceState.theme.rootController.navigationBar.accentTextColor))
|
||||
)))
|
||||
], spacing: 1.0)),
|
||||
content: AnyComponent(VStack(buttonContents, spacing: 1.0)),
|
||||
effectAlignment: .center,
|
||||
minSize: size,
|
||||
action: { [weak self] in
|
||||
|
@ -384,7 +384,14 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
}
|
||||
return true
|
||||
}
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action, timeout: nil, linkAction: { _ in
|
||||
|
||||
var hasAction = false
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
hasAction = true
|
||||
}
|
||||
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: hasAction ? self.presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
|
||||
}), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
|
Loading…
x
Reference in New Issue
Block a user