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