Pre-release improvements

This commit is contained in:
Ali 2022-04-05 19:05:36 +04:00
parent 811e5fcc8d
commit 3010d8e045
7 changed files with 261 additions and 131 deletions

View File

@ -7485,3 +7485,19 @@ Sorry for the inconvenience.";
"PeerInfo.TooltipMutedFor" = "Notifications are muted for %@."; "PeerInfo.TooltipMutedFor" = "Notifications are muted for %@.";
"PeerInfo.TooltipMutedUntil" = "Notifications are muted until %@."; "PeerInfo.TooltipMutedUntil" = "Notifications are muted until %@.";
"PeerInfo.TooltipMutedForever" = "Notifications are muted."; "PeerInfo.TooltipMutedForever" = "Notifications are muted.";
"PeerInfo.DeleteToneTitle" = "Delete Tone";
"PeerInfo.DeleteToneText" = "Are you sure you want to delete\n**%@** notification sound?";
"PeerInfo.AlertLeaveAction" = "Leave";
"PeerInfo.LeaveGroupTitle" = "Leave Group";
"PeerInfo.LeaveGroupText" = "Are you sure you want to leave the group **%@**?";
"PeerInfo.LeaveChannelTitle" = "Leave Channel";
"PeerInfo.LeaveChannelText" = "Are you sure you want to leave the channel **%@**?";
"PeerInfo.DeleteGroupTitle" = "Delete for All";
"PeerInfo.DeleteGroupText" = "Are you sure you want to delete the group **%@** and all of its messages for all members of the group?";
"PeerInfo.DeleteChannelTitle" = "Delete for All";
"PeerInfo.DeleteChannelText" = "Are you sure you want to delete the channel **%@** and all of its messages for all subscribers of the channel?";

View File

@ -155,6 +155,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
let titleSubtitleSpacing: CGFloat = 1.0 let titleSubtitleSpacing: CGFloat = 1.0
let iconSideInset: CGFloat = 12.0 let iconSideInset: CGFloat = 12.0
let standardIconWidth: CGFloat = 32.0 let standardIconWidth: CGFloat = 32.0
let iconSpacing: CGFloat = 8.0
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
@ -232,6 +233,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
maxTextWidth -= sideInset maxTextWidth -= sideInset
if let iconSize = iconSize { if let iconSize = iconSize {
maxTextWidth -= max(standardIconWidth, iconSize.width) maxTextWidth -= max(standardIconWidth, iconSize.width)
maxTextWidth -= iconSpacing
} else { } else {
maxTextWidth -= sideInset maxTextWidth -= sideInset
} }
@ -246,6 +248,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
if let iconSize = iconSize { if let iconSize = iconSize {
minSize.width += max(standardIconWidth, iconSize.width) minSize.width += max(standardIconWidth, iconSize.width)
minSize.width += iconSideInset minSize.width += iconSideInset
minSize.width += iconSpacing
} else { } else {
minSize.width += sideInset minSize.width += sideInset
} }

View File

