Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Mike Renoir 2022-02-23 10:48:03 +04:00
commit 2f70ec1ca2
37 changed files with 632 additions and 250 deletions

View File

@ -443,6 +443,7 @@
"DialogList.TabTitle" = "Chats";
"DialogList.Title" = "Chats";
"DialogList.SearchLabel" = "Search for messages or users";
"DialogList.SearchLabelCompact" = "Search";
"DialogList.NoMessagesTitle" = "You have no conversations yet";
"DialogList.NoMessagesText" = "Start messaging by pressing the pencil button in the top right corner or go to the Contacts section.";
"DialogList.SingleTypingSuffix" = "%@ is typing";
@ -7304,3 +7305,24 @@ Sorry for the inconvenience.";
"Attachment.OpenSettings" = "Go to Settings";
"Attachment.OpenCamera" = "Open Camera";
"Attachment.DeselectedPhotos_1" = "%@ photo deselected";
"Attachment.DeselectedPhotos_2" = "%@ photos deselected";
"Attachment.DeselectedPhotos_3_10" = "%@ photos deselected";
"Attachment.DeselectedPhotos_any" = "%@ photos deselected";
"Attachment.DeselectedPhotos_many" = "%@ photos deselected";
"Attachment.DeselectedPhotos_0" = "%@ photos deselected";
"Attachment.DeselectedVideos_1" = "%@ video deselected";
"Attachment.DeselectedVideos_2" = "%@ videos deselected";
"Attachment.DeselectedVideos_3_10" = "%@ videos deselected";
"Attachment.DeselectedVideos_any" = "%@ videos deselected";
"Attachment.DeselectedVideos_many" = "%@ videos deselected";
"Attachment.DeselectedVideos_0" = "%@ videos deselected";
"Attachment.DeselectedItems_1" = "%@ item deselected";
"Attachment.DeselectedItems_2" = "%@ items deselected";
"Attachment.DeselectedItems_3_10" = "%@ items deselected";
"Attachment.DeselectedItems_any" = "%@ items deselected";
"Attachment.DeselectedItems_many" = "%@ items deselected";
"Attachment.DeselectedItems_0" = "%@ items deselected";

View File

