mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add empty search result chat list footer
This commit is contained in:
parent
a5579103f4
commit
dea8f6b48c
@ -593,8 +593,10 @@ private struct NotificationContent: CustomStringConvertible {
|
||||
if !self.userInfo.isEmpty {
|
||||
content.userInfo = self.userInfo
|
||||
}
|
||||
if !self.attachments.isEmpty {
|
||||
content.attachments = self.attachments
|
||||
if self.isLockedMessage == nil {
|
||||
if !self.attachments.isEmpty {
|
||||
content.attachments = self.attachments
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
|
@ -13576,3 +13576,8 @@ Sorry for the inconvenience.";
|
||||
"Notification.StarGift.Subtitle.Downgraded" = "This gift was downgraded because a request to refund the payment related to this gift was made, and the money was returned.";
|
||||
|
||||
"Gift.View.KeepOrUpgradeDescription" = "You can keep this gift or upgrade it.";
|
||||
|
||||
"VideoChat.IncomingVideoQuality.AudioOnly" = "Audio Only";
|
||||
"VideoChat.IncomingVideoQuality.Title" = "Receive Video Quality";
|
||||
|
||||
"ChatList.EmptyResult.SearchInAll" = "Search in All Messages";
|
||||
|
@ -111,6 +111,7 @@ swift_library(
|
||||
"//submodules/ComposePollUI",
|
||||
"//submodules/ChatPresentationInterfaceState",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -380,6 +380,7 @@ public enum ChatListSearchEntryStableId: Hashable {
|
||||
case globalPeerId(EnginePeer.Id)
|
||||
case messageId(EngineMessage.Id, ChatListSearchEntry.MessageSection)
|
||||
case messagePlaceholder(Int32)
|
||||
case emptyMessagesFooter
|
||||
case addContact
|
||||
}
|
||||
|
||||
@ -442,6 +443,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool, String?)
|
||||
case message(EngineMessage, EngineRenderedPeer, EnginePeerReadCounters?, EngineMessageHistoryThread.Info?, ChatListPresentationData, Int32, Bool?, Bool, MessageOrderingKey, (id: String, size: Int64, isFirstInList: Bool)?, MessageSection, Bool, PeerStoryStats?, Bool, TelegramSearchPeersScope)
|
||||
case messagePlaceholder(Int32, ChatListPresentationData, TelegramSearchPeersScope)
|
||||
case emptyMessagesFooter(ChatListPresentationData, TelegramSearchPeersScope, String?)
|
||||
case addContact(String, PresentationTheme, PresentationStrings)
|
||||
|
||||
public var stableId: ChatListSearchEntryStableId {
|
||||
@ -458,6 +460,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return .messageId(message.id, section)
|
||||
case let .messagePlaceholder(index, _, _):
|
||||
return .messagePlaceholder(index)
|
||||
case .emptyMessagesFooter:
|
||||
return .emptyMessagesFooter
|
||||
case .addContact:
|
||||
return .addContact
|
||||
}
|
||||
@ -561,6 +565,21 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .emptyMessagesFooter(lhsPresentationData, lhsSearchScope, lhsQuery):
|
||||
if case let .emptyMessagesFooter(rhsPresentationData, rhsSearchScope, rhsQuery) = rhs {
|
||||
if lhsPresentationData !== rhsPresentationData {
|
||||
return false
|
||||
}
|
||||
if lhsSearchScope != rhsSearchScope {
|
||||
return false
|
||||
}
|
||||
if lhsQuery != rhsQuery {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .addContact(lhsPhoneNumber, lhsTheme, lhsStrings):
|
||||
if case let .addContact(rhsPhoneNumber, rhsTheme, rhsStrings) = rhs {
|
||||
if lhsPhoneNumber != rhsPhoneNumber {
|
||||
@ -601,7 +620,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return false
|
||||
case let .localPeer(_, _, _, rhsIndex, _, _, _, _, _, _, _):
|
||||
return lhsIndex <= rhsIndex
|
||||
case .globalPeer, .message, .messagePlaceholder, .addContact:
|
||||
case .globalPeer, .message, .messagePlaceholder, .emptyMessagesFooter, .addContact:
|
||||
return true
|
||||
}
|
||||
case let .globalPeer(_, _, lhsIndex, _, _, _, _, _, _, _, _):
|
||||
@ -610,7 +629,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return false
|
||||
case let .globalPeer(_, _, rhsIndex, _, _, _, _, _, _, _, _):
|
||||
return lhsIndex <= rhsIndex
|
||||
case .message, .messagePlaceholder, .addContact:
|
||||
case .message, .messagePlaceholder, .emptyMessagesFooter, .addContact:
|
||||
return true
|
||||
}
|
||||
case let .message(_, _, _, _, _, _, _, _, lhsKey, _, _, _, _, _, _):
|
||||
@ -618,6 +637,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return lhsKey < rhsKey
|
||||
} else if case .messagePlaceholder = rhs {
|
||||
return true
|
||||
} else if case .emptyMessagesFooter = rhs {
|
||||
return true
|
||||
} else if case .addContact = rhs {
|
||||
return true
|
||||
} else {
|
||||
@ -626,11 +647,19 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case let .messagePlaceholder(lhsIndex, _, _):
|
||||
if case let .messagePlaceholder(rhsIndex, _, _) = rhs {
|
||||
return lhsIndex < rhsIndex
|
||||
} else if case .emptyMessagesFooter = rhs {
|
||||
return true
|
||||
} else if case .addContact = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .emptyMessagesFooter:
|
||||
if case .addContact = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .addContact:
|
||||
return false
|
||||
}
|
||||
@ -658,7 +687,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
toggleAllPaused: @escaping () -> Void,
|
||||
openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void,
|
||||
openPublicPosts: @escaping () -> Void,
|
||||
openMessagesFilter: @escaping (ASDisplayNode) -> Void
|
||||
openMessagesFilter: @escaping (ASDisplayNode) -> Void,
|
||||
switchMessagesFilter: @escaping (TelegramSearchPeersScope) -> Void
|
||||
) -> ListViewItem {
|
||||
switch self {
|
||||
case let .topic(peer, threadInfo, _, theme, strings, expandType):
|
||||
@ -1128,6 +1158,33 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
openMessagesFilter(sourceNode)
|
||||
})
|
||||
return ChatListItem(presentationData: presentationData, context: context, chatListLocation: location, filterData: nil, index: EngineChatList.Item.Index.chatList(ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Cloud, id: 0), timestamp: 0))), content: .loading, editing: false, hasActiveRevealControls: false, selected: false, header: header, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
case let .emptyMessagesFooter(presentationData, searchScope, searchQuery):
|
||||
var actionTitle: String?
|
||||
let filterTitle: String
|
||||
switch searchScope {
|
||||
case .everywhere:
|
||||
filterTitle = presentationData.strings.ChatList_Search_Messages_AllChats
|
||||
case .channels:
|
||||
filterTitle = presentationData.strings.ChatList_Search_Messages_Channels
|
||||
case .groups:
|
||||
filterTitle = presentationData.strings.ChatList_Search_Messages_GroupChats
|
||||
case .privateChats:
|
||||
filterTitle = presentationData.strings.ChatList_Search_Messages_PrivateChats
|
||||
}
|
||||
actionTitle = "\(filterTitle) <"
|
||||
|
||||
let header = ChatListSearchItemHeader(type: .messages(location: nil), theme: presentationData.theme, strings: presentationData.strings, actionTitle: actionTitle, action: { sourceNode in
|
||||
openMessagesFilter(sourceNode)
|
||||
})
|
||||
return ChatListSearchEmptyFooterItem(
|
||||
theme: presentationData.theme,
|
||||
strings: presentationData.strings,
|
||||
header: header,
|
||||
searchQuery: searchQuery,
|
||||
searchAllMessages: searchScope == .everywhere ? nil : {
|
||||
switchMessagesFilter(.everywhere)
|
||||
}
|
||||
)
|
||||
case let .addContact(phoneNumber, theme, strings):
|
||||
return ContactsAddItem(context: context, theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
interaction.addContact(phoneNumber)
|
||||
@ -1201,12 +1258,42 @@ private func chatListSearchContainerPreparedRecentTransition(
|
||||
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates, isEmpty: isEmpty)
|
||||
}
|
||||
|
||||
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void, openPublicPosts: @escaping () -> Void, openMessagesFilter: @escaping (ASDisplayNode) -> Void) -> ChatListSearchContainerTransition {
|
||||
public func chatListSearchContainerPreparedTransition(
|
||||
from fromEntries: [ChatListSearchEntry],
|
||||
to toEntries: [ChatListSearchEntry],
|
||||
displayingResults: Bool,
|
||||
isEmpty: Bool,
|
||||
isLoading: Bool,
|
||||
animated: Bool,
|
||||
context: AccountContext,
|
||||
presentationData: PresentationData,
|
||||
enableHeaders: Bool,
|
||||
filter: ChatListNodePeersFilter,
|
||||
requestPeerType: [ReplyMarkupButtonRequestPeerType]?,
|
||||
location: ChatListControllerLocation,
|
||||
key: ChatListSearchPaneKey,
|
||||
tagMask: EngineMessage.Tags?,
|
||||
interaction: ChatListNodeInteraction,
|
||||
listInteraction: ListMessageItemInteraction,
|
||||
peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?,
|
||||
toggleExpandLocalResults: @escaping () -> Void,
|
||||
toggleExpandGlobalResults: @escaping () -> Void,
|
||||
searchPeer: @escaping (EnginePeer) -> Void,
|
||||
searchQuery: String?,
|
||||
searchOptions: ChatListSearchOptions?,
|
||||
messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?,
|
||||
openClearRecentlyDownloaded: @escaping () -> Void,
|
||||
toggleAllPaused: @escaping () -> Void,
|
||||
openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void,
|
||||
openPublicPosts: @escaping () -> Void,
|
||||
openMessagesFilter: @escaping (ASDisplayNode) -> Void,
|
||||
switchMessagesFilter: @escaping (TelegramSearchPeersScope) -> Void
|
||||
) -> ChatListSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter, switchMessagesFilter: switchMessagesFilter), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter, switchMessagesFilter: switchMessagesFilter), directionHint: nil) }
|
||||
|
||||
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, animated: animated)
|
||||
}
|
||||
@ -2912,6 +2999,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
index += 1
|
||||
}
|
||||
} else {
|
||||
var hasAnyMessages = false
|
||||
for foundRemoteMessageSet in foundRemoteMessages.0 {
|
||||
for message in foundRemoteMessageSet.messages {
|
||||
if existingMessageIds.contains(message.id) {
|
||||
@ -2936,10 +3024,22 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
|
||||
//TODO:requiresPremiumForMessaging
|
||||
hasAnyMessages = true
|
||||
entries.append(.message(message, peer, foundRemoteMessageSet.readCounters[message.id.peerId], foundRemoteMessageSet.threadsData[message.id]?.info, presentationData, foundRemoteMessageSet.totalCount, selectionState?.contains(message.id), headerId == firstHeaderId, .index(message.index), nil, .generic, false, nil, false, searchScope))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
if !hasAnyMessages {
|
||||
switch searchScope {
|
||||
case .everywhere:
|
||||
break
|
||||
default:
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data, data["ios_killswitch_empty_search_footer"] != nil {
|
||||
} else {
|
||||
entries.append(.emptyMessagesFooter(presentationData, searchScope, query))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3444,6 +3544,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
strongSelf.interaction.switchToFilter(.publicPosts)
|
||||
}, openMessagesFilter: { sourceNode in
|
||||
strongSelf.openMessagesFilter(sourceNode: sourceNode)
|
||||
}, switchMessagesFilter: { filter in
|
||||
strongSelf.searchScopePromise.set(.everywhere)
|
||||
})
|
||||
strongSelf.currentEntries = newEntries
|
||||
if strongSelf.key == .downloads {
|
||||
|
@ -4,6 +4,8 @@ import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import LottieComponent
|
||||
|
||||
class ChatListHoleItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
@ -78,3 +80,252 @@ class ChatListHoleItemNode: ListViewItemNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChatListSearchEmptyFooterItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let searchQuery: String?
|
||||
let searchAllMessages: (() -> Void)?
|
||||
|
||||
let header: ListViewItemHeader?
|
||||
let selectable: Bool = false
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, header: ListViewItemHeader?, searchQuery: String?, searchAllMessages: (() -> Void)?) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.header = header
|
||||
self.searchQuery = searchQuery
|
||||
self.searchAllMessages = searchAllMessages
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = ChatListSearchEmptyFooterItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params)
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
assert(node() is ChatListSearchEmptyFooterItemNode)
|
||||
if let nodeValue = node() as? ChatListSearchEmptyFooterItemNode {
|
||||
|
||||
let layout = nodeValue.asyncLayout()
|
||||
async {
|
||||
let (nodeLayout, apply) = layout(self, params)
|
||||
Queue.mainQueue().async {
|
||||
completion(nodeLayout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChatListSearchEmptyFooterItemNode: ListViewItemNode {
|
||||
private let contentNode: ASDisplayNode
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
private let searchAllMessagesButton: HighlightableButtonNode
|
||||
private let searchAllMessagesTitle: TextNode
|
||||
|
||||
private let icon = ComponentView<Empty>()
|
||||
|
||||
private var item: ChatListSearchEmptyFooterItem?
|
||||
|
||||
required init() {
|
||||
self.contentNode = ASDisplayNode()
|
||||
self.titleNode = TextNode()
|
||||
self.textNode = TextNode()
|
||||
|
||||
self.searchAllMessagesButton = HighlightableButtonNode()
|
||||
self.searchAllMessagesTitle = TextNode()
|
||||
self.searchAllMessagesTitle.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
self.contentNode.addSubnode(self.titleNode)
|
||||
self.contentNode.addSubnode(self.textNode)
|
||||
|
||||
self.contentNode.addSubnode(self.searchAllMessagesButton)
|
||||
self.searchAllMessagesButton.addSubnode(self.searchAllMessagesTitle)
|
||||
|
||||
self.searchAllMessagesButton.addTarget(self, action: #selector(self.searchAllMessagesButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.wantsTrailingItemSpaceUpdates = true
|
||||
}
|
||||
|
||||
@objc private func searchAllMessagesButtonPressed() {
|
||||
self.item?.searchAllMessages?()
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
let layout = self.asyncLayout()
|
||||
let (_, apply) = layout(item as! ChatListSearchEmptyFooterItem, params)
|
||||
apply()
|
||||
}
|
||||
|
||||
override func headers() -> [ListViewItemHeader]? {
|
||||
if let item = self.item {
|
||||
return item.header.flatMap { [$0] }
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func updateTrailingItemSpace(_ trailingItemSpace: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var contentFrame = self.contentNode.frame
|
||||
contentFrame.origin.y = max(0.0, floor(trailingItemSpace * 0.5))
|
||||
self.contentNode.frame = contentFrame
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ChatListSearchEmptyFooterItem, _ params: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleNodeLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextNodeLayout = TextNode.asyncLayout(self.textNode)
|
||||
let makeSearchAllMessagesTitleLayout = TextNode.asyncLayout(self.searchAllMessagesTitle)
|
||||
|
||||
return { [weak self] item, params in
|
||||
let titleLayout = makeTitleNodeLayout(TextNodeLayoutArguments(
|
||||
attributedString: NSAttributedString(string: item.strings.ChatList_Search_NoResults, font: Font.semibold(17.0), textColor: item.theme.list.freeTextColor),
|
||||
maximumNumberOfLines: 1,
|
||||
truncationType: .end,
|
||||
constrainedSize: CGSize(width: params.width - params.leftInset * 2.0 - 12.0 * 2.0, height: 1000.0)
|
||||
))
|
||||
|
||||
let textValue: String
|
||||
if let searchQuery = item.searchQuery {
|
||||
textValue = item.strings.ChatList_Search_NoResultsQueryDescription(searchQuery).string
|
||||
} else {
|
||||
textValue = item.strings.ChatList_Search_NoResults
|
||||
}
|
||||
|
||||
let textLayout = makeTextNodeLayout(TextNodeLayoutArguments(
|
||||
attributedString: NSAttributedString(string: textValue, font: Font.regular(16.0), textColor: item.theme.list.freeTextColor),
|
||||
maximumNumberOfLines: 0,
|
||||
truncationType: .end,
|
||||
constrainedSize: CGSize(width: params.width - params.leftInset * 2.0 - 12.0 * 2.0, height: 1000.0),
|
||||
alignment: .center,
|
||||
lineSpacing: 0.1
|
||||
))
|
||||
|
||||
let searchAllMessagesTitleLayout = makeSearchAllMessagesTitleLayout(TextNodeLayoutArguments(
|
||||
attributedString: NSAttributedString(string: item.strings.ChatList_EmptyResult_SearchInAll, font: Font.regular(17.0), textColor: item.theme.list.itemAccentColor),
|
||||
maximumNumberOfLines: 1,
|
||||
truncationType: .end,
|
||||
constrainedSize: CGSize(width: params.width - params.leftInset * 2.0 - 12.0 * 2.0, height: 1000.0)
|
||||
))
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let topInset: CGFloat = 40.0
|
||||
let bottomInset: CGFloat = 10.0
|
||||
let iconSpacing: CGFloat = 20.0
|
||||
let titleSpacing: CGFloat = 6.0
|
||||
|
||||
let buttonSpacing: CGFloat = 14.0
|
||||
let buttonInset: CGFloat = 11.0
|
||||
|
||||
let iconSize = CGSize(width: 128.0, height: 128.0)
|
||||
|
||||
contentHeight += topInset
|
||||
contentHeight += iconSize.height
|
||||
contentHeight += iconSpacing
|
||||
contentHeight += titleLayout.0.size.height
|
||||
contentHeight += titleSpacing
|
||||
contentHeight += textLayout.0.size.height
|
||||
|
||||
if item.searchAllMessages != nil {
|
||||
contentHeight += buttonSpacing
|
||||
contentHeight += buttonInset
|
||||
contentHeight += searchAllMessagesTitleLayout.0.size.height
|
||||
contentHeight += buttonInset
|
||||
}
|
||||
|
||||
contentHeight += bottomInset
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: contentHeight), insets: UIEdgeInsets())
|
||||
|
||||
return (layout, { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.item = item
|
||||
self.contentSize = layout.contentSize
|
||||
self.insets = layout.insets
|
||||
|
||||
let _ = titleLayout.1()
|
||||
let _ = textLayout.1()
|
||||
let _ = searchAllMessagesTitleLayout.1()
|
||||
|
||||
var contentY: CGFloat = 0.0
|
||||
contentY += topInset
|
||||
|
||||
let _ = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(
|
||||
name: "ChatListNoResults"
|
||||
),
|
||||
color: nil,
|
||||
placeholderColor: nil,
|
||||
startingPosition: .begin,
|
||||
size: iconSize,
|
||||
renderingScale: nil,
|
||||
loop: false,
|
||||
playOnce: nil
|
||||
)),
|
||||
environment: {}, containerSize: iconSize
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((params.width - iconSize.width) * 0.5), y: contentY), size: iconSize)
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.contentNode.view.addSubview(iconView)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
contentY += iconSize.height
|
||||
contentY += iconSpacing
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.0.size.width) * 0.5), y: contentY), size: titleLayout.0.size)
|
||||
self.titleNode.frame = titleFrame
|
||||
contentY += titleLayout.0.size.height
|
||||
contentY += titleSpacing
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: contentY), size: textLayout.0.size)
|
||||
self.textNode.frame = textFrame
|
||||
contentY += textLayout.0.size.height
|
||||
|
||||
if item.searchAllMessages != nil {
|
||||
contentY += buttonSpacing
|
||||
let searchAllMessagesButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - searchAllMessagesTitleLayout.0.size.width) * 0.5), y: contentY), size: CGSize(width: searchAllMessagesTitleLayout.0.size.width, height: searchAllMessagesTitleLayout.0.size.height + buttonInset * 2.0))
|
||||
contentY += searchAllMessagesTitleLayout.0.size.height + buttonInset * 2.0
|
||||
|
||||
self.searchAllMessagesButton.frame = searchAllMessagesButtonFrame
|
||||
self.searchAllMessagesTitle.frame = CGRect(origin: CGPoint(x: 0.0, y: buttonInset), size: searchAllMessagesTitleLayout.0.size)
|
||||
contentY += buttonInset
|
||||
contentY += searchAllMessagesTitleLayout.0.size.height
|
||||
contentY += buttonInset
|
||||
}
|
||||
|
||||
contentY += bottomInset
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: self.contentNode.frame.minY), size: CGSize(width: params.width, height: contentHeight))
|
||||
self.contentNode.frame = contentFrame
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -419,7 +419,12 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
alphaTransition.setAlpha(view: titleView, alpha: controlsAlpha)
|
||||
}
|
||||
|
||||
let videoDescription: GroupCallParticipantsContext.Participant.VideoDescription? = component.maxVideoQuality == 0 ? nil : (component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription)
|
||||
let videoDescription: GroupCallParticipantsContext.Participant.VideoDescription?
|
||||
if component.isMyPeer && component.isPresentation {
|
||||
videoDescription = nil
|
||||
} else {
|
||||
videoDescription = component.maxVideoQuality == 0 ? nil : (component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription)
|
||||
}
|
||||
|
||||
var isEffectivelyPaused = false
|
||||
if let videoDescription, videoDescription.isPaused {
|
||||
|
@ -152,16 +152,15 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let qualityList: [(Int, String)] = [
|
||||
(0, "Audio Only"),
|
||||
(0, environment.strings.VideoChat_IncomingVideoQuality_AudioOnly),
|
||||
(180, "180p"),
|
||||
(360, "360p"),
|
||||
(Int.max, "720p")
|
||||
]
|
||||
|
||||
let videoQualityTitle = qualityList.first(where: { $0.0 == self.maxVideoQuality })?.1 ?? ""
|
||||
items.append(.action(ContextMenuActionItem(text: "Receive Video Quality", textColor: .primary, textLayout: .secondLineWithValue(videoQualityTitle), icon: { _ in
|
||||
items.append(.action(ContextMenuActionItem(text: environment.strings.VideoChat_IncomingVideoQuality_Title, textColor: .primary, textLayout: .secondLineWithValue(videoQualityTitle), icon: { _ in
|
||||
return nil
|
||||
}, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user