Add empty search result chat list footer

This commit is contained in:
Isaac 2024-12-28 02:43:55 +08:00
parent a5579103f4
commit dea8f6b48c
7 changed files with 377 additions and 12 deletions

View File

@ -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, *) {

View File

@ -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";

View File

@ -111,6 +111,7 @@ swift_library(
"//submodules/ComposePollUI",
"//submodules/ChatPresentationInterfaceState",
"//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/TelegramUI/Components/LottieComponent",
],
visibility = [
"//visibility:public",

View File

@ -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 {

View File

@ -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
})
}
}
}

View File

@ -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 {

View File

@ -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 {