@ -116,10 +116,10 @@ public func peerMessageMediaPlayerType(_ message: Message) -> MediaManagerPlayer
return nil
}
public func peerMessagesMediaPlaylistAndItemId(_ message: Message, isRecentActions: Bool, isGlobalSearch: Bool) -> (SharedMediaPlaylistId, SharedMediaPlaylistItemId)? {
if isGlobalSearch {
public func peerMessagesMediaPlaylistAndItemId(_ message: Message, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> (SharedMediaPlaylistId, SharedMediaPlaylistItemId)? {
if isGlobalSearch && !isDownloadList {
return (PeerMessagesMediaPlaylistId.custom, PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))
} else if isRecentActions {
} else if isRecentActions && !isDownloadList {
return (PeerMessagesMediaPlaylistId.recentActions(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))
} else {
return (PeerMessagesMediaPlaylistId.peer(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))

View File

@ -280,8 +280,8 @@ private final class AnimatedStickerDirectFrameSourceCache {
self.storeQueue = sharedStoreQueue
self.frameCount = frameCount
self.width = alignUp(size: width, align: 8)
self.height = alignUp(size: height, align: 8)
self.width = width// alignUp(size: width, align: 8)
self.height = height//alignUp(size: height, align: 8)
self.useHardware = useHardware
let suffix : String

View File

@ -23,10 +23,15 @@ public enum AttachmentButtonType: Equatable {
public protocol AttachmentContainable: ViewController {
var requestAttachmentMenuExpansion: () -> Void { get set }
func resetForReuse()
func prepareForReuse()
}
public extension AttachmentContainable {
func resetForReuse() {
}
func prepareForReuse() {
}

View File

@ -116,7 +116,8 @@ public class ChatListSearchItemNode: ListViewItemNode {
let backgroundColor = nextIsPinned ? item.theme.chatList.pinnedItemBackgroundColor : item.theme.chatList.itemBackgroundColor
let placeholderColor = item.theme.list.itemSecondaryTextColor
let (_, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: placeholder ?? "", font: searchBarFont, textColor: placeholderColor), CGSize(width: baseWidth - 20.0, height: 36.0), 1.0, placeholderColor, nextIsPinned ? item.theme.chatList.pinnedSearchBarColor : item.theme.chatList.regularSearchBarColor, backgroundColor, .immediate)
let placeholderString = NSAttributedString(string: placeholder ?? "", font: searchBarFont, textColor: placeholderColor)
let (_, searchBarApply) = searchBarNodeLayout(placeholderString, placeholderString, CGSize(width: baseWidth - 20.0, height: 36.0), 1.0, placeholderColor, nextIsPinned ? item.theme.chatList.pinnedSearchBarColor : item.theme.chatList.regularSearchBarColor, backgroundColor, .immediate)
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 54.0), insets: UIEdgeInsets())

View File

@ -469,7 +469,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
if !previewing {
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.DialogList_SearchLabel, activate: { [weak self] in
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.DialogList_SearchLabel, compactPlaceholder: self.presentationData.strings.DialogList_SearchLabelCompact, activate: { [weak self] in
self?.activateSearch()
})
self.searchContentNode?.updateExpansionProgress(0.0)
@ -601,9 +601,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
let stateSignal: Signal<State, NoError> = (combineLatest(queue: .mainQueue(), entriesWithFetchStatuses, recentDownloadItems(postbox: context.account.postbox))
|> map { entries, recentDownloadItems -> State in
if !entries.isEmpty {
let displayRecentDownloads = context.account.postbox.tailChatListView(groupId: .root, filterPredicate: nil, count: 11, summaryComponents: ChatListEntrySummaryComponents(components: [:]))
|> map { view -> Bool in
return view.0.entries.count >= 10
}
|> distinctUntilChanged
let stateSignal: Signal<State, NoError> = (combineLatest(queue: .mainQueue(), entriesWithFetchStatuses, recentDownloadItems(postbox: context.account.postbox), displayRecentDownloads)
|> map { entries, recentDownloadItems, displayRecentDownloads -> State in
if !entries.isEmpty && displayRecentDownloads {
var totalBytes = 0.0
var totalProgressInBytes = 0.0
for (entry, progress) in entries {
@ -799,7 +805,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.navigationItem.backBarButtonItem = backBarButtonItem
}
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.DialogList_SearchLabel)
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.DialogList_SearchLabel, compactPlaceholder: self.presentationData.strings.DialogList_SearchLabelCompact)
let editing = self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing
let editItem: UIBarButtonItem
if editing {
@ -2088,7 +2094,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
activate()
activate(filter != .downloads)
if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
searchContentNode.search(filter: filter, query: query)

View File

@ -1193,7 +1193,7 @@ final class ChatListControllerNode: ASDisplayNode {
}
}
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, (Bool) -> Void)? {
guard let (containerLayout, _, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
return nil
}
@ -1226,7 +1226,7 @@ final class ChatListControllerNode: ASDisplayNode {
})
self.containerNode.accessibilityElementsHidden = true
return (contentNode.filterContainerNode, { [weak self] in
return (contentNode.filterContainerNode, { [weak self] focus in
guard let strongSelf = self else {
return
}
@ -1239,7 +1239,7 @@ final class ChatListControllerNode: ASDisplayNode {
strongSelf.insertSubnode(subnode, belowSubnode: navigationBar)
}
}
}, placeholder: placeholderNode)
}, placeholder: placeholderNode, focus: focus)
})
}

View File

@ -870,7 +870,28 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
})
})))
if !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty {
if isCachedValue {
if !items.isEmpty {
items.append(.separator)
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
c.dismiss(completion: {
if let strongSelf = self {
strongSelf.dismissInput()
strongSelf.updateState { state in
return state.withUpdatedSelectedMessageIds([message.id])
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
})
})))
}
/*if !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty {
if !items.isEmpty {
items.append(.separator)
}
@ -905,7 +926,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
f(.dismissWithoutContent)
})))
}
}
}*/
return items
}
@ -1061,7 +1082,65 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
func deleteMessages(messageIds: Set<EngineMessage.Id>?) {
let isDownloads = self.paneContainerNode.currentPaneKey == .downloads
if let messageIds = messageIds ?? self.stateValue.selectedMessageIds, !messageIds.isEmpty {
if isDownloads {
let _ = (self.context.account.postbox.transaction { transaction -> [Message] in
return messageIds.compactMap(transaction.getMessage)
}
|> deliverOnMainQueue).start(next: { [weak self] messages in
guard let strongSelf = self else {
return
}
let title: String
let text: String
//TODO:localize
if messageIds.count == 1 {
title = "Remove Document?"
text = "Are you sure you want to remove this\ndocument from Downloads?\nIt will be deleted from your disk, but\nwill remain accessible in the cloud.";
} else {
title = "Remove \(messages.count) Documents?"
text = "Do you want to remove these\n\(messages.count) documents from Downloads?\nThey will be deleted from your disk,\nbut will remain accessible\nin the cloud."
}
strongSelf.present?(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
//TODO:localize
TextAlertAction(type: .defaultAction, title: "Remove", action: {
guard let strongSelf = self else {
return
}
var resourceIds = Set<MediaResourceId>()
for message in messages {
for media in message.media {
if let file = media as? TelegramMediaFile {
resourceIds.insert(file.resource.id)
}
}
}
let _ = (strongSelf.context.account.postbox.mediaBox.removeCachedResources(resourceIds, force: true, notify: true)
|> deliverOnMainQueue).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.updateState { state in
return state.withUpdatedSelectedMessageIds(nil)
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
})
})
], actionLayout: .horizontal, parseMarkdown: true), nil)
})
} else {
let (peers, messages) = self.currentMessages
let _ = (self.context.account.postbox.transaction { transaction -> Void in
for id in messageIds {
@ -1126,6 +1205,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}))
}
}
}
func forwardMessages(messageIds: Set<EngineMessage.Id>?) {
let messageIds = messageIds ?? self.stateValue.selectedMessageIds

View File

@ -400,7 +400,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
}
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int, isFirstInList: Bool)?) -> Void)?, openStorageSettings: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem {
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem {
switch self {
case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType):
let primaryPeer: EnginePeer
@ -564,8 +564,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
case .downloaded:
//TODO:localize
header = ChatListSearchItemHeader(type: .recentDownloads, theme: presentationData.theme, strings: presentationData.strings, actionTitle: "Settings", action: {
openStorageSettings()
header = ChatListSearchItemHeader(type: .recentDownloads, theme: presentationData.theme, strings: presentationData.strings, actionTitle: "Clear", action: {
openClearRecentlyDownloaded()
})
case .index:
header = ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
@ -628,12 +628,12 @@ private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
}
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, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int, isFirstInList: Bool)?) -> Void)?, openStorageSettings: @escaping () -> Void, toggleAllPaused: @escaping () -> 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, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> 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, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openStorageSettings: openStorageSettings, toggleAllPaused: toggleAllPaused), 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, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openStorageSettings: openStorageSettings, toggleAllPaused: toggleAllPaused), 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, 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), 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, 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), directionHint: nil) }
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, animated: animated)
}
@ -981,8 +981,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return false
}
return presentationDataPromise.get()
|> map { presentationData -> ([ChatListSearchEntry], Bool)? in
return combineLatest(queue: .mainQueue(), presentationDataPromise.get(), selectionPromise.get())
|> map { presentationData, selectionState -> ([ChatListSearchEntry], Bool)? in
var entries: [ChatListSearchEntry] = []
var existingMessageIds = Set<MessageId>()
@ -1047,7 +1047,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
peer = EngineRenderedPeer(peer: EnginePeer(channelPeer))
}
}
entries.append(.message(message, peer, nil, presentationData, 1, nil, false, .downloaded(timestamp: item.timestamp, index: message.index), (item.resourceId, item.size, false), .recentlyDownloaded, false))
entries.append(.message(message, peer, nil, presentationData, 1, selectionState?.contains(message.id), false, .downloaded(timestamp: item.timestamp, index: message.index), (item.resourceId, item.size, false), .recentlyDownloaded, false))
}
return (entries.sorted(), false)
}
@ -1501,6 +1502,17 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
})
}
let playlistLocation: PeerMessagesPlaylistLocation?
if strongSelf.key == .downloads {
playlistLocation = nil
} else {
playlistLocation = .custom(messages: foundMessages |> map { message, a, b in
return (message.map { $0._asMessage() }, a, b)
}, at: message.id, loadMore: {
loadMore()
})
}
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, mode: mode, navigationController: navigationController, dismissInput: {
interaction.dismissInput()
}, present: { c, a in
@ -1524,11 +1536,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, openPeer: { peer, navigation in
}, callPeer: { _, _ in
}, enqueueMessage: { _ in
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages |> map { message, a, b in
return (message.map { $0._asMessage() }, a, b)
}, at: message.id, loadMore: {
loadMore()
}), gallerySource: gallerySource))
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation, gallerySource: gallerySource))
}, openMessageContextMenu: { [weak self] message, _, node, rect, gesture in
guard let strongSelf = self, let currentEntries = strongSelf.currentEntries else {
return
@ -1641,11 +1649,44 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, searchPeer: { peer in
}, searchQuery: strongSelf.searchQueryValue, searchOptions: strongSelf.searchOptionsValue, messageContextAction: { message, node, rect, gesture, paneKey, downloadResource in
interaction.messageContextAction(message, node, rect, gesture, paneKey, downloadResource)
}, openStorageSettings: {
}, openClearRecentlyDownloaded: {
guard let strongSelf = self else {
return
}
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
var items: [ActionSheetItem] = []
//TODO:localize
items.append(ActionSheetTextItem(title: "Telegram allows to store all received and sent\ndocuments in the cloud and save storage\nspace on your device."))
//TODO:localize
items.append(ActionSheetButtonItem(title: "Manage Device Storage", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.context.sharedContext.openStorageUsage(context: strongSelf.context)
}))
//TODO:localize
items.append(ActionSheetButtonItem(title: "Clear Downloads List", color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
let _ = clearRecentDownloadList(postbox: strongSelf.context.account.postbox).start()
}))
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
strongSelf.interaction.dismissInput()
strongSelf.interaction.present(actionSheet, nil)
}, toggleAllPaused: {
guard let strongSelf = self else {
return
@ -1833,7 +1874,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
loadMore()
}
if [.file, .music, .voiceOrInstantVideo].contains(tagMask) {
if [.file, .music, .voiceOrInstantVideo].contains(tagMask) || self.key == .downloads {
self.mediaStatusDisposable = (context.sharedContext.mediaManager.globalMediaPlayerState
|> mapToSignal { playlistStateAndType -> Signal<(Account, SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> in
if let (account, state, type) = playlistStateAndType {
@ -1846,7 +1887,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return .single(nil) |> delay(0.2, queue: .mainQueue())
}
case .music:
if tagMask != .music {
if tagMask != .music && self.key != .downloads {
return .single(nil) |> delay(0.2, queue: .mainQueue())
}
case .file:

View File

@ -1106,6 +1106,11 @@ open class NavigationBar: ASDisplayNode {
let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape)
leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0
var transition = transition
if self.leftButtonNode.frame.width.isZero {
transition = .immediate
}
self.leftButtonNode.alpha = 1.0
transition.updateFrame(node: self.leftButtonNode, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize))
}
@ -1118,6 +1123,11 @@ open class NavigationBar: ASDisplayNode {
let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape)
rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0
self.rightButtonNode.alpha = 1.0
var transition = transition
if self.rightButtonNode.frame.width.isZero {
transition = .immediate
}
transition.updateFrame(node: self.rightButtonNode, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize))
}
@ -1182,6 +1192,10 @@ open class NavigationBar: ASDisplayNode {
self.titleNode.alpha = progress * progress
}
} else {
var transition = transition
if self.titleNode.frame.width.isZero {
transition = .immediate
}
self.titleNode.alpha = 1.0
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize))
}