@ -106,6 +106,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
private let activateArea: AccessibilityAreaNode private let activateArea: AccessibilityAreaNode
private let contentParentNode: ASDisplayNode
private let contentContainerNode: ASDisplayNode private let contentContainerNode: ASDisplayNode
private let imageNode: ASImageNode private let imageNode: ASImageNode
private let iconNode: ASImageNode private let iconNode: ASImageNode
@ -113,6 +114,10 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
private var item: ItemListCheckboxItem? private var item: ItemListCheckboxItem?
override public var controlsContainer: ASDisplayNode {
return self.contentParentNode
}
public init() { public init() {
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true self.backgroundNode.isLayerBacked = true
@ -125,6 +130,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
self.maskNode = ASImageNode() self.maskNode = ASImageNode()
self.contentParentNode = ASDisplayNode()
self.contentContainerNode = ASDisplayNode() self.contentContainerNode = ASDisplayNode()
self.imageNode = ASImageNode() self.imageNode = ASImageNode()
@ -149,7 +155,8 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
self.addSubnode(self.contentContainerNode) self.addSubnode(self.contentParentNode)
self.contentParentNode.addSubnode(self.contentContainerNode)
self.contentContainerNode.addSubnode(self.imageNode) self.contentContainerNode.addSubnode(self.imageNode)
self.contentContainerNode.addSubnode(self.iconNode) self.contentContainerNode.addSubnode(self.iconNode)
self.contentContainerNode.addSubnode(self.titleNode) self.contentContainerNode.addSubnode(self.titleNode)
@ -171,12 +178,12 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
switch item.style { switch item.style {
case .left: case .left:
leftInset += 44.0 leftInset += 62.0
case .right: case .right:
leftInset += 16.0 leftInset += 16.0
} }
let iconInset: CGFloat = 44.0 let iconInset: CGFloat = 62.0
if item.icon != nil { if item.icon != nil {
switch item.iconPlacement { switch item.iconPlacement {
case .default: case .default:
@ -225,7 +232,8 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
strongSelf.contentContainerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: layout.contentSize.height)) strongSelf.contentParentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: layout.contentSize.height))
strongSelf.contentContainerNode.frame = CGRect(origin: CGPoint(x: strongSelf.contentContainerNode.frame.minX, y: 0.0), size: CGSize(width: params.width, height: layout.contentSize.height))
strongSelf.activateArea.accessibilityLabel = item.title strongSelf.activateArea.accessibilityLabel = item.title
if item.checked { if item.checked {
@ -269,7 +277,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
} }
if strongSelf.maskNode.supernode == nil { if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3) strongSelf.insertSubnode(strongSelf.maskNode, aboveSubnode: strongSelf.contentParentNode)
} }
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false

View File

