mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-18 20:30:51 +00:00
Pre-release improvements
This commit is contained in:
parent
811e5fcc8d
commit
3010d8e045
@ -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?";
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user