View File

@ -6,12 +6,12 @@ import SwiftSignalKit
import UniversalMediaPlayer
import AccountContext
private func internalMessageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool) -> Signal<MediaPlayerStatus?, NoError> {
private func internalMessageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal<MediaPlayerStatus?, NoError> {
guard let playerType = peerMessageMediaPlayerType(message) else {
return .single(nil)
}
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch) {
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) {
return context.sharedContext.mediaManager.filteredPlaylistState(accountId: context.account.id, playlistId: playlistId, itemId: itemId, type: playerType)
|> mapToSignal { state -> Signal<MediaPlayerStatus?, NoError> in
return .single(state?.status)
@ -21,31 +21,32 @@ private func internalMessageFileMediaPlaybackStatus(context: AccountContext, fil
}
}
public func messageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool) -> Signal<MediaPlayerStatus, NoError> {
public func messageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal<MediaPlayerStatus, NoError> {
var duration = 0.0
if let value = file.duration {
duration = Double(value)
}
let defaultStatus = MediaPlayerStatus(generationTimestamp: 0.0, duration: duration, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused, soundEnabled: true)
return internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch) |> map { status in
return internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList)
|> map { status in
return status ?? defaultStatus
}
}
public func messageFileMediaPlaybackAudioLevelEvents(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool) -> Signal<Float, NoError> {
public func messageFileMediaPlaybackAudioLevelEvents(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal<Float, NoError> {
guard let playerType = peerMessageMediaPlayerType(message) else {
return .never()
}
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch) {
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) {
return context.sharedContext.mediaManager.filteredPlayerAudioLevelEvents(accountId: context.account.id, playlistId: playlistId, itemId: itemId, type: playerType)
} else {
return .never()
}
}
public func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isSharedMedia: Bool = false, isGlobalSearch: Bool = false) -> Signal<FileMediaResourceStatus, NoError> {
let playbackStatus = internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch) |> map { status -> MediaPlayerPlaybackStatus? in
public func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMediaFile, message: Message, isRecentActions: Bool, isSharedMedia: Bool = false, isGlobalSearch: Bool = false, isDownloadList: Bool = false) -> Signal<FileMediaResourceStatus, NoError> {
let playbackStatus = internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) |> map { status -> MediaPlayerPlaybackStatus? in
return status?.status
}

View File

@ -98,7 +98,7 @@ public final class HashtagSearchController: TelegramBaseController {
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, isEmpty: entries.isEmpty, isLoading: false, animated: false, context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], key: .chats, tagMask: nil, interaction: interaction, listInteraction: listInteraction, peerContextAction: nil, toggleExpandLocalResults: {
}, toggleExpandGlobalResults: {
}, searchPeer: { _ in
}, searchQuery: "", searchOptions: nil, messageContextAction: nil, openStorageSettings: {}, toggleAllPaused: {})
}, searchQuery: "", searchOptions: nil, messageContextAction: nil, openClearRecentlyDownloaded: {}, toggleAllPaused: {})
strongSelf.controllerNode.enqueueTransition(transition, firstTime: firstTime)
}
})

View File