@ -13,6 +13,7 @@ import AppBundle
import LegacyMediaPickerUI import LegacyMediaPickerUI
import AVFoundation import AVFoundation
import UndoUI import UndoUI
import Postbox
private struct NotificationSoundSelectionArguments { private struct NotificationSoundSelectionArguments {
let account: Account let account: Account
@ -21,7 +22,7 @@ private struct NotificationSoundSelectionArguments {
let complete: () -> Void let complete: () -> Void
let cancel: () -> Void let cancel: () -> Void
let upload: () -> Void let upload: () -> Void
let deleteSound: (PeerMessageSound) -> Void let deleteSound: (PeerMessageSound, String) -> Void
} }
private enum NotificationSoundSelectionSection: Int32 { private enum NotificationSoundSelectionSection: Int32 {
@ -204,7 +205,7 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.selectSound(sound) arguments.selectSound(sound)
}, deleteAction: canBeDeleted ? { }, deleteAction: canBeDeleted ? {
arguments.deleteSound(sound) arguments.deleteSound(sound, text)
} : nil) } : nil)
} }
} }
@ -368,7 +369,7 @@ public func notificationSoundSelectionController(context: AccountContext, update
var completeImpl: (() -> Void)? var completeImpl: (() -> Void)?
var cancelImpl: (() -> Void)? var cancelImpl: (() -> Void)?
var presentFilePicker: (() -> Void)? var presentFilePicker: (() -> Void)?
var deleteSoundImpl: ((PeerMessageSound) -> Void)? var deleteSoundImpl: ((PeerMessageSound, String) -> Void)?
let playSoundDisposable = MetaDisposable() let playSoundDisposable = MetaDisposable()
let soundActionDisposable = MetaDisposable() let soundActionDisposable = MetaDisposable()
@ -393,8 +394,8 @@ public func notificationSoundSelectionController(context: AccountContext, update
cancelImpl?() cancelImpl?()
}, upload: { }, upload: {
presentFilePicker?() presentFilePicker?()
}, deleteSound: { sound in }, deleteSound: { sound, title in
deleteSoundImpl?(sound) deleteSoundImpl?(sound, title)
}) })
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
@ -442,18 +443,15 @@ public func notificationSoundSelectionController(context: AccountContext, update
presentCustomNotificationSoundFilePicker(context: context, controller: controller, disposable: soundActionDisposable) presentCustomNotificationSoundFilePicker(context: context, controller: controller, disposable: soundActionDisposable)
} }
deleteSoundImpl = { [weak controller] sound in deleteSoundImpl = { [weak controller] sound, title in
guard let controller = controller else { guard let controller = controller else {
return return
} }
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
actionSheet.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
updateState { state in updateState { state in
var state = state var state = state
@ -472,15 +470,10 @@ public func notificationSoundSelectionController(context: AccountContext, update
default: default:
break break
} }
}),
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
}) })
]), ], parseMarkdown: true), in: .window(.root))
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
controller.present(actionSheet, in: .window(.root))
} }
return controller return controller
@ -501,6 +494,8 @@ public func presentCustomNotificationSoundFilePicker(context: AccountContext, co
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
controller.present(legacyICloudFilePicker(theme: presentationData.theme, documentTypes: ["public.mp3"], completion: { urls in controller.present(legacyICloudFilePicker(theme: presentationData.theme, documentTypes: ["public.mp3"], completion: { urls in
guard !urls.isEmpty, let url = urls.first else { guard !urls.isEmpty, let url = urls.first else {
Logger.shared.log("NotificationSoundSelection", "url is nil")
return return
} }
@ -511,7 +506,28 @@ public func presentCustomNotificationSoundFilePicker(context: AccountContext, co
let coordinator = NSFileCoordinator(filePresenter: nil) let coordinator = NSFileCoordinator(filePresenter: nil)
var error: NSError? var error: NSError?
coordinator.coordinate(readingItemAt: url, options: .forUploading, error: &error, byAccessor: { url in coordinator.coordinate(readingItemAt: url, options: .forUploading, error: &error, byAccessor: { souceUrl in
let fileName = url.lastPathComponent
var maybeUrl: URL?
let tempFile = TempBox.shared.tempFile(fileName: "file.mp3")
do {
try FileManager.default.copyItem(at: url, to: URL(fileURLWithPath: tempFile.path))
maybeUrl = URL(fileURLWithPath: tempFile.path)
} catch let e {
Logger.shared.log("NotificationSoundSelection", "copy file error \(e)")
TempBox.shared.dispose(tempFile)
souceUrl.stopAccessingSecurityScopedResource()
return
}
guard let url = maybeUrl else {
Logger.shared.log("NotificationSoundSelection", "temp url is nil")
TempBox.shared.dispose(tempFile)
souceUrl.stopAccessingSecurityScopedResource()
return
}
Queue.mainQueue().async { Queue.mainQueue().async {
do { do {
let asset = AVAsset(url: url) let asset = AVAsset(url: url)
@ -521,37 +537,72 @@ public func presentCustomNotificationSoundFilePicker(context: AccountContext, co
if data.count > settings.maxSize { if data.count > settings.maxSize {
presentUndo(.info(title: presentationData.strings.Notifications_UploadError_TooLarge_Title, text: presentationData.strings.Notifications_UploadError_TooLarge_Text(dataSizeString(Int64(settings.maxSize), formatting: DataSizeStringFormatting(presentationData: presentationData))).string)) presentUndo(.info(title: presentationData.strings.Notifications_UploadError_TooLarge_Title, text: presentationData.strings.Notifications_UploadError_TooLarge_Text(dataSizeString(Int64(settings.maxSize), formatting: DataSizeStringFormatting(presentationData: presentationData))).string))
url.stopAccessingSecurityScopedResource() souceUrl.stopAccessingSecurityScopedResource()
TempBox.shared.dispose(tempFile)
return return
} }
func loadValues(asset: AVAsset, retryCount: Int, completion: @escaping () -> Void) {
asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"], completionHandler: { asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"], completionHandler: {
if asset.statusOfValue(forKey: "duration", error: nil) != .loaded { if asset.statusOfValue(forKey: "tracks", error: nil) == .loading {
url.stopAccessingSecurityScopedResource() if retryCount < 2 {
Queue.mainQueue().after(0.1, {
return loadValues(asset: asset, retryCount: retryCount + 1, completion: completion)
})
} else {
completion()
} }
} else {
completion()
}
})
}
loadValues(asset: asset, retryCount: 0, completion: {
var duration = 0.0
guard let track = asset.tracks(withMediaType: .audio).first else { guard let track = asset.tracks(withMediaType: .audio).first else {
Logger.shared.log("NotificationSoundSelection", "track is nil")
url.stopAccessingSecurityScopedResource() url.stopAccessingSecurityScopedResource()
TempBox.shared.dispose(tempFile)
return return
} }
let duration = track.timeRange.duration.seconds
duration = track.timeRange.duration.seconds
if duration.isZero {
Logger.shared.log("NotificationSoundSelection", "duration is zero")
souceUrl.stopAccessingSecurityScopedResource()
TempBox.shared.dispose(tempFile)
return
}
TempBox.shared.dispose(tempFile)
Queue.mainQueue().async { Queue.mainQueue().async {
if duration > Double(settings.maxDuration) { if duration > Double(settings.maxDuration) {
url.stopAccessingSecurityScopedResource() souceUrl.stopAccessingSecurityScopedResource()
presentUndo(.info(title: presentationData.strings.Notifications_UploadError_TooLong_Title(url.lastPathComponent).string, text: presentationData.strings.Notifications_UploadError_TooLong_Text(stringForDuration(Int32(settings.maxDuration))).string)) presentUndo(.info(title: presentationData.strings.Notifications_UploadError_TooLong_Title(fileName).string, text: presentationData.strings.Notifications_UploadError_TooLong_Text(stringForDuration(Int32(settings.maxDuration))).string))
} else { } else {
disposable.set((context.engine.peers.uploadNotificationSound(title: url.lastPathComponent, data: data) Logger.shared.log("NotificationSoundSelection", "Uploading sound")
disposable.set((context.engine.peers.uploadNotificationSound(title: fileName, data: data)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in
presentUndo(.notificationSoundAdded(title: presentationData.strings.Notifications_UploadSuccess_Title, text: presentationData.strings.Notifications_UploadSuccess_Text(url.deletingPathExtension().lastPathComponent).string, action: nil)) Logger.shared.log("NotificationSoundSelection", "Upload done")
presentUndo(.notificationSoundAdded(title: presentationData.strings.Notifications_UploadSuccess_Title, text: presentationData.strings.Notifications_UploadSuccess_Text(fileName).string, action: nil))
}, error: { _ in }, error: { _ in
url.stopAccessingSecurityScopedResource() Logger.shared.log("NotificationSoundSelection", "Upload error")
souceUrl.stopAccessingSecurityScopedResource()
}, completed: { }, completed: {
url.stopAccessingSecurityScopedResource() souceUrl.stopAccessingSecurityScopedResource()
})) }))
} }
} }

View File

@ -53,9 +53,9 @@ private final class NotificationPeerExceptionArguments {
let complete: () -> Void let complete: () -> Void
let cancel: () -> Void let cancel: () -> Void
let upload: () -> Void let upload: () -> Void
let deleteSound: (PeerMessageSound) -> Void let deleteSound: (PeerMessageSound, String) -> Void
init(account: Account, selectSound: @escaping(PeerMessageSound) -> Void, selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void, selectDisplayPreviews: @escaping (NotificationPeerExceptionSwitcher) -> Void, removeFromExceptions: @escaping () -> Void, complete: @escaping()->Void, cancel: @escaping() -> Void, upload: @escaping () -> Void, deleteSound: @escaping (PeerMessageSound) -> Void) { init(account: Account, selectSound: @escaping(PeerMessageSound) -> Void, selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void, selectDisplayPreviews: @escaping (NotificationPeerExceptionSwitcher) -> Void, removeFromExceptions: @escaping () -> Void, complete: @escaping()->Void, cancel: @escaping() -> Void, upload: @escaping () -> Void, deleteSound: @escaping (PeerMessageSound, String) -> Void) {
self.account = account self.account = account
self.selectSound = selectSound self.selectSound = selectSound
self.selectMode = selectMode self.selectMode = selectMode
@ -233,7 +233,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.selectSound(sound) arguments.selectSound(sound)
}, deleteAction: canBeDeleted ? { }, deleteAction: canBeDeleted ? {
arguments.deleteSound(sound) arguments.deleteSound(sound, text)
} : nil) } : nil)
} }
} }
@ -375,7 +375,7 @@ public func notificationPeerExceptionController(context: AccountContext, updated
var cancelImpl: (() -> Void)? var cancelImpl: (() -> Void)?
let playSoundDisposable = MetaDisposable() let playSoundDisposable = MetaDisposable()
var presentFilePicker: (() -> Void)? var presentFilePicker: (() -> Void)?
var deleteSoundImpl: ((PeerMessageSound) -> Void)? var deleteSoundImpl: ((PeerMessageSound, String) -> Void)?
let soundActionDisposable = MetaDisposable() let soundActionDisposable = MetaDisposable()
@ -411,8 +411,8 @@ public func notificationPeerExceptionController(context: AccountContext, updated
cancelImpl?() cancelImpl?()
}, upload: { }, upload: {
presentFilePicker?() presentFilePicker?()
}, deleteSound: { sound in }, deleteSound: { sound, title in
deleteSoundImpl?(sound) deleteSoundImpl?(sound, title)
}) })
statePromise.set(context.account.postbox.transaction { transaction -> NotificationExceptionPeerState in statePromise.set(context.account.postbox.transaction { transaction -> NotificationExceptionPeerState in
@ -502,18 +502,15 @@ public func notificationPeerExceptionController(context: AccountContext, updated
presentCustomNotificationSoundFilePicker(context: context, controller: controller, disposable: soundActionDisposable) presentCustomNotificationSoundFilePicker(context: context, controller: controller, disposable: soundActionDisposable)
} }
deleteSoundImpl = { [weak controller] sound in deleteSoundImpl = { [weak controller] sound, title in
guard let controller = controller else { guard let controller = controller else {
return return
} }
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
actionSheet.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
updateState { state in updateState { state in
var state = state var state = state
@ -532,15 +529,10 @@ public func notificationPeerExceptionController(context: AccountContext, updated
default: default:
break break
} }
}),
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
}) })
]), ], parseMarkdown: true), in: .window(.root))
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
controller.present(actionSheet, in: .window(.root))
} }
return controller return controller

View File

@ -9844,22 +9844,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self?.beginClearHistory(type: type) self?.beginClearHistory(type: type)
} }
let _ = (self.context.account.postbox.transaction { transaction -> Bool in let _ = (self.context.account.postbox.transaction { transaction -> (isLargeGroupOrChannel: Bool, canClearChannel: Bool) in
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount { if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount {
if memberCount > 1000 { return (memberCount > 1000, cachedData.flags.contains(.canDeleteHistory))
return true
} else { } else {
return false return (false, false)
}
} else {
return false
} }
} }
|> deliverOnMainQueue).start(next: { [weak self] isLargeGroupOrChannel in |> deliverOnMainQueue).start(next: { [weak self] parameters in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let (isLargeGroupOrChannel, canClearChannel) = parameters
guard let peer = strongSelf.presentationInterfaceState.renderedPeer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { guard let peer = strongSelf.presentationInterfaceState.renderedPeer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
return return
} }
@ -9900,7 +9898,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if isLargeGroupOrChannel { if isLargeGroupOrChannel {
canClearCache = true canClearCache = true
canClearForMyself = nil canClearForMyself = nil
canClearForEveryone = nil canClearForEveryone = canClearChannel ? .channel : nil
} else { } else {
canClearCache = true canClearCache = true
canClearForMyself = nil canClearForMyself = nil
@ -9908,15 +9906,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch channel.info { switch channel.info {
case .broadcast: case .broadcast:
if channel.flags.contains(.isCreator) { if channel.flags.contains(.isCreator) {
canClearForEveryone = nil canClearForEveryone = canClearChannel ? .channel : nil
} else { } else {
canClearForEveryone = nil canClearForEveryone = canClearChannel ? .channel : nil
} }
case .group: case .group:
if channel.flags.contains(.isCreator) { if channel.flags.contains(.isCreator) {
canClearForEveryone = nil canClearForEveryone = canClearChannel ? .channel : nil
} else { } else {
canClearForEveryone = nil canClearForEveryone = canClearChannel ? .channel : nil
} }
} }
} }

View File

@ -3674,7 +3674,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return return
} }
if let muteInterval = muteInterval, muteInterval == Int32.max { if let muteInterval = muteInterval, muteInterval == Int32.max {
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) let iconColor: UIColor = .white
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
} }
}) })
}, updatePeerDisplayPreviews: { peerId, displayPreviews in }, updatePeerDisplayPreviews: { peerId, displayPreviews in
@ -3701,7 +3708,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: Int32.max).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: Int32.max).start()
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) let iconColor: UIColor = .white
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}))) })))
self.view.endEditing(true) self.view.endEditing(true)
@ -4082,21 +4096,40 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
self?.openLeavePeer() self?.openLeavePeer(delete: false)
}))) })))
if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_DeleteChannel, textColor: .destructive, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
self?.openLeavePeer(delete: true)
})))
}
} }
case .group: case .group:
if case .member = channel.participationStatus, !headerButtons.contains(.leave) { if case .member = channel.participationStatus, !headerButtons.contains(.leave) {
if !items.isEmpty { if !items.isEmpty {
items.append(.separator) items.append(.separator)
} }
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .destructive, icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .primary, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
self?.openLeavePeer() self?.openLeavePeer(delete: false)
}))) })))
if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
self?.openLeavePeer(delete: true)
})))
}
} }
} }
} else if let group = peer as? TelegramGroup { } else if let group = peer as? TelegramGroup {
@ -4118,8 +4151,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
self?.openLeavePeer() self?.openLeavePeer(delete: false)
}))) })))
if case .creator = group.role {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
self?.openLeavePeer(delete: true)
})))
}
} }
} }
@ -4146,7 +4189,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
case .search: case .search:
self.openChatWithMessageSearch() self.openChatWithMessageSearch()
case .leave: case .leave:
self.openLeavePeer() self.openLeavePeer(delete: false)
case .stop: case .stop:
self.updateBlocked(block: true) self.updateBlocked(block: true)
} }
@ -5547,7 +5590,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}) })
} }
private func openLeavePeer() { private func openLeavePeer(delete: Bool) {
let peerId = self.peerId let peerId = self.peerId
let _ = (self.context.account.postbox.transaction { transaction -> Peer? in let _ = (self.context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId) return transaction.getPeer(peerId)
@ -5556,6 +5599,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self, let peer = peer else { guard let strongSelf = self, let peer = peer else {
return return
} }
var isGroup = false var isGroup = false
if let channel = peer as? TelegramChannel { if let channel = peer as? TelegramChannel {
if case .group = channel.info { if case .group = channel.info {
@ -5564,22 +5608,40 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} else if peer is TelegramGroup { } else if peer is TelegramGroup {
isGroup = true isGroup = true
} }
let presentationData = strongSelf.presentationData
let actionSheet = ActionSheetController(presentationData: presentationData) let title: String
let dismissAction: () -> Void = { [weak actionSheet] in let text: String
actionSheet?.dismissAnimated() let actionText: String
if delete {
if isGroup {
title = strongSelf.presentationData.strings.PeerInfo_DeleteGroupTitle
text = strongSelf.presentationData.strings.PeerInfo_DeleteGroupText(peer.debugDisplayTitle).string
} else {
title = strongSelf.presentationData.strings.PeerInfo_DeleteChannelTitle
text = strongSelf.presentationData.strings.PeerInfo_DeleteChannelText(peer.debugDisplayTitle).string
} }
actionSheet.setItemGroups([ actionText = strongSelf.presentationData.strings.Common_Delete
ActionSheetItemGroup(items: [ } else {
ActionSheetButtonItem(title: isGroup ? presentationData.strings.Group_LeaveGroup : presentationData.strings.Channel_LeaveChannel, color: .destructive, action: { if isGroup {
dismissAction() title = strongSelf.presentationData.strings.PeerInfo_LeaveGroupTitle
self?.deletePeerChat(peer: peer, globally: false) text = strongSelf.presentationData.strings.PeerInfo_LeaveGroupText(peer.debugDisplayTitle).string
}), } else {
]), title = strongSelf.presentationData.strings.PeerInfo_LeaveChannelTitle
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) text = strongSelf.presentationData.strings.PeerInfo_LeaveChannelText(peer.debugDisplayTitle).string
]) }
actionText = strongSelf.presentationData.strings.PeerInfo_AlertLeaveAction
}
strongSelf.view.endEditing(true) strongSelf.view.endEditing(true)
strongSelf.controller?.present(actionSheet, in: .window(.root))
strongSelf.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [
TextAlertAction(type: .destructiveAction, title: actionText, action: {
self?.deletePeerChat(peer: peer, globally: delete)
}),
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
})
], parseMarkdown: true), in: .window(.root))
}) })
} }