Attachment menu improvements

This commit is contained in:
Ilya Laktyushin 2022-02-22 23:49:26 +03:00
parent 3ddf3518c5
commit 2c3a1c802b
14 changed files with 249 additions and 85 deletions

View File

@ -7304,3 +7304,24 @@ Sorry for the inconvenience.";
"Attachment.OpenSettings" = "Go to Settings"; "Attachment.OpenSettings" = "Go to Settings";
"Attachment.OpenCamera" = "Open Camera"; "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

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

View File

@ -1106,6 +1106,11 @@ open class NavigationBar: ASDisplayNode {
let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape) let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape)
leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0
var transition = transition
if self.leftButtonNode.frame.width.isZero {
transition = .immediate
}
self.leftButtonNode.alpha = 1.0 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)) 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) let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape)
rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0 rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0
self.rightButtonNode.alpha = 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)) 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 self.titleNode.alpha = progress * progress
} }
} else { } else {
var transition = transition
if self.titleNode.frame.width.isZero {
transition = .immediate
}
self.titleNode.alpha = 1.0 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)) 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

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

View File

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

View File

@ -187,7 +187,7 @@ final class MediaPickerGridItemNode: GridItemNode {
} }
let scale = min(2.0, UIScreenScale) 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 originalSignal = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false)
let imageSignal: Signal<UIImage?, NoError> = editedSignal let imageSignal: Signal<UIImage?, NoError> = editedSignal
|> mapToSignal { result in |> 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) 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 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)) 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 { if themeUpdated {

View File

@ -8,6 +8,7 @@ import SwiftSignalKit
import AccountContext import AccountContext
import TelegramPresentationData import TelegramPresentationData
import TelegramUIPreferences import TelegramUIPreferences
import TelegramStringFormatting
import MergeLists import MergeLists
import Photos import Photos
import PhotosUI import PhotosUI
@ -75,8 +76,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
fileprivate var interaction: MediaPickerInteraction?
private let peer: EnginePeer? private let peer: EnginePeer?
private let chatLocation: ChatLocation? private let chatLocation: ChatLocation?
private let bannedSendMedia: (Int32, Bool)?
private let titleView: MediaPickerTitleView private let titleView: MediaPickerTitleView
private let moreButtonNode: MediaPickerMoreButtonNode private let moreButtonNode: MediaPickerMoreButtonNode
@ -102,19 +106,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
enum State { enum State {
case noAccess(cameraAccess: AVAuthorizationStatus?) 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? private weak var controller: MediaPickerScreen?
fileprivate var interaction: MediaPickerInteraction?
private var presentationData: PresentationData private var presentationData: PresentationData
private let mediaAssetsContext: MediaAssetsContext private let mediaAssetsContext: MediaAssetsContext
private let backgroundNode: NavigationBackgroundNode private let backgroundNode: NavigationBackgroundNode
private let gridNode: GridNode private let gridNode: GridNode
private var cameraView: TGAttachmentCameraView? fileprivate var cameraView: TGAttachmentCameraView?
private var placeholderNode: MediaPickerPlaceholderNode? private var placeholderNode: MediaPickerPlaceholderNode?
private var manageNode: MediaPickerManageNode? private var manageNode: MediaPickerManageNode?
private var bannedTextNode: ImmediateTextNode?
private var selectionNode: MediaPickerSelectedListNode? private var selectionNode: MediaPickerSelectedListNode?
@ -123,6 +127,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var enqueuedTransactions: [MediaPickerGridTransaction] = [] private var enqueuedTransactions: [MediaPickerGridTransaction] = []
private var state: State? private var state: State?
private var preloadPromise = ValuePromise<Bool>(true)
private var itemsDisposable: Disposable? private var itemsDisposable: Disposable?
private var selectionChangedDisposable: Disposable? private var selectionChangedDisposable: Disposable?
private var itemsDimensionsUpdatedDisposable: Disposable? private var itemsDimensionsUpdatedDisposable: Disposable?
@ -131,7 +137,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private let hiddenMediaId = Promise<String?>(nil) private let hiddenMediaId = Promise<String?>(nil)
private var didSetReady = false private var didSetReady = false
private let _ready = Promise<Bool>() private let _ready = Promise<Bool>(true)
var ready: Promise<Bool> { var ready: Promise<Bool> {
return self._ready return self._ready
} }
@ -155,44 +161,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.addSubnode(self.backgroundNode) self.addSubnode(self.backgroundNode)
self.addSubnode(self.gridNode) self.addSubnode(self.gridNode)
self.interaction = MediaPickerInteraction(openMedia: { [weak self] fetchResult, index, immediateThumbnail in let preloadPromise = self.preloadPromise
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 updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess()) let updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess())
|> mapToSignal { mediaAccess, cameraAccess -> Signal<State, NoError> in |> mapToSignal { mediaAccess, cameraAccess -> Signal<State, NoError> in
if case .notDetermined = mediaAccess { 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) { } else if [.restricted, .denied].contains(mediaAccess) {
return .single(.noAccess(cameraAccess: cameraAccess)) return .single(.noAccess(cameraAccess: cameraAccess))
} else { } else {
return mediaAssetsContext.recentAssets() return combineLatest(mediaAssetsContext.recentAssets(), preloadPromise.get())
|> map { fetchResult in |> map { fetchResult, preload in
return .assets(fetchResult: fetchResult, mediaAccess: mediaAccess, cameraAccess: cameraAccess) 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() self.hiddenMediaDisposable = (self.hiddenMediaId.get()
|> deliverOnMainQueue).start(next: { [weak self] id in |> deliverOnMainQueue).start(next: { [weak self] id in
if let strongSelf = self { if let strongSelf = self {
strongSelf.interaction?.hiddenMediaId = id strongSelf.controller?.interaction?.hiddenMediaId = id
strongSelf.gridNode.forEachItemNode { itemNode in strongSelf.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? MediaPickerGridItemNode { 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> { func selectionChangedSignal(selectionState: TGMediaSelectionContext) -> Signal<Void, NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = selectionState.selectionChangedSignal()?.start(next: { next 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> { func itemsDimensionsUpdatedSignal(editingState: TGMediaEditingContext) -> Signal<Void, NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = editingState.cropAdjustmentsUpdatedSignal()?.start(next: { next 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) // self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
} }
private func dismissInput() { fileprivate func dismissInput() {
self.view.window?.endEditing(true) self.view.window?.endEditing(true)
} }
@ -302,7 +281,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var requestedCameraAccess = false private var requestedCameraAccess = false
private func updateState(_ state: State) { 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 return
} }
@ -325,14 +304,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.requestedCameraAccess = true self.requestedCameraAccess = true
self.mediaAssetsContext.requestCameraAccess() self.mediaAssetsContext.requestCameraAccess()
} }
case let .assets(fetchResult, mediaAccess, cameraAccess): case let .assets(fetchResult, preload, mediaAccess, cameraAccess):
if let fetchResult = fetchResult { if let fetchResult = fetchResult {
for i in 0 ..< fetchResult.count { let totalCount = fetchResult.count
entries.append(MediaPickerGridEntry(stableId: stableId, content: .asset(fetchResult, fetchResult.count - i - 1))) 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 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 updateLayout = true
} }
@ -365,7 +347,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} }
self.selectionNode?.updateSelectionState() 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) self.controller?.updateSelectionState(count: count)
if let (layout, navigationBarHeight) = self.validLayout { if let (layout, navigationBarHeight) = self.validLayout {
@ -387,7 +369,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
let selectionNode = MediaPickerSelectedListNode(context: controller.context) let selectionNode = MediaPickerSelectedListNode(context: controller.context)
selectionNode.alpha = 0.0 selectionNode.alpha = 0.0
selectionNode.isUserInteractionEnabled = false selectionNode.isUserInteractionEnabled = false
selectionNode.interaction = self.interaction selectionNode.interaction = self.controller?.interaction
self.insertSubnode(selectionNode, aboveSubnode: self.gridNode) self.insertSubnode(selectionNode, aboveSubnode: self.gridNode)
self.selectionNode = selectionNode self.selectionNode = selectionNode
@ -405,8 +387,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} }
} }
private func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) { fileprivate func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = self.interaction, let (layout, _) = self.validLayout else { guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout else {
return return
} }
Queue.mainQueue().justDispatch { Queue.mainQueue().justDispatch {
@ -422,15 +404,15 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return self?.transitionView(for: identifier) return self?.transitionView(for: identifier)
}, completed: { [weak self] result, silently, scheduleTime in }, completed: { [weak self] result, silently, scheduleTime in
if let strongSelf = self { 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 }, 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) self?.controller?.present(c, in: .window(.root), with: a)
}) })
} }
private func openSelectedMedia(item: TGMediaSelectableItem, immediateThumbnail: UIImage?) { fileprivate func openSelectedMedia(item: TGMediaSelectableItem, immediateThumbnail: UIImage?) {
guard let controller = self.controller, let interaction = self.interaction, let (layout, _) = self.validLayout else { guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout else {
return return
} }
Queue.mainQueue().justDispatch { Queue.mainQueue().justDispatch {
@ -445,7 +427,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return self?.transitionView(for: identifier) return self?.transitionView(for: identifier)
}, completed: { [weak self] result, silently, scheduleTime in }, completed: { [weak self] result, silently, scheduleTime in
if let strongSelf = self { 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 }, 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) 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) { 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 return
} }
self.controller?.legacyCompletion(signals, silently, scheduleTime) 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 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 var manageHeight: CGFloat = 0.0
if case let .assets(_, mediaAccess, cameraAccess) = self.state { if case let .assets(_, _, mediaAccess, cameraAccess) = self.state {
if cameraAccess == nil { if cameraAccess == nil {
cameraRect = 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 { } else {
if case .limited = mediaAccess { if case .limited = mediaAccess {
@ -604,7 +617,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
transition.updateFrame(node: self.backgroundNode, frame: bounds) transition.updateFrame(node: self.backgroundNode, frame: bounds)
self.backgroundNode.update(size: bounds.size, transition: transition) 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 { guard let strongSelf = self else {
return return
} }
@ -612,12 +625,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
strongSelf.didSetReady = true strongSelf.didSetReady = true
Queue.mainQueue().justDispatch { Queue.mainQueue().justDispatch {
strongSelf._ready.set(.single(true)) strongSelf._ready.set(.single(true))
Queue.mainQueue().after(0.5, {
strongSelf.preloadPromise.set(false)
})
} }
} }
}) })
if let selectionNode = self.selectionNode { if let selectionNode = self.selectionNode {
let selectedItems = self.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? [] let selectedItems = self.controller?.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? []
let updateSelectionNode = { 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) 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 { private var groupedValue: Bool = true {
didSet { didSet {
self.groupedPromise.set(self.groupedValue) self.groupedPromise.set(self.groupedValue)
self.controllerNode.interaction?.selectionState?.grouping = self.groupedValue self.interaction?.selectionState?.grouping = self.groupedValue
if let layout = self.validLayout { if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .immediate) self.containerLayoutUpdated(layout, transition: .immediate)
@ -699,11 +716,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} }
private let groupedPromise = ValuePromise<Bool>(true) 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.context = context
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.peer = peer self.peer = peer
self.chatLocation = chatLocation 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 = 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 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) { required init(coder aDecoder: NSCoder) {
@ -811,12 +857,18 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.dismiss() self.dismiss()
} }
public func prepareForReuse() { public func resetForReuse() {
if let webSearchController = self.webSearchController { if let webSearchController = self.webSearchController {
self.webSearchController = nil self.webSearchController = nil
webSearchController.dismiss() webSearchController.dismiss()
} }
self.scrollToTop?() self.scrollToTop?()
self.controllerNode.cameraView?.pausePreview()
}
public func prepareForReuse() {
self.controllerNode.cameraView?.resumePreview()
} }
@objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) { @objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) {
@ -881,7 +933,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} }
public var mediaPickerContext: AttachmentMediaPickerContext? { public var mediaPickerContext: AttachmentMediaPickerContext? {
if let interaction = self.controllerNode.interaction { if let interaction = self.interaction {
return MediaPickerContext(interaction: interaction) return MediaPickerContext(interaction: interaction)
} else { } else {
return nil 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 private let context: AccountContext
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
@ -226,9 +226,11 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
} }
self.scrollNode.view.delegate = self self.scrollNode.view.delegate = self
self.scrollNode.view.panGestureRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(ReorderingGestureRecognizer(shouldBegin: { [weak self] point in 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 { for (_, itemNode) in strongSelf.itemNodes {
if itemNode.frame.contains(point) { if itemNode.frame.contains(point) {
return (true, true, itemNode) return (true, true, itemNode)
@ -242,20 +244,54 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
}, began: { [weak self] itemNode in }, began: { [weak self] itemNode in
self?.beginReordering(itemNode: itemNode) self?.beginReordering(itemNode: itemNode)
}, ended: { [weak self] point in }, 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 }, moved: { [weak self] offset in
self?.updateReordering(offset: offset) 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) { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.interaction?.dismissInput() self.interaction?.dismissInput()
} }
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateAbsoluteRects()
}
func scrollToTop(animated: Bool) { func scrollToTop(animated: Bool) {
self.scrollNode.view.setContentOffset(CGPoint(), animated: animated) 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) { private func beginReordering(itemNode: MediaPickerSelectedItemNode) {
self.isReordering = true self.isReordering = true
@ -265,7 +301,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
let reorderNode = ReorderingItemNode(itemNode: itemNode, initialLocation: itemNode.frame.origin) let reorderNode = ReorderingItemNode(itemNode: itemNode, initialLocation: itemNode.frame.origin)
self.reorderNode = reorderNode self.reorderNode = reorderNode
self.addSubnode(reorderNode) self.scrollNode.addSubnode(reorderNode)
itemNode.isHidden = true itemNode.isHidden = true
@ -549,6 +585,8 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
self.updateAbsoluteRects()
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight) self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
} }

View File

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

View File

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

View File

@ -749,6 +749,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.maximumNumberOfLines = 2 self.textNode.maximumNumberOfLines = 2
displayUndo = false displayUndo = false
self.originalRemainingSeconds = 5 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 self.remainingSeconds = self.originalRemainingSeconds
@ -777,7 +787,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content { switch content {
case .removedChat: case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode) 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 break
case .dice: case .dice:
self.panelWrapperNode.clipsToBounds = true self.panelWrapperNode.clipsToBounds = true