@ -531,6 +531,9 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
{
TGDispatchOnMainThread(^
{
if (!_subscribedForCameraChanges) {
return;
}
if ([keyPath isEqualToString:PGCameraAdjustingFocusKey])
{
bool adjustingFocus = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:@YES];

View File

@ -654,7 +654,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
if statusUpdated && item.displayFileInfo {
if let file = selectedMedia as? TelegramMediaFile {
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult || item.isDownloadList)
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList)
|> mapToSignal { value -> Signal<FileMediaResourceStatus, NoError> in
if case .Fetching = value.fetchStatus {
return .single(value) |> delay(0.1, queue: Queue.concurrentDefaultQueue())
@ -669,7 +669,11 @@ public final class ListMessageFileItemNode: ListMessageNode {
|> map { status in
switch status.mediaStatus {
case .fetchStatus:
if item.isDownloadList {
return FileMediaResourceStatus(mediaStatus: .fetchStatus(status.fetchStatus), fetchStatus: status.fetchStatus)
} else {
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
}
case .playbackStatus:
return status
}
@ -677,7 +681,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
}
}
if isVoice {
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, message: message, isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult || item.isDownloadList)
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, message: message, isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList)
}
} else if let image = selectedMedia as? TelegramMediaImage {
updatedStatusSignal = messageImageMediaResourceStatus(context: item.context, image: image, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult || item.isDownloadList)
@ -890,7 +894,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
}
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel)))
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.contentSize.height + UIScreenPixel))
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: nodeLayout.contentSize.height + UIScreenPixel))
if let backgroundNode = strongSelf.backgroundNode {
backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.contentSize.height))
@ -1098,11 +1102,16 @@ public final class ListMessageFileItemNode: ListMessageNode {
if !isAudio && !isInstantVideo {
self.updateProgressFrame(size: contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate)
} else {
if item.isDownloadList {
self.updateProgressFrame(size: contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate)
}
switch status {
case let .fetchStatus(fetchStatus):
switch fetchStatus {
case .Fetching:
break
case let .Fetching(_, progress):
if item.isDownloadList {
iconStatusState = .progress(value: CGFloat(progress), cancelEnabled: true, appearance: nil)
}
case .Local:
if isAudio || isInstantVideo {
iconStatusState = .play
@ -1196,7 +1205,13 @@ public final class ListMessageFileItemNode: ListMessageNode {
break
case let .fetchStatus(fetchStatus):
maybeFetchStatus = fetchStatus
switch fetchStatus {
}
if item.isDownloadList, let fetchStatus = self.fetchStatus {
maybeFetchStatus = fetchStatus
}
switch maybeFetchStatus {
case .Fetching(_, let progress), .Paused(let progress):
if let file = self.currentMedia as? TelegramMediaFile, let size = file.size {
downloadingString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData))) / \(dataSizeString(size, forceDecimal: true, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData)))"
@ -1207,7 +1222,6 @@ public final class ListMessageFileItemNode: ListMessageNode {
case .Local:
break
}
}
switch maybeFetchStatus {
case .Fetching(_, let progress), .Paused(let progress):
@ -1269,7 +1283,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
} else {
if let linearProgressNode = self.linearProgressNode {
self.linearProgressNode = nil
linearProgressNode.layer.animateAlpha(from: 1.0, to: 1.0, duration: 0.2, removeOnCompletion: false, completion: { [weak linearProgressNode] _ in
linearProgressNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak linearProgressNode] _ in
linearProgressNode?.removeFromSupernode()
})
}
@ -1525,9 +1539,11 @@ private final class DownloadIconNode: ASImageNode {
}
func updateTheme(theme: PresentationTheme) {
if self.image != nil {
self.image = PresentationResourcesChat.sharedMediaFileDownloadStartIcon(theme, generate: {
return generateDownloadIcon(color: theme.list.itemAccentColor)
})
}
self.customColor = theme.list.itemAccentColor
self.animationNode?.customColor = self.customColor
}

View File

@ -337,7 +337,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
self.interaction?.openSearch()
}
public func prepareForReuse() {
public func resetForReuse() {
self.interaction?.updateMapMode(.map)
self.interaction?.dismissSearch()
self.scrollToTop?()

View File

@ -187,7 +187,7 @@ final class MediaPickerGridItemNode: GridItemNode {
}
let scale = min(2.0, UIScreenScale)
let targetSize = CGSize(width: 140.0 * scale, height: 140.0 * scale)
let targetSize = CGSize(width: 128.0 * scale, height: 128.0 * scale)
let originalSignal = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false)
let imageSignal: Signal<UIImage?, NoError> = editedSignal
|> mapToSignal { result in

View File

@ -58,7 +58,7 @@ final class MediaPickerManageNode: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .left)
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 16.0 - buttonWidth - 26.0, height: layout.size.height))
let panelHeight = max(64.0, textSize.height + 10.0)
let panelHeight = max(64.0, textSize.height + 24.0)
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + 16.0, y: floorToScreenPixels((panelHeight - textSize.height) / 2.0) - 5.0), size: textSize))
if themeUpdated {

View File

@ -8,6 +8,7 @@ import SwiftSignalKit
import AccountContext
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import MergeLists
import Photos
import PhotosUI
@ -75,8 +76,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
fileprivate var interaction: MediaPickerInteraction?
private let peer: EnginePeer?
private let chatLocation: ChatLocation?
private let bannedSendMedia: (Int32, Bool)?
private let titleView: MediaPickerTitleView
private let moreButtonNode: MediaPickerMoreButtonNode
@ -102,19 +106,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
enum State {
case noAccess(cameraAccess: AVAuthorizationStatus?)
case assets(fetchResult: PHFetchResult<PHAsset>?, mediaAccess: PHAuthorizationStatus, cameraAccess: AVAuthorizationStatus?)
case assets(fetchResult: PHFetchResult<PHAsset>?, preload: Bool, mediaAccess: PHAuthorizationStatus, cameraAccess: AVAuthorizationStatus?)
}
private weak var controller: MediaPickerScreen?
fileprivate var interaction: MediaPickerInteraction?
private var presentationData: PresentationData
private let mediaAssetsContext: MediaAssetsContext
private let backgroundNode: NavigationBackgroundNode
private let gridNode: GridNode
private var cameraView: TGAttachmentCameraView?
fileprivate var cameraView: TGAttachmentCameraView?
private var placeholderNode: MediaPickerPlaceholderNode?
private var manageNode: MediaPickerManageNode?
private var bannedTextNode: ImmediateTextNode?
private var selectionNode: MediaPickerSelectedListNode?
@ -123,6 +127,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var enqueuedTransactions: [MediaPickerGridTransaction] = []
private var state: State?
private var preloadPromise = ValuePromise<Bool>(true)
private var itemsDisposable: Disposable?
private var selectionChangedDisposable: Disposable?
private var itemsDimensionsUpdatedDisposable: Disposable?
@ -131,7 +137,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private let hiddenMediaId = Promise<String?>(nil)
private var didSetReady = false
private let _ready = Promise<Bool>()
private let _ready = Promise<Bool>(true)
var ready: Promise<Bool> {
return self._ready
}
@ -155,44 +161,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.addSubnode(self.backgroundNode)
self.addSubnode(self.gridNode)
self.interaction = MediaPickerInteraction(openMedia: { [weak self] fetchResult, index, immediateThumbnail in
self?.openMedia(fetchResult: fetchResult, index: index, immediateThumbnail: immediateThumbnail)
}, openSelectedMedia: { [weak self] item, immediateThumbnail in
self?.openSelectedMedia(item: item, immediateThumbnail: immediateThumbnail)
}, toggleSelection: { [weak self] item, value in
if let strongSelf = self {
strongSelf.interaction?.selectionState?.setItem(item, selected: value)
}
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated in
if let strongSelf = self, let selectionState = strongSelf.interaction?.selectionState {
if let currentItem = currentItem {
selectionState.setItem(currentItem, selected: true)
}
strongSelf.send(silently: silently, scheduleTime: scheduleTime, animated: animated)
}
}, schedule: { [weak self] in
if let strongSelf = self {
strongSelf.controller?.presentSchedulePicker(false, { [weak self] time in
self?.interaction?.sendSelected(nil, false, time, true)
})
}
}, dismissInput: { [weak self] in
if let strongSelf = self {
strongSelf.dismissInput()
}
}, selectionState: TGMediaSelectionContext(), editingState: TGMediaEditingContext())
self.interaction?.selectionState?.grouping = true
let preloadPromise = self.preloadPromise
let updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess())
|> mapToSignal { mediaAccess, cameraAccess -> Signal<State, NoError> in
if case .notDetermined = mediaAccess {
return .single(.assets(fetchResult: nil, mediaAccess: mediaAccess, cameraAccess: cameraAccess))
return .single(.assets(fetchResult: nil, preload: false, mediaAccess: mediaAccess, cameraAccess: cameraAccess))
} else if [.restricted, .denied].contains(mediaAccess) {
return .single(.noAccess(cameraAccess: cameraAccess))
} else {
return mediaAssetsContext.recentAssets()
|> map { fetchResult in
return .assets(fetchResult: fetchResult, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
return combineLatest(mediaAssetsContext.recentAssets(), preloadPromise.get())
|> map { fetchResult, preload in
return .assets(fetchResult: fetchResult, preload: preload, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
}
}
}
@ -212,7 +191,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.hiddenMediaDisposable = (self.hiddenMediaId.get()
|> deliverOnMainQueue).start(next: { [weak self] id in
if let strongSelf = self {
strongSelf.interaction?.hiddenMediaId = id
strongSelf.controller?.interaction?.hiddenMediaId = id
strongSelf.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? MediaPickerGridItemNode {
@ -224,7 +203,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
})
if let selectionState = self.interaction?.selectionState {
if let selectionState = self.controller?.interaction?.selectionState {
func selectionChangedSignal(selectionState: TGMediaSelectionContext) -> Signal<Void, NoError> {
return Signal { subscriber in
let disposable = selectionState.selectionChangedSignal()?.start(next: { next in
@ -244,7 +223,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
})
}
if let editingState = self.interaction?.editingState {
if let editingState = self.controller?.interaction?.editingState {
func itemsDimensionsUpdatedSignal(editingState: TGMediaEditingContext) -> Signal<Void, NoError> {
return Signal { subscriber in
let disposable = editingState.cropAdjustmentsUpdatedSignal()?.start(next: { next in
@ -294,7 +273,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
// self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
}
private func dismissInput() {
fileprivate func dismissInput() {
self.view.window?.endEditing(true)
}
@ -302,7 +281,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var requestedCameraAccess = false
private func updateState(_ state: State) {
guard let interaction = self.interaction, let controller = self.controller else {
guard let controller = self.controller, let interaction = controller.interaction else {
return
}
@ -325,14 +304,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.requestedCameraAccess = true
self.mediaAssetsContext.requestCameraAccess()
}
case let .assets(fetchResult, mediaAccess, cameraAccess):
case let .assets(fetchResult, preload, mediaAccess, cameraAccess):
if let fetchResult = fetchResult {
for i in 0 ..< fetchResult.count {
entries.append(MediaPickerGridEntry(stableId: stableId, content: .asset(fetchResult, fetchResult.count - i - 1)))
let totalCount = fetchResult.count
let count = preload ? min(10, totalCount) : totalCount
for i in 0 ..< count {
entries.append(MediaPickerGridEntry(stableId: stableId, content: .asset(fetchResult, totalCount - i - 1)))
stableId += 1
}
if case let .assets(previousFetchResult, _, previousCameraAccess) = previousState, previousFetchResult == nil || previousCameraAccess != cameraAccess {
if case let .assets(previousFetchResult, _, _, previousCameraAccess) = previousState, previousFetchResult == nil || previousCameraAccess != cameraAccess {
updateLayout = true
}
@ -365,7 +347,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
self.selectionNode?.updateSelectionState()
let count = Int32(self.interaction?.selectionState?.count() ?? 0)
let count = Int32(self.controller?.interaction?.selectionState?.count() ?? 0)
self.controller?.updateSelectionState(count: count)
if let (layout, navigationBarHeight) = self.validLayout {
@ -387,7 +369,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
let selectionNode = MediaPickerSelectedListNode(context: controller.context)
selectionNode.alpha = 0.0
selectionNode.isUserInteractionEnabled = false
selectionNode.interaction = self.interaction
selectionNode.interaction = self.controller?.interaction
self.insertSubnode(selectionNode, aboveSubnode: self.gridNode)
self.selectionNode = selectionNode
@ -405,8 +387,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
}
private func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = self.interaction, let (layout, _) = self.validLayout else {
fileprivate func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout else {
return
}
Queue.mainQueue().justDispatch {
@ -422,15 +404,15 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return self?.transitionView(for: identifier)
}, completed: { [weak self] result, silently, scheduleTime in
if let strongSelf = self {
strongSelf.interaction?.sendSelected(result, silently, scheduleTime, false)
strongSelf.controller?.interaction?.sendSelected(result, silently, scheduleTime, false)
}
}, presentStickers: controller.presentStickers, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
self?.controller?.present(c, in: .window(.root), with: a)
})
}
private func openSelectedMedia(item: TGMediaSelectableItem, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = self.interaction, let (layout, _) = self.validLayout else {
fileprivate func openSelectedMedia(item: TGMediaSelectableItem, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout else {
return
}
Queue.mainQueue().justDispatch {
@ -445,7 +427,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return self?.transitionView(for: identifier)
}, completed: { [weak self] result, silently, scheduleTime in
if let strongSelf = self {
strongSelf.interaction?.sendSelected(result, silently, scheduleTime, false)
strongSelf.controller?.interaction?.sendSelected(result, silently, scheduleTime, false)
}
}, presentStickers: controller.presentStickers, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
self?.controller?.present(c, in: .window(.root), with: a)
@ -453,7 +435,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
fileprivate func send(asFile: Bool = false, silently: Bool, scheduleTime: Int32?, animated: Bool) {
guard let signals = TGMediaAssetsController.resultSignals(for: self.interaction?.selectionState, editingContext: self.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: true) else {
guard let signals = TGMediaAssetsController.resultSignals(for: self.controller?.interaction?.selectionState, editingContext: self.controller?.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: true) else {
return
}
self.controller?.legacyCompletion(signals, silently, scheduleTime)
@ -549,11 +531,42 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
var cameraRect: CGRect? = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth * 2.0 + 1.0))
var manageHeight: CGFloat = 0.0
if case let .assets(_, mediaAccess, cameraAccess) = self.state {
if case let .assets(_, _, mediaAccess, cameraAccess) = self.state {
if cameraAccess == nil {
cameraRect = nil
}
if case .notDetermined = mediaAccess {
if let (untilDate, personal) = self.controller?.bannedSendMedia {
self.gridNode.alpha = 0.3
self.gridNode.isUserInteractionEnabled = false
let banDescription: String
if untilDate != 0 && untilDate != Int32.max {
banDescription = self.presentationData.strings.Conversation_RestrictedMediaTimed(stringForFullDate(timestamp: untilDate, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)).string
} else if personal {
banDescription = self.presentationData.strings.Conversation_RestrictedMedia
} else {
banDescription = self.presentationData.strings.Conversation_DefaultRestrictedMedia
}
let bannedTextNode: ImmediateTextNode
if let current = self.bannedTextNode {
bannedTextNode = current
} else {
bannedTextNode = ImmediateTextNode()
bannedTextNode.maximumNumberOfLines = 0
bannedTextNode.textAlignment = .center
self.bannedTextNode = bannedTextNode
self.addSubnode(bannedTextNode)
}
bannedTextNode.attributedText = NSAttributedString(string: banDescription, font: Font.regular(15.0), textColor: self.presentationData.theme.list.freeTextColor, paragraphAlignment: .center)
let bannedTextSize = bannedTextNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 16.0, height: layout.size.height))
manageHeight = max(44.0, bannedTextSize.height + 20.0)
transition.updateFrame(node: bannedTextNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - bannedTextSize.width) / 2.0), y: insets.top + floorToScreenPixels((manageHeight - bannedTextSize.height) / 2.0) - 4.0), size: bannedTextSize))
} else if case .notDetermined = mediaAccess {
} else {
if case .limited = mediaAccess {
@ -604,7 +617,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
transition.updateFrame(node: self.backgroundNode, frame: bounds)
self.backgroundNode.update(size: bounds.size, transition: transition)
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: 200.0, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemWidth), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cameraRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: itemWidth, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemWidth), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cameraRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
@ -612,12 +625,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
strongSelf.didSetReady = true
Queue.mainQueue().justDispatch {
strongSelf._ready.set(.single(true))
Queue.mainQueue().after(0.5, {
strongSelf.preloadPromise.set(false)
})
}
}
})
if let selectionNode = self.selectionNode {
let selectedItems = self.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? []
let selectedItems = self.controller?.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? []
let updateSelectionNode = {
selectionNode.updateLayout(size: bounds.size, insets: cleanGridInsets, items: selectedItems, grouped: self.controller?.groupedValue ?? true, theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, bubbleCorners: self.presentationData.chatBubbleCorners, transition: transition)
}
@ -690,7 +707,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var groupedValue: Bool = true {
didSet {
self.groupedPromise.set(self.groupedValue)
self.controllerNode.interaction?.selectionState?.grouping = self.groupedValue
self.interaction?.selectionState?.grouping = self.groupedValue
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .immediate)
@ -699,11 +716,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
private let groupedPromise = ValuePromise<Bool>(true)
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, bannedSendMedia: (Int32, Bool)?) {
self.context = context
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.peer = peer
self.chatLocation = chatLocation
self.bannedSendMedia = bannedSendMedia
self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0)
self.titleView.title = self.presentationData.strings.Attachment_Gallery
@ -763,6 +781,34 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
}
}
self.interaction = MediaPickerInteraction(openMedia: { [weak self] fetchResult, index, immediateThumbnail in
self?.controllerNode.openMedia(fetchResult: fetchResult, index: index, immediateThumbnail: immediateThumbnail)
}, openSelectedMedia: { [weak self] item, immediateThumbnail in
self?.controllerNode.openSelectedMedia(item: item, immediateThumbnail: immediateThumbnail)
}, toggleSelection: { [weak self] item, value in
if let strongSelf = self {
strongSelf.interaction?.selectionState?.setItem(item, selected: value)
}
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated in
if let strongSelf = self, let selectionState = strongSelf.interaction?.selectionState {
if let currentItem = currentItem {
selectionState.setItem(currentItem, selected: true)
}
strongSelf.controllerNode.send(silently: silently, scheduleTime: scheduleTime, animated: animated)
}
}, schedule: { [weak self] in
if let strongSelf = self {
strongSelf.presentSchedulePicker(false, { [weak self] time in
self?.interaction?.sendSelected(nil, false, time, true)
})
}
}, dismissInput: { [weak self] in
if let strongSelf = self {
strongSelf.controllerNode.dismissInput()
}
}, selectionState: TGMediaSelectionContext(), editingState: TGMediaEditingContext())
self.interaction?.selectionState?.grouping = true
}
required init(coder aDecoder: NSCoder) {
@ -811,12 +857,18 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.dismiss()
}
public func prepareForReuse() {
public func resetForReuse() {
if let webSearchController = self.webSearchController {
self.webSearchController = nil
webSearchController.dismiss()
}
self.scrollToTop?()
self.controllerNode.cameraView?.pausePreview()
}
public func prepareForReuse() {
self.controllerNode.cameraView?.resumePreview()
}
@objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) {
@ -881,7 +933,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
public var mediaPickerContext: AttachmentMediaPickerContext? {
if let interaction = self.controllerNode.interaction {
if let interaction = self.interaction {
return MediaPickerContext(interaction: interaction)
} else {
return nil

View File

@ -189,7 +189,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
}
}
final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
private let context: AccountContext
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
@ -226,9 +226,11 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
}
self.scrollNode.view.delegate = self
self.scrollNode.view.panGestureRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(ReorderingGestureRecognizer(shouldBegin: { [weak self] point in
if let strongSelf = self, !strongSelf.scrollNode.view.isTracking {
if let strongSelf = self, !strongSelf.scrollNode.view.isDragging {
let point = strongSelf.view.convert(point, to: strongSelf.scrollNode.view)
for (_, itemNode) in strongSelf.itemNodes {
if itemNode.frame.contains(point) {
return (true, true, itemNode)
@ -242,20 +244,54 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
}, began: { [weak self] itemNode in
self?.beginReordering(itemNode: itemNode)
}, ended: { [weak self] point in
self?.endReordering(point: point)
if let strongSelf = self {
if var point = point {
point = strongSelf.view.convert(point, to: strongSelf.scrollNode.view)
strongSelf.endReordering(point: point)
} else {
strongSelf.endReordering(point: nil)
}
}
}, moved: { [weak self] offset in
self?.updateReordering(offset: offset)
}))
Queue.mainQueue().after(0.1, {
self.updateAbsoluteRects()
})
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.interaction?.dismissInput()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateAbsoluteRects()
}
func scrollToTop(animated: Bool) {
self.scrollNode.view.setContentOffset(CGPoint(), animated: animated)
}
func updateAbsoluteRects() {
guard let messageNodes = self.messageNodes, let (size, _, _, _, _, _, _) = self.validLayout else {
return
}
for itemNode in messageNodes {
var absoluteRect = itemNode.frame
if let supernode = self.supernode {
absoluteRect = supernode.convert(itemNode.bounds, from: itemNode)
}
absoluteRect.origin.y = size.height - absoluteRect.origin.y - absoluteRect.size.height
itemNode.updateAbsoluteRect(absoluteRect, within: self.bounds.size)
}
}
private func beginReordering(itemNode: MediaPickerSelectedItemNode) {
self.isReordering = true
@ -265,7 +301,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
let reorderNode = ReorderingItemNode(itemNode: itemNode, initialLocation: itemNode.frame.origin)
self.reorderNode = reorderNode
self.addSubnode(reorderNode)
self.scrollNode.addSubnode(reorderNode)
itemNode.isHidden = true
@ -549,6 +585,8 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
}
}
self.updateAbsoluteRects()
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
}

View File

@ -134,12 +134,19 @@ public class SearchBarPlaceholderNode: ASDisplayNode {
}
}
public func asyncLayout() -> (_ placeholderString: NSAttributedString?, _ constrainedSize: CGSize, _ expansionProgress: CGFloat, _ iconColor: UIColor, _ foregroundColor: UIColor, _ backgroundColor: UIColor, _ transition: ContainedViewLayoutTransition) -> (CGFloat, () -> Void) {
public func asyncLayout() -> (_ placeholderString: NSAttributedString?, _ compactPlaceholderString: NSAttributedString?, _ constrainedSize: CGSize, _ expansionProgress: CGFloat, _ iconColor: UIColor, _ foregroundColor: UIColor, _ backgroundColor: UIColor, _ transition: ContainedViewLayoutTransition) -> (CGFloat, () -> Void) {
let labelLayout = TextNode.asyncLayout(self.labelNode)
let currentForegroundColor = self.foregroundColor
let currentIconColor = self.iconColor
return { placeholderString, constrainedSize, expansionProgress, iconColor, foregroundColor, backgroundColor, transition in
return { fullPlaceholderString, compactPlaceholderString, constrainedSize, expansionProgress, iconColor, foregroundColor, backgroundColor, transition in
let placeholderString: NSAttributedString?
if constrainedSize.width < 350.0 {
placeholderString = compactPlaceholderString
} else {
placeholderString = fullPlaceholderString
}
let (labelLayoutResult, labelApply) = labelLayout(TextNodeLayoutArguments(attributedString: placeholderString, backgroundColor: .clear, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
var updatedColor: UIColor?

View File

@ -11,6 +11,7 @@ public let navigationBarSearchContentHeight: CGFloat = 54.0
public class NavigationBarSearchContentNode: NavigationBarContentNode {
public var theme: PresentationTheme?
public var placeholder: String
public var compactPlaceholder: String
public let placeholderNode: SearchBarPlaceholderNode
public var placeholderHeight: CGFloat?
@ -20,9 +21,10 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
private var validLayout: (CGSize, CGFloat, CGFloat)?
public init(theme: PresentationTheme, placeholder: String, activate: @escaping () -> Void) {
public init(theme: PresentationTheme, placeholder: String, compactPlaceholder: String? = nil, activate: @escaping () -> Void) {
self.theme = theme
self.placeholder = placeholder
self.compactPlaceholder = compactPlaceholder ?? placeholder
self.placeholderNode = SearchBarPlaceholderNode(fieldStyle: .modern)
self.placeholderNode.labelNode.displaysAsynchronously = false
@ -36,9 +38,10 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
self.placeholderNode.activate = activate
}
public func updateThemeAndPlaceholder(theme: PresentationTheme, placeholder: String) {
public func updateThemeAndPlaceholder(theme: PresentationTheme, placeholder: String, compactPlaceholder: String? = nil) {
self.theme = theme
self.placeholder = placeholder
self.compactPlaceholder = compactPlaceholder ?? placeholder
self.placeholderNode.accessibilityLabel = placeholder
if let disabledOverlay = self.disabledOverlay {
disabledOverlay.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor.withAlphaComponent(0.5)
@ -105,7 +108,11 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
let overscrollProgress = max(0.0, max(0.0, self.expansionProgress - 1.0 + fraction) / fraction - visibleProgress)
let searchBarNodeLayout = self.placeholderNode.asyncLayout()
let (searchBarHeight, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93)), CGSize(width: baseWidth, height: fieldHeight), visibleProgress, self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93), self.theme?.rootController.navigationSearchBar.inputFillColor ?? .clear, self.theme?.rootController.navigationBar.opaqueBackgroundColor ?? .clear, transition)
let placeholderString = NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93))
let compactPlaceholderString = NSAttributedString(string: self.compactPlaceholder, font: searchBarFont, textColor: self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93))
let (searchBarHeight, searchBarApply) = searchBarNodeLayout(placeholderString, compactPlaceholderString, CGSize(width: baseWidth, height: fieldHeight), visibleProgress, self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93), self.theme?.rootController.navigationSearchBar.inputFillColor ?? .clear, self.theme?.rootController.navigationBar.opaqueBackgroundColor ?? .clear, transition)
searchBarApply()
let searchBarFrame = CGRect(origin: CGPoint(x: padding + leftInset, y: 8.0 + overscrollProgress * fieldHeight), size: CGSize(width: baseWidth, height: fieldHeight))

View File

@ -156,7 +156,7 @@ public final class SearchDisplayController {
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition)
}
public func activate(insertSubnode: @escaping (ASDisplayNode, Bool) -> Void, placeholder: SearchBarPlaceholderNode?) {
public func activate(insertSubnode: @escaping (ASDisplayNode, Bool) -> Void, placeholder: SearchBarPlaceholderNode?, focus: Bool = true) {
guard let (layout, navigationBarHeight) = self.containerLayout else {
return
}
@ -231,7 +231,9 @@ public final class SearchDisplayController {
insertSubnode(self.searchBar, true)
self.searchBar.layout()
if focus {
self.searchBar.activate()
}
if let placeholder = placeholder {
self.searchBar.animateIn(from: placeholder, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
if self.contentNode.hasDim {

View File

@ -112,7 +112,8 @@ class NotificationSearchItemNode: ListViewItemNode {
let backgroundColor = item.theme.chatList.itemBackgroundColor
let (_, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: placeholder ?? "", font: searchBarFont, textColor: UIColor(rgb: 0x8e8e93)), CGSize(width: baseWidth - 16.0, height: 28.0), 1.0, UIColor(rgb: 0x8e8e93), item.theme.chatList.regularSearchBarColor, backgroundColor, .immediate)
let placeholderString = NSAttributedString(string: placeholder ?? "", font: searchBarFont, textColor: UIColor(rgb: 0x8e8e93))
let (_, searchBarApply) = searchBarNodeLayout(placeholderString, placeholderString, CGSize(width: baseWidth - 16.0, height: 28.0), 1.0, UIColor(rgb: 0x8e8e93), item.theme.chatList.regularSearchBarColor, backgroundColor, .immediate)
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 44.0), insets: UIEdgeInsets())

View File

@ -1511,12 +1511,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
switch joinCallResult.connectionMode {
case .rtc:
strongSelf.currentConnectionMode = .rtc
strongSelf.genericCallContext?.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false)
strongSelf.genericCallContext?.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false, isUnifiedBroadcast: false)
strongSelf.genericCallContext?.setJoinResponse(payload: clientParams)
case let .broadcast(isExternalStream):
strongSelf.currentConnectionMode = .broadcast
strongSelf.genericCallContext?.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: strongSelf.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: isExternalStream))
strongSelf.genericCallContext?.setConnectionMode(.broadcast, keepBroadcastConnectedIfWasEnabled: false)
strongSelf.genericCallContext?.setConnectionMode(.broadcast, keepBroadcastConnectedIfWasEnabled: false, isUnifiedBroadcast: isExternalStream)
}
strongSelf.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl)
@ -1784,15 +1784,25 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
let chatPeer = self.accountContext.account.postbox.peerView(id: self.peerId)
|> map { view -> Peer? in
if let peer = peerViewMainPeer(view) {
return peer
} else {
return nil
}
}
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
participantsContext.state,
participantsContext.activeSpeakers,
self.speakingParticipantsContext.get(),
adminIds,
myPeer,
chatPeer,
accountContext.account.postbox.peerView(id: peerId),
self.isReconnectingAsSpeakerPromise.get()
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeerAndCachedData, view, isReconnectingAsSpeaker in
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeerAndCachedData, chatPeer, view, isReconnectingAsSpeaker in
guard let strongSelf = self else {
return
}
@ -1875,6 +1885,30 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
if let chatPeer = chatPeer, !participants.contains(where: { $0.peer.id == chatPeer.id }) {
participants.append(GroupCallParticipantsContext.Participant(
peer: chatPeer,
ssrc: 100,
videoDescription: GroupCallParticipantsContext.Participant.VideoDescription(
endpointId: "unified",
ssrcGroups: [],
audioSsrc: 100,
isPaused: false
),
presentationDescription: nil,
joinTimestamp: strongSelf.temporaryJoinTimestamp,
raiseHandRating: nil,
hasRaiseHand: false,
activityTimestamp: nil,
activityRank: nil,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false),
volume: nil,
about: nil,
joinedVideo: false
))
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
}
var otherParticipantsWithVideo = 0
var videoWatchingParticipants = 0
@ -2691,7 +2725,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
let clientParams = joinCallResult.jsonParams
screencastCallContext.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false)
screencastCallContext.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false, isUnifiedBroadcast: false)
screencastCallContext.setJoinResponse(payload: clientParams)
}, error: { error in
guard let _ = self else {
@ -2885,7 +2919,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if !self.didInitializeConnectionMode || self.currentConnectionMode != .none {
self.didInitializeConnectionMode = true
self.currentConnectionMode = .none
self.genericCallContext?.setConnectionMode(.none, keepBroadcastConnectedIfWasEnabled: movingFromBroadcastToRtc)
self.genericCallContext?.setConnectionMode(.none, keepBroadcastConnectedIfWasEnabled: movingFromBroadcastToRtc, isUnifiedBroadcast: false)
}
self.internalState = .requesting

View File

@ -190,6 +190,13 @@ public func addRecentDownloadItem(postbox: Postbox, item: RecentDownloadItem) ->
|> ignoreValues
}
public func clearRecentDownloadList(postbox: Postbox) -> Signal<Never, NoError> {
return postbox.transaction { transaction -> Void in
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.RecentDownloads, items: [])
}
|> ignoreValues
}
public func markRecentDownloadItemsAsSeen(postbox: Postbox, items: [(messageId: MessageId, resourceId: String)]) -> Signal<Never, NoError> {
return postbox.transaction { transaction -> Void in
var unseenIds: [(messageId: MessageId, resourceId: String)] = []

View File

@ -863,6 +863,13 @@ public final class GroupCallParticipantsContext {
public var ssrcGroups: [SsrcGroup]
public var audioSsrc: UInt32?
public var isPaused: Bool
public init(endpointId: String, ssrcGroups: [SsrcGroup], audioSsrc: UInt32?, isPaused: Bool) {
self.endpointId = endpointId
self.ssrcGroups = ssrcGroups
self.audioSsrc = audioSsrc
self.isPaused = isPaused
}
}
public var peer: Peer

View File

@ -164,7 +164,7 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai
public var requestAttachmentMenuExpansion: () -> Void = {}
var prepareForReuseImpl: () -> Void = {}
public func prepareForReuse() {
public func resetForReuse() {
self.prepareForReuseImpl()
self.scrollToTop?()
}

View File

@ -9121,6 +9121,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
var layout = layout
if let _ = self.attachmentController {
layout = layout.withUpdatedInputHeight(nil)
}
var navigationBarTransition = transition
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
@ -10321,17 +10326,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
self.chatDisplayNode.dismissInput()
let currentFilesController = Atomic<AttachmentContainable?>(value: nil)
let currentLocationController = Atomic<AttachmentContainable?>(value: nil)
var bannedSendMedia: (Int32, Bool)?
var canSendPolls = true
if peer is TelegramUser || peer is TelegramSecretChat {
canSendPolls = false
} else if let channel = peer as? TelegramChannel {
if let value = channel.hasBannedPermission(.banSendMedia) {
bannedSendMedia = value
}
if channel.hasBannedPermission(.banSendPolls) != nil {
canSendPolls = false
}
} else if let group = peer as? TelegramGroup {
if group.hasBannedPermission(.banSendMedia) {
bannedSendMedia = (Int32.max, false)
}
if group.hasBannedPermission(.banSendPolls) {
canSendPolls = false
}
@ -10344,6 +10353,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let inputText = self.presentationInterfaceState.interfaceState.effectiveInputState.inputText
let currentMediaController = Atomic<AttachmentContainable?>(value: nil)
let currentFilesController = Atomic<AttachmentContainable?>(value: nil)
let currentLocationController = Atomic<AttachmentContainable?>(value: nil)
let attachmentController = AttachmentController(context: self.context, updatedPresentationData: self.updatedPresentationData, buttons: availableTabs)
attachmentController.requestController = { [weak self, weak attachmentController] type, completion in
guard let strongSelf = self else {
@ -10351,7 +10364,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
switch type {
case .gallery:
strongSelf.presentMediaPicker(present: { controller, mediaPickerContext in
strongSelf.controllerNavigationDisposable.set(nil)
let existingController = currentMediaController.with { $0 }
if let controller = existingController {
controller.prepareForReuse()
completion(controller, nil)
return
}
strongSelf.presentMediaPicker(bannedSendMedia: bannedSendMedia, present: { controller, mediaPickerContext in
let _ = currentMediaController.swap(controller)
completion(controller, mediaPickerContext)
}, updateMediaPickerContext: { [weak attachmentController] mediaPickerContext in
attachmentController?.mediaPickerContext = mediaPickerContext
@ -10361,7 +10382,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime)
})
strongSelf.controllerNavigationDisposable.set(nil)
case .file:
strongSelf.controllerNavigationDisposable.set(nil)
let existingController = currentFilesController.with { $0 }
@ -10978,11 +10998,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.present(actionSheet, in: .window(.root))
}
private func presentMediaPicker(present: @escaping (AttachmentContainable, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?) -> Void) {
private func presentMediaPicker(bannedSendMedia: (Int32, Bool)?, present: @escaping (AttachmentContainable, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?) -> Void) {
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return
}
let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), chatLocation: self.chatLocation)
let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), chatLocation: self.chatLocation, bannedSendMedia: bannedSendMedia)
let mediaPickerContext = controller.mediaPickerContext
controller.openCamera = { [weak self] cameraView in
self?.openCamera(cameraView: cameraView)

View File

@ -20,7 +20,7 @@ import ContextUI
import ChatPresentationInterfaceState
private struct FetchControls {
let fetch: () -> Void
let fetch: (Bool) -> Void
let cancel: () -> Void
}
@ -228,7 +228,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
case .Remote, .Paused:
if let fetch = self.fetchControls.with({ return $0?.fetch }) {
fetch()
fetch(true)
}
case .Local:
break
@ -251,7 +251,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
case .Remote, .Paused:
if let fetch = self.fetchControls.with({ return $0?.fetch }) {
fetch()
fetch(true)
}
case .Local:
self.activateLocalContent()
@ -316,9 +316,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
updateImageSignal = chatMessageImageFile(account: arguments.context.account, fileReference: .message(message: MessageReference(arguments.message), media: arguments.file), thumbnail: true)
}
updatedFetchControls = FetchControls(fetch: { [weak self] in
updatedFetchControls = FetchControls(fetch: { [weak self] userInitiated in
if let strongSelf = self {
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: arguments.context, message: arguments.message, file: arguments.file, userInitiated: true).start())
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: arguments.context, message: arguments.message, file: arguments.file, userInitiated: userInitiated).start())
}
}, cancel: {
messageMediaFileCancelInteractiveFetch(context: arguments.context, messageId: arguments.message.id, file: arguments.file)
@ -331,15 +331,15 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|> map { resourceStatus, actualFetchStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, actualFetchStatus)
}
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false)
} else {
updatedStatusSignal = messageFileMediaResourceStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions)
|> map { resourceStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, nil)
}
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false)
}
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false)
}
var consumableContentIcon: UIImage?
@ -765,7 +765,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if let updatedFetchControls = updatedFetchControls {
let _ = strongSelf.fetchControls.swap(updatedFetchControls)
if arguments.automaticDownload {
updatedFetchControls.fetch()
updatedFetchControls.fetch(false)
}
}

View File

@ -720,7 +720,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
playbackStatusNode.frame = videoFrame.insetBy(dx: 1.5, dy: 1.5)
let status = messageFileMediaPlaybackStatus(context: item.context, file: file, message: item.message, isRecentActions: item.associatedData.isRecentActions, isGlobalSearch: false)
let status = messageFileMediaPlaybackStatus(context: item.context, file: file, message: item.message, isRecentActions: item.associatedData.isRecentActions, isGlobalSearch: false, isDownloadList: false)
playbackStatusNode.status = status
self.durationNode?.status = status
|> map(Optional.init)

View File

@ -58,6 +58,8 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource {
return self.engine.calls.requestStreamState(callId: self.callId, accessHash: self.accessHash).start(next: { result in
if let channel = result?.channels.first {
completion(channel.latestTimestamp)
} else {
completion(0)
}
})
} else {
@ -124,6 +126,11 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource {
let part: OngoingGroupCallBroadcastPart
switch result.status {
case let .data(dataValue):
/*#if DEBUG
let tempFile = EngineTempBox.shared.tempFile(fileName: "part.mp4")
let _ = try? dataValue.write(to: URL(fileURLWithPath: tempFile.path))
print("Dump stream part: \(tempFile.path)")
#endif*/
part = OngoingGroupCallBroadcastPart(timestampMilliseconds: timestampIdMilliseconds, responseTimestamp: result.responseTimestamp, status: .success, oggData: dataValue)
case .notReady:
part = OngoingGroupCallBroadcastPart(timestampMilliseconds: timestampIdMilliseconds, responseTimestamp: result.responseTimestamp, status: .notReady, oggData: Data())
@ -606,7 +613,7 @@ public final class OngoingGroupCallContext {
self.context.stop()
}
func setConnectionMode(_ connectionMode: ConnectionMode, keepBroadcastConnectedIfWasEnabled: Bool) {
func setConnectionMode(_ connectionMode: ConnectionMode, keepBroadcastConnectedIfWasEnabled: Bool, isUnifiedBroadcast: Bool) {
let mappedConnectionMode: OngoingCallConnectionMode
switch connectionMode {
case .none:
@ -616,7 +623,7 @@ public final class OngoingGroupCallContext {
case .broadcast:
mappedConnectionMode = .broadcast
}
self.context.setConnectionMode(mappedConnectionMode, keepBroadcastConnectedIfWasEnabled: keepBroadcastConnectedIfWasEnabled)
self.context.setConnectionMode(mappedConnectionMode, keepBroadcastConnectedIfWasEnabled: keepBroadcastConnectedIfWasEnabled, isUnifiedBroadcast: isUnifiedBroadcast)
if (mappedConnectionMode != .rtc) {
self.joinPayload.set(.never())
@ -900,9 +907,9 @@ public final class OngoingGroupCallContext {
})
}
public func setConnectionMode(_ connectionMode: ConnectionMode, keepBroadcastConnectedIfWasEnabled: Bool) {
public func setConnectionMode(_ connectionMode: ConnectionMode, keepBroadcastConnectedIfWasEnabled: Bool, isUnifiedBroadcast: Bool) {
self.impl.with { impl in
impl.setConnectionMode(connectionMode, keepBroadcastConnectedIfWasEnabled: keepBroadcastConnectedIfWasEnabled)
impl.setConnectionMode(connectionMode, keepBroadcastConnectedIfWasEnabled: keepBroadcastConnectedIfWasEnabled, isUnifiedBroadcast: isUnifiedBroadcast)
}
}

View File

@ -354,7 +354,7 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) {
- (void)stop;
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled;
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast;
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload;

View File

@ -1580,7 +1580,7 @@ private:
}
}
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled {
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast {
if (_instance) {
tgcalls::GroupConnectionMode mappedConnectionMode;
switch (connectionMode) {
@ -1601,7 +1601,7 @@ private:
break;
}
}
_instance->setConnectionMode(mappedConnectionMode, keepBroadcastConnectedIfWasEnabled);
_instance->setConnectionMode(mappedConnectionMode, keepBroadcastConnectedIfWasEnabled, isUnifiedBroadcast);
}
}

@ -1 +1 @@
Subproject commit 20860ca29147b4faa4f0b75a0da58517d9d4856c
Subproject commit 382d1b6756768021274cb1edfc1e144cfb101fb8

View File

@ -38,6 +38,7 @@ public enum UndoOverlayContent {
case mediaSaved(text: String)
case paymentSent(currencyValue: String, itemTitle: String)
case inviteRequestSent(title: String, text: String)
case image(image: UIImage, text: String)
}
public enum UndoOverlayAction {

View File

@ -749,6 +749,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 5
case let .image(image, text):
self.avatarNode = nil
self.iconNode = ASImageNode()
self.iconNode?.image = image
self.iconCheckNode = nil
self.animationNode = nil
self.animatedStickerNode = nil
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = true
self.originalRemainingSeconds = 5
}
self.remainingSeconds = self.originalRemainingSeconds
@ -777,7 +787,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content {
case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode)
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent, .inviteRequestSent:
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent:
break
case .dice:
self.panelWrapperNode.clipsToBounds = true