Merge branch 'master' into shared-audio-device-module

This commit is contained in:
Ali 2022-12-02 16:16:16 +04:00
commit 0871e98409
33 changed files with 493 additions and 216 deletions

View File

@ -414,6 +414,7 @@ official_apple_pay_merchants = [
"merchant.psbank.test.telegramios",
"merchant.psbank.prod.telegramios",
#"merchant.org.telegram.billinenet.test",
#"merchant.org.telegram.billinenet.prod",
#"merchant.org.telegram.portmone.test",
]

View File

@ -8261,14 +8261,16 @@ Sorry for the inconvenience.";
"Channel.AdminLog.TopicRenamedWithRemovedIcon" = "%1$@ renamed topic %2$@ to %3$@ and removed icon";
"Channel.AdminLog.TopicChangedIcon" = "%1$@ changed topic %2$@ icon to %3$@";
"Channel.AdminLog.TopicRemovedIcon" = "%1$@ removed topic %2$@ icon";
"Channel.AdminLog.TopicUnhidden" = "%1$@ unhid topic %2$@";
"Channel.AdminLog.TopicHidden" = "%1$@ hid topic %2$@";
"Attachment.Pasteboard" = "Clipboard";
"Attachment.DiscardPasteboardAlertText" = "Discard pasted items?";
"Undo.DeletedTopic" = "Topic Deleted";
"ChatList.MaxThreadPinsFinalText_1" = "Sorry, you can't pin more than **%@** thread to the top. Unpin some that are currently pinned.";
"ChatList.MaxThreadPinsFinalText_any" = "Sorry, you can't pin more than **%@** threads to the top. Unpin some that are currently pinned.";
"ChatList.MaxThreadPinsFinalText_1" = "Sorry, you can't pin more than %@ topics to the top. Unpin some that are currently pinned.";
"ChatList.MaxThreadPinsFinalText_any" = "Sorry, you can't pin more than %@ topics to the top. Unpin some that are currently pinned.";
"EmojiSearch.SearchTopicIconsPlaceholder" = "Search Topic Icons";
"EmojiSearch.SearchTopicIconsEmptyResult" = "No emoji found";
@ -8299,8 +8301,8 @@ Sorry for the inconvenience.";
"Notification.ForumTopicHidden" = "Topic hidden";
"Notification.ForumTopicUnhidden" = "Topic unhidden";
"Notification.ForumTopicHiddenAuthor" = "%1$@ hid topic";
"Notification.ForumTopicUnhiddenAuthor" = "%1$@ unhid topic";
"Notification.ForumTopicHiddenAuthor" = "%1$@ hid the topic";
"Notification.ForumTopicUnhiddenAuthor" = "%1$@ unhid the topic";
"Notification.OverviewTopicHidden" = "%1$@ hid %2$@ %3$@";
"Notification.OverviewTopicUnhidden" = "%1$@ unhid %2$@ %3$@";
@ -8405,6 +8407,8 @@ Sorry for the inconvenience.";
"GlobalAutodeleteSettings.AttemptDisabledGenericSelection" = "You can't enable auto-delete in this chat.";
"EmojiSearch.SearchEmojiPlaceholder" = "Search Emoji";
"StickersSearch.SearchStickersPlaceholder" = "Search Stickers";
"GifSearch.SearchGifPlaceholder" = "Search GIFs";
"MessageTimer.LargeShortSeconds_1" = "%@s";
"MessageTimer.LargeShortSeconds_2" = "%@s";

View File

@ -2,6 +2,7 @@ import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import ProgressNavigationButtonNode
@ -120,6 +121,10 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController as? NavigationController, let layout = self.validLayout {
addTemporaryKeyboardSnapshotView(navigationController: navigationController, parentView: self.view, layout: layout)
}
self.controllerNode.activateInput()
}
@ -219,3 +224,25 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self.controllerNode.updateCode("\(code)")
}
}
func addTemporaryKeyboardSnapshotView(navigationController: NavigationController, parentView: UIView, layout: ContainerViewLayout) {
if case .compact = layout.metrics.widthClass, let statusBarHost = navigationController.statusBarHost {
if let keyboardView = statusBarHost.keyboardView {
if let snapshotView = keyboardView.snapshotView(afterScreenUpdates: false) {
keyboardView.layer.removeAllAnimations()
UIView.performWithoutAnimation {
snapshotView.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - snapshotView.frame.size.height), size: snapshotView.frame.size)
if let keyboardWindow = statusBarHost.keyboardWindow {
keyboardWindow.addSubview(snapshotView)
}
Queue.mainQueue().after(0.45, {
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
})
}
}
}
}
}

View File

@ -74,6 +74,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.codeInputView.alpha = self.inProgress ? 0.6 : 1.0
}
}
private let appearanceTimestamp = CACurrentMediaTime()
init(strings: PresentationStrings, theme: PresentationTheme) {
self.strings = strings
@ -270,8 +272,16 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let previousInputHeight = self.layoutArguments?.0.inputHeight ?? 0.0
let newInputHeight = layout.inputHeight ?? 0.0
self.layoutArguments = (layout, navigationBarHeight)
var layout = layout
if CACurrentMediaTime() - self.appearanceTimestamp < 2.0, newInputHeight < previousInputHeight {
layout = layout.withUpdatedInputHeight(previousInputHeight)
}
let maximumWidth: CGFloat = min(430.0, layout.size.width)
let inset: CGFloat = 24.0

View File

@ -89,6 +89,10 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController as? NavigationController, let layout = self.validLayout {
addTemporaryKeyboardSnapshotView(navigationController: navigationController, parentView: self.view, layout: layout)
}
self.controllerNode.activateInput()
}

View File

@ -130,6 +130,10 @@ final class AuthorizationSequenceSignUpController: ViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController as? NavigationController, let layout = self.validLayout {
addTemporaryKeyboardSnapshotView(navigationController: navigationController, parentView: self.view, layout: layout)
}
self.controllerNode.activateInput()
}

View File

@ -2,6 +2,7 @@ import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramPresentationData
import TextFormat
import Markdown
@ -40,6 +41,8 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
private var layoutArguments: (ContainerViewLayout, CGFloat)?
private let appearanceTimestamp = CACurrentMediaTime()
var currentName: (String, String) {
return (self.firstNameField.textField.text ?? "", self.lastNameField.textField.text ?? "")
}
@ -209,8 +212,16 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let previousInputHeight = self.layoutArguments?.0.inputHeight ?? 0.0
let newInputHeight = layout.inputHeight ?? 0.0
self.layoutArguments = (layout, navigationBarHeight)
var layout = layout
if CACurrentMediaTime() - self.appearanceTimestamp < 2.0, newInputHeight < previousInputHeight {
layout = layout.withUpdatedInputHeight(previousInputHeight)
}
let maximumWidth: CGFloat = min(430.0, layout.size.width)
var insets = layout.insets(options: [.statusBar])

View File

@ -865,6 +865,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let layout = self.validLayout {
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.effectiveContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate)
self.requestUpdateHeaderContent(transition: .immediate)
}
if self.isNodeLoaded {

View File

@ -721,7 +721,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
})
case .index:
var headerType: ChatListSearchItemHeaderType = .messages(location: nil)
if case .forum = location, let peer = peer.peer {
if case let .forum(peerId) = location, let peer = peer.peer, peer.id == peerId {
headerType = .messages(location: peer.compactDisplayTitle)
}
header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
@ -738,15 +738,26 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
} else {
let index: EngineChatList.Item.Index
var chatThreadInfo: ChatListItemContent.ThreadInfo?
chatThreadInfo = nil
var displayAsMessage = false
switch location {
case .chatList:
index = .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index))
case .forum:
if let threadId = message.threadId, let threadInfo = threadInfo {
chatThreadInfo = ChatListItemContent.ThreadInfo(id: threadId, info: threadInfo, isOwnedByMe: false, isClosed: false, isHidden: false)
index = .forum(pinnedIndex: .none, timestamp: message.index.timestamp, threadId: threadId, namespace: message.index.id.namespace, id: message.index.id.id)
case let .forum(peerId):
let _ = peerId
let _ = threadInfo
displayAsMessage = true
if message.id.peerId == peerId {
if let threadId = message.threadId, let threadInfo = threadInfo {
chatThreadInfo = ChatListItemContent.ThreadInfo(id: threadId, info: threadInfo, isOwnedByMe: false, isClosed: false, isHidden: false)
index = .forum(pinnedIndex: .none, timestamp: message.index.timestamp, threadId: threadId, namespace: message.index.id.namespace, id: message.index.id.id)
} else {
index = .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index))
}
} else {
index = .chatList( EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index))
index = .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index))
}
}
return ChatListItem(presentationData: presentationData, context: context, chatListLocation: location, filterData: nil, index: index, content: .peer(ChatListItemContent.PeerData(
@ -762,7 +773,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: true,
displayAsMessage: false,
displayAsMessage: displayAsMessage,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: [],
@ -1217,7 +1228,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
let previousRecentlySearchedPeersState = Atomic<SearchedPeersState?>(value: nil)
let foundItems = combineLatest(queue: .mainQueue(), searchQuery, searchOptions, downloadItems)
let foundItems: Signal<([ChatListSearchEntry], Bool)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, searchOptions, downloadItems)
|> mapToSignal { [weak self] query, options, downloadItems -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
if query == nil && options == nil && [.chats, .topics].contains(key) {
let _ = currentRemotePeers.swap(nil)
@ -1464,55 +1475,113 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
updateSearchContexts { _ in
return ([:], true)
}
let foundRemoteMessages: Signal<(([EngineMessage], [EnginePeer.Id: EnginePeerReadCounters], [EngineMessage.Id: MessageHistoryThreadData], Int32), Bool), NoError>
struct FoundRemoteMessages {
var messages: [EngineMessage]
var readCounters: [EnginePeer.Id: EnginePeerReadCounters]
var threadsData: [EngineMessage.Id: MessageHistoryThreadData]
var totalCount: Int32
init(messages: [EngineMessage], readCounters: [EnginePeer.Id: EnginePeerReadCounters], threadsData: [EngineMessage.Id: MessageHistoryThreadData], totalCount: Int32) {
self.messages = messages
self.readCounters = readCounters
self.threadsData = threadsData
self.totalCount = totalCount
}
}
let foundRemoteMessages: Signal<([FoundRemoteMessages], Bool), NoError>
if peersFilter.contains(.doNotSearchMessages) {
foundRemoteMessages = .single((([], [:], [:], 0), false))
foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false))
} else {
if !finalQuery.isEmpty {
addAppLogEvent(postbox: context.account.postbox, type: "search_global_query")
}
let searchSignals: [Signal<(SearchMessagesResult, SearchMessagesState), NoError>] = searchLocations.map { searchLocation in
return context.engine.messages.searchMessages(location: searchLocation, query: finalQuery, state: nil, limit: 50)
let limit: Int32
#if DEBUG
limit = 50
#else
limit = 50
#endif
return context.engine.messages.searchMessages(location: searchLocation, query: finalQuery, state: nil, limit: limit)
}
let searchSignal = combineLatest(searchSignals)
|> map { results -> ChatListSearchMessagesResult in
let (result, updatedState) = results[0]
return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.map({ EngineMessage($0) }).sorted(by: { $0.index > $1.index }), readStates: result.readStates.mapValues { EnginePeerReadCounters(state: $0, isMuted: false) }, threadInfo: result.threadInfo, hasMore: !result.completed, totalCount: result.totalCount, state: updatedState)
|> map { results -> [ChatListSearchMessagesResult] in
var mappedResults: [ChatListSearchMessagesResult] = []
for resultData in results {
let (result, updatedState) = resultData
mappedResults.append(ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.map({ EngineMessage($0) }).sorted(by: { $0.index > $1.index }), readStates: result.readStates.mapValues { EnginePeerReadCounters(state: $0, isMuted: false) }, threadInfo: result.threadInfo, hasMore: !result.completed, totalCount: result.totalCount, state: updatedState))
}
return mappedResults
}
let loadMore = searchContexts.get()
|> mapToSignal { searchContexts -> Signal<(([EngineMessage], [EnginePeer.Id: EnginePeerReadCounters], [EngineMessage.Id: MessageHistoryThreadData], Int32), Bool), NoError> in
if let searchContext = searchContexts[0], searchContext.result.hasMore {
if let _ = searchContext.loadMoreIndex {
return context.engine.messages.searchMessages(location: searchLocations[0], query: finalQuery, state: searchContext.result.state, limit: 80)
|> map { result, updatedState -> ChatListSearchMessagesResult in
return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.map({ EngineMessage($0) }).sorted(by: { $0.index > $1.index }), readStates: result.readStates.mapValues { EnginePeerReadCounters(state: $0, isMuted: false) }, threadInfo: result.threadInfo, hasMore: !result.completed, totalCount: result.totalCount, state: updatedState)
}
|> mapToSignal { foundMessages -> Signal<(([EngineMessage], [EnginePeer.Id: EnginePeerReadCounters], [EngineMessage.Id: MessageHistoryThreadData], Int32), Bool), NoError> in
updateSearchContexts { previous in
let updated = ChatListSearchMessagesContext(result: foundMessages, loadMoreIndex: nil)
return ([0: updated], true)
|> mapToSignal { searchContexts -> Signal<([FoundRemoteMessages], Bool), NoError> in
for i in 0 ..< 2 {
if let searchContext = searchContexts[i], searchContext.result.hasMore {
var restResults: [Int: FoundRemoteMessages] = [:]
for j in 0 ..< 2 {
if j != i {
if let otherContext = searchContexts[j] {
restResults[j] = FoundRemoteMessages(messages: otherContext.result.messages, readCounters: otherContext.result.readStates, threadsData: otherContext.result.threadInfo, totalCount: otherContext.result.totalCount)
}
}
return .complete()
}
} else {
return .single(((searchContext.result.messages, searchContext.result.readStates, searchContext.result.threadInfo, searchContext.result.totalCount), false))
if let _ = searchContext.loadMoreIndex {
return context.engine.messages.searchMessages(location: searchLocations[i], query: finalQuery, state: searchContext.result.state, limit: 80)
|> map { result, updatedState -> ChatListSearchMessagesResult in
return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.map({ EngineMessage($0) }).sorted(by: { $0.index > $1.index }), readStates: result.readStates.mapValues { EnginePeerReadCounters(state: $0, isMuted: false) }, threadInfo: result.threadInfo, hasMore: !result.completed, totalCount: result.totalCount, state: updatedState)
}
|> mapToSignal { foundMessages -> Signal<([FoundRemoteMessages], Bool), NoError> in
updateSearchContexts { previous in
let updated = ChatListSearchMessagesContext(result: foundMessages, loadMoreIndex: nil)
var previous = previous
previous[i] = updated
return (previous, true)
}
return .complete()
}
} else {
var currentResults: [FoundRemoteMessages] = []
for i in 0 ..< 2 {
if let currentContext = searchContexts[i] {
currentResults.append(FoundRemoteMessages(messages: currentContext.result.messages, readCounters: currentContext.result.readStates, threadsData: currentContext.result.threadInfo, totalCount: currentContext.result.totalCount))
if currentContext.result.hasMore {
break
}
}
}
return .single((currentResults, false))
}
}
} else {
return .complete()
}
return .complete()
}
foundRemoteMessages = .single((([], [:], [:], 0), true))
foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], true))
|> then(
searchSignal
|> map { foundMessages -> (([EngineMessage], [EnginePeer.Id: EnginePeerReadCounters], [EngineMessage.Id: MessageHistoryThreadData], Int32), Bool) in
|> map { foundMessages -> ([FoundRemoteMessages], Bool) in
updateSearchContexts { _ in
return ([0: ChatListSearchMessagesContext(result: foundMessages, loadMoreIndex: nil)], true)
var resultContexts: [Int: ChatListSearchMessagesContext] = [:]
for i in 0 ..< foundMessages.count {
resultContexts[i] = ChatListSearchMessagesContext(result: foundMessages[i], loadMoreIndex: nil)
}
return (resultContexts, true)
}
return ((foundMessages.messages, foundMessages.readStates, foundMessages.threadInfo, foundMessages.totalCount), false)
var result: [FoundRemoteMessages] = []
for i in 0 ..< foundMessages.count {
result.append(FoundRemoteMessages(messages: foundMessages[i].messages, readCounters: foundMessages[i].readStates, threadsData: foundMessages[i].threadInfo, totalCount: foundMessages[i].totalCount))
if foundMessages[i].hasMore {
break
}
}
return (result, false)
}
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|> then(loadMore)
@ -1766,25 +1835,33 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
var firstHeaderId: Int64?
if !foundRemotePeers.2 {
index = 0
for message in foundRemoteMessages.0.0 {
if searchState.deletedMessageIds.contains(message.id) {
continue
} else if message.id.namespace == Namespaces.Message.Cloud && searchState.deletedGlobalMessageIds.contains(message.id.id) {
continue
}
let headerId = listMessageDateHeaderId(timestamp: message.timestamp)
if firstHeaderId == nil {
firstHeaderId = headerId
}
var peer = EngineRenderedPeer(message: message)
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
if let channelPeer = message.peers[migrationReference.peerId] {
peer = EngineRenderedPeer(peer: EnginePeer(channelPeer))
var existingMessageIds = Set<MessageId>()
for foundRemoteMessageSet in foundRemoteMessages.0 {
for message in foundRemoteMessageSet.messages {
if existingMessageIds.contains(message.id) {
continue
}
existingMessageIds.insert(message.id)
if searchState.deletedMessageIds.contains(message.id) {
continue
} else if message.id.namespace == Namespaces.Message.Cloud && searchState.deletedGlobalMessageIds.contains(message.id.id) {
continue
}
let headerId = listMessageDateHeaderId(timestamp: message.timestamp)
if firstHeaderId == nil {
firstHeaderId = headerId
}
var peer = EngineRenderedPeer(message: message)
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
if let channelPeer = message.peers[migrationReference.peerId] {
peer = EngineRenderedPeer(peer: EnginePeer(channelPeer))
}
}
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))
index += 1
}
entries.append(.message(message, peer, foundRemoteMessages.0.1[message.id.peerId], foundRemoteMessages.0.2[message.id]?.info, presentationData, foundRemoteMessages.0.3, selectionState?.contains(message.id), headerId == firstHeaderId, .index(message.index), nil, .generic, false))
index += 1
}
}
@ -1807,16 +1884,25 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let loadMore = {
updateSearchContexts { previousMap in
guard let previous = previousMap[0] else {
return ([:], false)
var updatedMap = previousMap
var isSearching = false
for i in 0 ..< 2 {
if let previous = updatedMap[i] {
if previous.loadMoreIndex != nil {
continue
}
guard let last = previous.result.messages.last else {
continue
}
updatedMap[i] = ChatListSearchMessagesContext(result: previous.result, loadMoreIndex: last.index)
isSearching = true
if previous.result.hasMore {
break
}
}
}
if previous.loadMoreIndex != nil {
return ([0: previous], false)
}
guard let last = previous.result.messages.last else {
return ([0: previous], false)
}
return ([0: ChatListSearchMessagesContext(result: previous.result, loadMoreIndex: last.index)], true)
return (updatedMap, isSearching)
}
}
@ -1919,6 +2005,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.listNode.clearHighlightAnimated(true)
})
})
chatListInteraction.isSearchMode = true
let listInteraction = ListMessageItemInteraction(openMessage: { [weak self] message, mode -> Bool in
guard let strongSelf = self else {
@ -2023,25 +2110,25 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
})
self.searchDisposable.set((foundItems
|> deliverOnMainQueue).start(next: { [weak self] entriesAndFlags in
|> deliverOnMainQueue).start(next: { [weak self] foundItems in
if let strongSelf = self {
let previousSelectedMessageIds = previousSelectedMessages.swap(strongSelf.selectedMessages)
let isSearching = entriesAndFlags?.1 ?? false
var entriesAndFlags = foundItems?.0
let isSearching = foundItems?.1 ?? false
strongSelf._isSearching.set(isSearching)
if strongSelf.tagMask == .photoOrVideo {
var entries: [ChatListSearchEntry]? = entriesAndFlags?.0 ?? []
var entries: [ChatListSearchEntry]? = entriesAndFlags ?? []
if isSearching && (entries?.isEmpty ?? true) {
entries = nil
}
strongSelf.mediaNode.updateHistory(entries: entries, totalCount: 0, updateType: .Initial)
}
var entriesAndFlags = entriesAndFlags
var peers: [EnginePeer] = []
if let entries = entriesAndFlags?.0 {
if let entries = entriesAndFlags {
var filteredEntries: [ChatListSearchEntry] = []
for entry in entries {
if case let .localPeer(peer, _, _, _, _, _, _, _, _) = entry {
@ -2053,16 +2140,16 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
if strongSelf.tagMask != nil || strongSelf.searchOptionsValue?.date != nil || strongSelf.searchOptionsValue?.peer != nil {
entriesAndFlags?.0 = filteredEntries
entriesAndFlags = filteredEntries
}
}
let previousEntries = previousSearchItems.swap(entriesAndFlags?.0)
let newEntries = entriesAndFlags?.0 ?? []
let previousEntries = previousSearchItems.swap(entriesAndFlags)
let newEntries = entriesAndFlags ?? []
let animated = (previousSelectedMessageIds == nil) != (strongSelf.selectedMessages == nil)
let firstTime = previousEntries == nil
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags?.0 != nil, isEmpty: !isSearching && (entriesAndFlags?.0.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags != nil, isEmpty: !isSearching && (entriesAndFlags?.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
interaction.peerContextAction?(message, node, rect, gesture, location)
}, toggleExpandLocalResults: {
guard let strongSelf = self else {
@ -3115,8 +3202,9 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
}, present: { _ in }, openForumThread: { _, _ in })
var isInlineMode = false
if case .topics = key {
isInlineMode = true
isInlineMode = false
}
interaction.isSearchMode = true
interaction.isInlineMode = isInlineMode
let items = (0 ..< 2).compactMap { _ -> ListViewItem? in

View File

@ -1295,8 +1295,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
return
}
let cachedPeerData = peerView.cachedData
if let cachedPeerData = cachedPeerData as? CachedUserData {
if let photo = cachedPeerData.photo, let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
if let cachedPeerData = cachedPeerData as? CachedUserData, case let .known(maybePhoto) = cachedPeerData.photo {
if let photo = maybePhoto, let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value()
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false)
@ -1590,6 +1590,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
let useChatListLayout: Bool
if case .chatList = item.chatListLocation {
useChatListLayout = true
} else if displayAsMessage {
useChatListLayout = true
} else {
useChatListLayout = false
}
let theme = item.presentationData.theme.chatList
var updatedTheme: PresentationTheme?
@ -1653,7 +1662,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let avatarLeftInset: CGFloat
if item.interaction.isInlineMode {
avatarLeftInset = 12.0
} else if case .forum = item.index {
} else if !useChatListLayout {
avatarLeftInset = 50.0
} else {
avatarLeftInset = 18.0 + avatarDiameter
@ -2501,7 +2510,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var mainContentBoundsOffset: CGFloat
var mainContentAlpha: CGFloat = 1.0
if case .chatList = item.chatListLocation {
if useChatListLayout {
mainContentFrame = CGRect(origin: CGPoint(x: leftInset - 2.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
mainContentBoundsOffset = mainContentFrame.origin.x
@ -2694,7 +2703,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
if let threadInfo = threadInfo {
if let threadInfo = threadInfo, !displayAsMessage {
let avatarIconView: ComponentHostView<Empty>
if let current = strongSelf.avatarIconView {
avatarIconView = current
@ -2742,7 +2751,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
avatarIconView.removeFromSuperview()
}
if case .forum = item.index {
if !useChatListLayout {
strongSelf.avatarContainerNode.isHidden = true
} else {
strongSelf.avatarContainerNode.isHidden = false

View File

@ -95,6 +95,8 @@ public final class ChatListNodeInteraction {
public var searchTextHighightState: String?
var highlightedChatLocation: ChatListHighlightedLocation?
var isSearchMode: Bool = false
var isInlineMode: Bool = false
var inlineNavigationLocation: ChatListHighlightedLocation?
@ -2297,7 +2299,7 @@ public final class ChatListNode: ListView {
isEmpty = true
loop1: for entry in transition.chatListView.filteredEntries {
switch entry {
case .GroupReferenceEntry, .HeaderEntry, .HoleEntry:
case .HeaderEntry, .HoleEntry:
break
default:
if case .ArchiveIntro = entry {

View File

@ -612,7 +612,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
autoremoveTimeout: item.item.autoremoveTimeout,
forumTopicData: item.item.forumTopicData,
topForumTopicItems: item.item.topForumTopicItems,
revealed: threadId == 1 && (state.hiddenItemShouldBeTemporaryRevealed || state.editing)
revealed: state.hiddenItemShouldBeTemporaryRevealed || state.editing
)))
if pinningIndex != 0 {
pinningIndex -= 1
@ -632,7 +632,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
message: groupReference.topMessage,
editing: state.editing,
unreadCount: groupReference.unreadCount,
revealed: state.hiddenItemShouldBeTemporaryRevealed,
revealed: state.hiddenItemShouldBeTemporaryRevealed || view.items.isEmpty,
hiddenByDefault: hideArchivedFolderByDefault
))
if pinningIndex != 0 {

View File

@ -1411,10 +1411,14 @@ open class TextNode: ASDisplayNode {
context.setAllowsFontSubpixelQuantization(true)
context.setShouldSubpixelQuantizeFonts(true)
var blendMode: CGBlendMode = .normal
var clearRects: [CGRect] = []
if let layout = parameters as? TextNodeLayout {
if !isRasterizing || layout.backgroundColor != nil {
context.setBlendMode(.copy)
blendMode = .copy
context.setFillColor((layout.backgroundColor ?? UIColor.clear).cgColor)
context.fill(bounds)
}
@ -1426,6 +1430,8 @@ open class TextNode: ASDisplayNode {
if let (textStrokeColor, textStrokeWidth) = layout.textStroke {
context.setBlendMode(.normal)
blendMode = .normal
context.setLineCap(.round)
context.setLineJoin(.round)
context.setStrokeColor(textStrokeColor.cgColor)
@ -1487,7 +1493,28 @@ open class TextNode: ASDisplayNode {
if attributes["Attribute__EmbeddedItem"] != nil {
continue
}
var fixCoupleEmoji = false
if glyphCount == 2, let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji"), let string = layout.attributedString {
let range = CTRunGetStringRange(run)
let substring = string.attributedSubstring(from: NSMakeRange(range.location, range.length)).string
let heart = Unicode.Scalar(0x2764)!
let man = Unicode.Scalar(0x1F468)!
let woman = Unicode.Scalar(0x1F469)!
if substring.unicodeScalars.contains(heart) && (substring.unicodeScalars.contains(man) || substring.unicodeScalars.contains(woman)) {
fixCoupleEmoji = true
}
}
if fixCoupleEmoji {
context.setBlendMode(.normal)
}
CTRunDraw(run, context, CFRangeMake(0, glyphCount))
if fixCoupleEmoji {
context.setBlendMode(blendMode)
}
}
}

View File

@ -517,7 +517,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
guard let status = self.status else {
return CMTimeRange(start: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)))
}
return CMTimeRange(start: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: status.duration, preferredTimescale: CMTimeScale(30.0)))
return CMTimeRange(start: CMTime(seconds: status.timestamp, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: status.duration, preferredTimescale: CMTimeScale(30.0)))
}
public func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -> Bool {

View File

@ -104,7 +104,9 @@ final class AppIconsDemoComponent: Component {
position = CGPoint(x: availableSize.width * 0.5, y: availableSize.height * 0.5)
}
view.center = position.offsetBy(dx: availableSize.width / 2.0, dy: 0.0)
if !self.animating {
view.center = position.offsetBy(dx: availableSize.width / 2.0, dy: 0.0)
}
i += 1
}
@ -126,7 +128,10 @@ final class AppIconsDemoComponent: Component {
return availableSize
}
private var animating = false
func animateIn(availableSize: CGSize) {
self.animating = true
var i = 0
for view in self.imageViews {
let from: CGPoint
@ -146,9 +151,17 @@ final class AppIconsDemoComponent: Component {
delay = 0.0
}
let initialPosition = view.layer.position
view.layer.position = initialPosition.offsetBy(dx: from.x, dy: from.y)
Queue.mainQueue().after(delay) {
view.layer.position = initialPosition
view.layer.animateScale(from: 3.0, to: 1.0, duration: 0.5, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring)
view.layer.animatePosition(from: from, to: CGPoint(), duration: 0.5, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
if i == 2 {
self.animating = false
}
}
i += 1

View File

@ -50,6 +50,34 @@ public enum CachedPeerAutoremoveTimeout: Equatable, PostboxCoding {
}
}
public enum CachedPeerProfilePhoto: Equatable, PostboxCoding {
case unknown
case known(TelegramMediaImage?)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
case 1:
self = .known(decoder.decodeObjectForKey("v", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage)
default:
self = .unknown
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case .unknown:
encoder.encodeInt32(0, forKey: "_v")
case let .known(value):
encoder.encodeInt32(1, forKey: "_v")
if let value = value {
encoder.encodeObject(value, forKey: "v")
} else {
encoder.encodeNil(forKey: "v")
}
}
}
}
public struct CachedPremiumGiftOption: Equatable, PostboxCoding {
public let months: Int32
public let currency: String
@ -123,7 +151,7 @@ public final class CachedUserData: CachedPeerData {
public let hasScheduledMessages: Bool
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
public let themeEmoticon: String?
public let photo: TelegramMediaImage?
public let photo: CachedPeerProfilePhoto
public let premiumGiftOptions: [CachedPremiumGiftOption]
public let voiceMessagesAvailable: Bool
@ -145,14 +173,14 @@ public final class CachedUserData: CachedPeerData {
self.hasScheduledMessages = false
self.autoremoveTimeout = .unknown
self.themeEmoticon = nil
self.photo = nil
self.photo = .unknown
self.premiumGiftOptions = []
self.voiceMessagesAvailable = true
self.peerIds = Set()
self.messageIds = Set()
}
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: TelegramMediaImage?, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool) {
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool) {
self.about = about
self.botInfo = botInfo
self.peerStatusSettings = peerStatusSettings
@ -204,12 +232,8 @@ public final class CachedUserData: CachedPeerData {
self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown
self.themeEmoticon = decoder.decodeOptionalStringForKey("te")
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
self.photo = photo
} else {
self.photo = nil
}
self.photo = decoder.decodeObjectForKey("phv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown
self.premiumGiftOptions = decoder.decodeObjectArrayWithDecoderForKey("pgo") as [CachedPremiumGiftOption]
self.voiceMessagesAvailable = decoder.decodeInt32ForKey("vma", orElse: 0) != 0
@ -261,12 +285,8 @@ public final class CachedUserData: CachedPeerData {
encoder.encodeNil(forKey: "te")
}
if let photo = self.photo {
encoder.encodeObject(photo, forKey: "ph")
} else {
encoder.encodeNil(forKey: "ph")
}
encoder.encodeObject(self.photo, forKey: "phv")
encoder.encodeObjectArray(self.premiumGiftOptions, forKey: "pgo")
encoder.encodeInt32(self.voiceMessagesAvailable ? 1 : 0, forKey: "vma")
}
@ -338,7 +358,7 @@ public final class CachedUserData: CachedPeerData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedUserData {
public func withUpdatedPhoto(_ photo: CachedPeerProfilePhoto) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}

View File

@ -739,7 +739,11 @@ public extension TelegramEngine.EngineData.Item {
preconditionFailure()
}
if let cachedData = view.cachedPeerData as? CachedUserData {
return .known(cachedData.photo)
if case let .known(value) = cachedData.photo {
return .known(value)
} else {
return .unknown
}
} else if let cachedData = view.cachedPeerData as? CachedGroupData {
return .known(cachedData.photo)
} else if let cachedData = view.cachedPeerData as? CachedChannelData {

View File

@ -117,7 +117,10 @@ private func mergedState(transaction: Transaction, seedConfiguration: SeedConfig
var peerIdsSet: Set<PeerId> = Set()
var readStates: [PeerId: CombinedPeerReadState] = [:]
var threadInfo:[MessageId : MessageHistoryThreadData] = [:]
var threadInfo: [MessageId : MessageHistoryThreadData] = [:]
if let state = state {
threadInfo = state.threadInfo
}
var renderedMessages: [Message] = []
for message in messages {

View File

@ -35,10 +35,12 @@ public enum AdminLogEventAction {
public struct ForumTopicInfo {
public var info: EngineMessageHistoryThread.Info
public var isClosed: Bool
public var isHidden: Bool
public init(info: EngineMessageHistoryThread.Info, isClosed: Bool) {
public init(info: EngineMessageHistoryThread.Info, isClosed: Bool, isHidden: Bool) {
self.info = info
self.isClosed = isClosed
self.isHidden = isHidden
}
}
@ -302,17 +304,17 @@ func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, m
let prevInfo: AdminLogEventAction.ForumTopicInfo
switch prevTopic {
case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _):
prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0)
prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0)
case .forumTopicDeleted:
prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false)
prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false)
}
let newInfo: AdminLogEventAction.ForumTopicInfo
switch newTopic {
case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _):
newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0)
newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0)
case .forumTopicDeleted:
newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false)
newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false)
}
action = .editTopic(prevInfo: prevInfo, newInfo: newInfo)

View File

@ -267,7 +267,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
return previous.withUpdatedAbout(userFullAbout).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFullCommonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
.withUpdatedThemeEmoticon(userFullThemeEmoticon)
.withUpdatedPhoto(photo)
.withUpdatedPhoto(.known(photo))
.withUpdatedPremiumGiftOptions(premiumGiftOptions)
.withUpdatedVoiceMessagesAvailable(voiceMessagesAvailable)
}

View File

@ -290,6 +290,9 @@ public enum PresentationResourceKey: Int32 {
case chatKeyboardActionButtonWebAppIcon
case chatGeneralThreadIcon
case chatGeneralThreadIncomingIcon
case chatGeneralThreadOutgoingIcon
case chatGeneralThreadFreeIcon
case uploadToneIcon
}

View File

@ -1325,4 +1325,22 @@ public struct PresentationResourcesChat {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/GeneralIcon"), color: theme.rootController.navigationBar.controlColor)
})
}
public static func chatGeneralThreadIncomingIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatGeneralThreadIncomingIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: theme.chat.message.incoming.accentTextColor)
})
}
public static func chatGeneralThreadOutgoingIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatGeneralThreadOutgoingIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: theme.chat.message.outgoing.accentTextColor)
})
}
public static func chatGeneralThreadFreeIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatGeneralThreadFreeIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: theme.chat.message.mediaOverlayControlColors.foregroundColor)
})
}
}

View File

@ -123,8 +123,8 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
return
}
let cachedPeerData = peerView.cachedData
if let cachedPeerData = cachedPeerData as? CachedUserData {
if let photo = cachedPeerData.photo, let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
if let cachedPeerData = cachedPeerData as? CachedUserData, case let .known(maybePhoto) = cachedPeerData.photo {
if let photo = maybePhoto, let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value()
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(videoId, "header"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false)

View File

@ -501,6 +501,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
}
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
return EmojiPagerContentComponent(
id: "stickers",
context: context,
@ -537,7 +538,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
itemLayoutType: .detailed,
itemContentUniqueId: nil,
warpContentsOnEdges: false,
displaySearchWithPlaceholder: "Search Stickers",
displaySearchWithPlaceholder: presentationData.strings.StickersSearch_SearchStickersPlaceholder,
searchInitiallyHidden: false,
searchIsPlaceholderOnly: true,
emptySearchResults: nil,
@ -748,6 +749,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
return !savedGifs.isEmpty
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let gifItems: Signal<EntityKeyboardGifContent, NoError>
switch subject {
case .recent:
@ -769,7 +771,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
items: items,
isLoading: false,
loadMoreToken: nil,
displaySearchWithPlaceholder: "Search GIFs",
displaySearchWithPlaceholder: presentationData.strings.GifSearch_SearchGifPlaceholder,
searchInitiallyHidden: false
)
)

View File

@ -1186,6 +1186,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
var replyMessage: Message?
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var inlineBotNameString: String?
@ -1205,51 +1206,48 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableContentWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
}
}
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
var hasReply = true
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
hasReply = false
}
if case .peer = item.chatLocation, replyMessage.threadId != nil, case let .peer(peerId) = item.chatLocation, peerId == replyMessage.id.peerId, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
if let threadId = item.message.threadId, Int64(replyMessage.id.id) == threadId {
hasReply = false
}
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
controllerInteraction: item.controllerInteraction,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if hasReply {
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if let replyAttribute = attribute as? ReplyMessageAttribute {
replyMessage = item.message.associatedMessages[replyAttribute.messageId]
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
replyMarkup = attribute
}
}
var hasReply = replyMessage != nil
if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil {
if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId {
hasReply = false
}
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
controllerInteraction: item.controllerInteraction,
type: .standalone,
threadId: item.message.threadId ?? 1,
parentMessage: item.message,
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if let replyMessage = replyMessage, hasReply {
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if item.message.id.peerId != item.context.account.peerId && !item.message.id.peerId.isReplies {
for attribute in item.message.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {

View File

@ -1737,6 +1737,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
if replyMessage != nil {
displayHeader = true
}
if !displayHeader, case .peer = item.chatLocation, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil {
displayHeader = true
}
}
let firstNodeTopPosition: ChatMessageBubbleRelativePosition
@ -1963,8 +1966,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
var hasReply = replyMessage != nil
if !isInstantVideo, let replyMessage = replyMessage, replyMessage.threadId != nil, case let .peer(peerId) = item.chatLocation, peerId == replyMessage.id.peerId, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil {
if let threadId = item.message.threadId, Int64(replyMessage.id.id) == threadId {
if !isInstantVideo, case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil {
if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId {
hasReply = false
}
@ -1980,7 +1983,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
context: item.context,
controllerInteraction: item.controllerInteraction,
type: .bubble(incoming: incoming),
message: replyMessage,
threadId: item.message.threadId ?? 1,
parentMessage: item.message,
constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,

View File

@ -531,8 +531,8 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
return
}
let cachedPeerData = peerView.cachedData
if let cachedPeerData = cachedPeerData as? CachedUserData {
if let photo = cachedPeerData.photo, let video = photo.videoRepresentations.last, let peerReference = PeerReference(peer) {
if let cachedPeerData = cachedPeerData as? CachedUserData, case let .known(maybePhoto) = cachedPeerData.photo {
if let photo = maybePhoto, let video = photo.videoRepresentations.last, let peerReference = PeerReference(peer) {
let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value()
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(videoId, "\(Int32.random(in: 0 ..< Int32.max))"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false)

View File

@ -614,6 +614,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
var replyMessage: Message?
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var inlineBotNameString: String?
@ -634,49 +635,48 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
var hasReply = true
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
hasReply = false
}
if case .peer = item.chatLocation, replyMessage.threadId != nil, case let .peer(peerId) = item.chatLocation, peerId == replyMessage.id.peerId, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
if let threadId = item.message.threadId, Int64(replyMessage.id.id) == threadId {
hasReply = false
}
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
controllerInteraction: item.controllerInteraction,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if hasReply {
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if let replyAttribute = attribute as? ReplyMessageAttribute {
replyMessage = item.message.associatedMessages[replyAttribute.messageId]
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
replyMarkup = attribute
}
}
var hasReply = replyMessage != nil
if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil {
if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId {
hasReply = false
}
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
controllerInteraction: item.controllerInteraction,
type: .standalone,
threadId: item.message.threadId ?? 1,
parentMessage: item.message,
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if let replyMessage = replyMessage, hasReply {
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,
context: item.context,
type: .standalone,
message: replyMessage,
parentMessage: item.message,
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
animationCache: item.controllerInteraction.presentationContext.animationCache,
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
))
}
if item.message.id.peerId != item.context.account.peerId && !item.message.id.peerId.isReplies {
for attribute in item.message.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {

View File

@ -183,7 +183,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
let context: AccountContext
let controllerInteraction: ChatControllerInteraction
let type: ChatMessageThreadInfoType
let message: Message
let threadId: Int64
let parentMessage: Message
let constrainedSize: CGSize
let animationCache: AnimationCache?
@ -195,7 +195,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
context: AccountContext,
controllerInteraction: ChatControllerInteraction,
type: ChatMessageThreadInfoType,
message: Message,
threadId: Int64,
parentMessage: Message,
constrainedSize: CGSize,
animationCache: AnimationCache?,
@ -206,7 +206,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
self.context = context
self.controllerInteraction = controllerInteraction
self.type = type
self.message = message
self.threadId = threadId
self.parentMessage = parentMessage
self.constrainedSize = constrainedSize
self.animationCache = animationCache
@ -318,7 +318,6 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
var topicIconId: Int64?
var topicIconColor: Int32 = 0
if let _ = arguments.parentMessage.threadId, let channel = arguments.parentMessage.peers[arguments.parentMessage.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), let threadInfo = arguments.parentMessage.associatedThreadInfo {
topicTitle = threadInfo.title
topicIconId = threadInfo.icon
topicIconColor = threadInfo.iconColor
@ -327,9 +326,10 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
let backgroundColor: UIColor
let textColor: UIColor
let arrowIcon: UIImage?
let generalThreadIcon: UIImage?
switch arguments.type {
case let .bubble(incoming):
if topicIconId == nil, topicIconColor != 0, incoming {
if topicIconId == nil, topicIconColor != 0, incoming, arguments.threadId != 1 {
let colors = topicIconColors(for: topicIconColor)
backgroundColor = UIColor(rgb: colors.0.last ?? 0x000000)
textColor = UIColor(rgb: colors.1.first ?? 0x000000)
@ -345,13 +345,15 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
arrowIcon = PresentationResourcesChat.chatBubbleArrowOutgoingImage(arguments.presentationData.theme.theme)
}
}
generalThreadIcon = incoming ? PresentationResourcesChat.chatGeneralThreadIncomingIcon(arguments.presentationData.theme.theme) : PresentationResourcesChat.chatGeneralThreadOutgoingIcon(arguments.presentationData.theme.theme)
case .standalone:
textColor = .white
textColor = arguments.presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor
backgroundColor = .white
arrowIcon = PresentationResourcesChat.chatBubbleArrowFreeImage(arguments.presentationData.theme.theme)
generalThreadIcon = PresentationResourcesChat.chatGeneralThreadFreeIcon(arguments.presentationData.theme.theme)
}
let placeholderColor: UIColor = arguments.message.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : arguments.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
let placeholderColor: UIColor = arguments.parentMessage.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : arguments.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
let text = NSAttributedString(string: topicTitle, font: textFont, textColor: textColor)
@ -390,9 +392,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
}
node.pressed = {
if let threadId = arguments.message.threadId {
arguments.controllerInteraction.navigateToThreadMessage(arguments.parentMessage.id.peerId, threadId, arguments.parentMessage.id)
}
arguments.controllerInteraction.navigateToThreadMessage(arguments.parentMessage.id.peerId, arguments.threadId, arguments.parentMessage.id)
}
if node.lineRects != lineRects {
@ -480,7 +480,9 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
}
let titleTopicIconContent: EmojiStatusComponent.Content
if let fileId = topicIconId, fileId != 0 {
if arguments.threadId == 1 {
titleTopicIconContent = .image(image: generalThreadIcon)
} else if let fileId = topicIconId, fileId != 0 {
titleTopicIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 36.0, height: 36.0), placeholderColor: arguments.presentationData.theme.theme.list.mediaPlaceholderColor, themeColor: arguments.presentationData.theme.theme.list.itemAccentColor, loopMode: .count(1))
} else {
titleTopicIconContent = .topic(title: String(topicTitle.prefix(1)), color: topicIconColor, size: CGSize(width: 22.0, height: 22.0))

View File

@ -1714,7 +1714,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
"Channel.AdminLog.TopicRemovedIcon" = "%1$@ removed topic %2$@ icon";*/
let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
if prevInfo.isClosed != newInfo.isClosed {
if prevInfo.isHidden != newInfo.isHidden {
appendAttributedText(text: newInfo.isHidden ? self.presentationData.strings.Channel_AdminLog_TopicHidden(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicUnhidden(authorTitle, newInfo.info.title), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
}
return []
}, to: &text, entities: &entities)
} else if prevInfo.isClosed != newInfo.isClosed {
appendAttributedText(text: newInfo.isClosed ? self.presentationData.strings.Channel_AdminLog_TopicClosed(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicReopened(authorTitle, newInfo.info.title), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]

View File

@ -1151,7 +1151,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
}
var canReport = true
if channel.isVerified || channel.adminRights != nil || channel.flags.contains(.isCreator) {
if channel.adminRights != nil || channel.flags.contains(.isCreator) {
canReport = false
}

View File

@ -8,6 +8,7 @@ import Postbox
import TelegramAudio
import AccountContext
import AVKit
import UniversalMediaPlayer
public final class OverlayUniversalVideoNode: OverlayMediaItemNode, AVPictureInPictureSampleBufferPlaybackDelegate {
public let content: UniversalVideoContent
@ -37,6 +38,9 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode, AVPictureInP
public var customClose: (() -> Void)?
public var controlsAreShowingUpdated: ((Bool) -> Void)?
private var statusDisposable: Disposable?
private var status: MediaPlayerStatus?
public init(postbox: Postbox, audioSession: ManagedAudioSession, manager: UniversalVideoManager, content: UniversalVideoContent, shouldBeDismissed: Signal<Bool, NoError> = .single(false), expand: @escaping () -> Void, close: @escaping () -> Void) {
self.content = content
self.defaultExpand = expand
@ -124,6 +128,16 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode, AVPictureInP
strongSelf.dismiss()
closeImpl?()
})
self.statusDisposable = (self.videoNode.status
|> deliverOnMainQueue).start(next: { [weak self] status in
self?.status = status
})
}
deinit {
self.shouldBeDismissedDisposable?.dispose()
self.statusDisposable?.dispose()
}
override public func didLoad() {
@ -194,7 +208,10 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode, AVPictureInP
}
public func pictureInPictureControllerTimeRangeForPlayback(_ pictureInPictureController: AVPictureInPictureController) -> CMTimeRange {
return CMTimeRange(start: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: 10.0, preferredTimescale: CMTimeScale(30.0)))
guard let status = self.status else {
return CMTimeRange(start: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: 0.0, preferredTimescale: CMTimeScale(30.0)))
}
return CMTimeRange(start: CMTime(seconds: status.timestamp, preferredTimescale: CMTimeScale(30.0)), duration: CMTime(seconds: status.duration, preferredTimescale: CMTimeScale(30.0)))
}
public func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -> Bool {

View File

@ -413,11 +413,7 @@ public final class WebSearchController: ViewController {
}
}
private func updateSearchQuery(_ query: String) {
if !query.isEmpty {
let _ = addRecentWebSearchQuery(engine: self.context.engine, string: query).start()
}
private func updateSearchQuery(_ query: String) {
let scope: Signal<WebSearchScope?, NoError>
switch self.mode {
case .media: