From 1fbc6a5bd093232a3b9928b01cdd48ab196fcf2d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 12 Nov 2019 17:42:23 +0400 Subject: [PATCH 1/3] Optimize media deletion --- submodules/Postbox/Sources/MediaBox.swift | 26 ++++++++++ .../Sources/AccountStateManagementUtils.swift | 12 ++++- .../TelegramCore/Sources/DeleteMessages.swift | 48 ++++++++++++------- .../Sources/DeleteMessagesInteractively.swift | 4 +- .../Sources/ProcessRemovedMedia.swift | 13 ----- .../Sources/ScheduledMessages.swift | 6 ++- .../Sources/UpdateCachedPeerData.swift | 6 ++- 7 files changed, 79 insertions(+), 36 deletions(-) delete mode 100644 submodules/TelegramCore/Sources/ProcessRemovedMedia.swift diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index a12e7a9b8f..555a608e95 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -929,6 +929,32 @@ public final class MediaBox { unlink(paths.partial + ".meta") self.fileContexts.removeValue(forKey: id) } + + let uniqueIds = Set(ids.map { $0.id.uniqueId }) + + var pathsToDelete: [String] = [] + + for cacheType in ["cache", "short-cache"] { + if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: "\(self.basePath)/\(cacheType)"), includingPropertiesForKeys: [], options: [.skipsSubdirectoryDescendants], errorHandler: nil) { + while let item = enumerator.nextObject() { + guard let url = item as? NSURL, let path = url.path, let fileName = url.lastPathComponent else { + continue + } + + if let range = fileName.range(of: ":") { + let resourceId = String(fileName[fileName.startIndex ..< range.lowerBound]) + if uniqueIds.contains(resourceId) { + pathsToDelete.append(path) + } + } + } + } + } + + for path in pathsToDelete { + unlink(path) + } + subscriber.putCompletion() } return EmptyDisposable diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index dc9f3f6f5f..ce6e9e6b7d 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2278,18 +2278,26 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } } case let .DeleteMessagesWithGlobalIds(ids): + var resourceIds: [WrappedMediaResourceId] = [] transaction.deleteMessagesWithGlobalIds(ids, forEachMedia: { media in - processRemovedMedia(mediaBox, media) + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + } case let .DeleteMessages(ids): deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids) case let .UpdateMinAvailableMessage(id): if let message = transaction.getMessage(id) { updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: id.peerId, minTimestamp: message.timestamp, forceRootGroupIfNotExists: false) } + var resourceIds: [WrappedMediaResourceId] = [] transaction.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id, forEachMedia: { media in - processRemovedMedia(mediaBox, media) + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + } case let .UpdatePeerChatInclusion(peerId, groupId, changedGroup): let currentInclusion = transaction.getPeerChatListInclusion(peerId) var currentPinningIndex: UInt16? diff --git a/submodules/TelegramCore/Sources/DeleteMessages.swift b/submodules/TelegramCore/Sources/DeleteMessages.swift index e271570313..14dfab3fd3 100644 --- a/submodules/TelegramCore/Sources/DeleteMessages.swift +++ b/submodules/TelegramCore/Sources/DeleteMessages.swift @@ -4,48 +4,64 @@ import SwiftSignalKit import SyncCore -private func removeMessageMedia(message: Message, mediaBox: MediaBox) { - for media in message.media { - if let image = media as? TelegramMediaImage { - let _ = mediaBox.removeCachedResources(Set(image.representations.map({ WrappedMediaResourceId($0.resource.id) }))).start() - } else if let file = media as? TelegramMediaFile { - let _ = mediaBox.removeCachedResources(Set(file.previewRepresentations.map({ WrappedMediaResourceId($0.resource.id) }))).start() - let _ = mediaBox.removeCachedResources(Set([WrappedMediaResourceId(file.resource.id)])).start() +func addMessageMediaResourceIdsToRemove(media: Media, resourceIds: inout [WrappedMediaResourceId]) { + if let image = media as? TelegramMediaImage { + for representation in image.representations { + resourceIds.append(WrappedMediaResourceId(representation.resource.id)) } + } else if let file = media as? TelegramMediaFile { + for representation in file.previewRepresentations { + resourceIds.append(WrappedMediaResourceId(representation.resource.id)) + } + resourceIds.append(WrappedMediaResourceId(file.resource.id)) + } +} + +func addMessageMediaResourceIdsToRemove(message: Message, resourceIds: inout [WrappedMediaResourceId]) { + for media in message.media { + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) } } public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [MessageId], deleteMedia: Bool = true) { + var resourceIds: [WrappedMediaResourceId] = [] if deleteMedia { for id in ids { if id.peerId.namespace == Namespaces.Peer.SecretChat { if let message = transaction.getMessage(id) { - removeMessageMedia(message: message, mediaBox: mediaBox) + addMessageMediaResourceIdsToRemove(message: message, resourceIds: &resourceIds) } } } } - transaction.deleteMessages(ids, forEachMedia: { media in - if deleteMedia { - processRemovedMedia(mediaBox, media) - } + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + } + transaction.deleteMessages(ids, forEachMedia: { _ in }) } public func deleteAllMessagesWithAuthor(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace) { + var resourceIds: [WrappedMediaResourceId] = [] transaction.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: { media in - processRemovedMedia(mediaBox, media) + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + } } public func clearHistory(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, namespaces: MessageIdNamespaces) { if peerId.namespace == Namespaces.Peer.SecretChat { + var resourceIds: [WrappedMediaResourceId] = [] transaction.withAllMessages(peerId: peerId, { message in - removeMessageMedia(message: message, mediaBox: mediaBox) + addMessageMediaResourceIdsToRemove(message: message, resourceIds: &resourceIds) return true }) + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + } } - transaction.clearHistory(peerId, namespaces: namespaces, forEachMedia: { media in - processRemovedMedia(mediaBox, media) + transaction.clearHistory(peerId, namespaces: namespaces, forEachMedia: { _ in }) } diff --git a/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift b/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift index dd3c1fe45d..2d45771b69 100644 --- a/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift +++ b/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift @@ -147,9 +147,7 @@ public func clearAuthorHistory(account: Account, peerId: PeerId, memberId: PeerI |> `catch` { success -> Signal in if success { return account.postbox.transaction { transaction -> Void in - transaction.removeAllMessagesWithAuthor(peerId, authorId: memberId, namespace: Namespaces.Message.Cloud, forEachMedia: { media in - processRemovedMedia(account.postbox.mediaBox, media) - }) + deleteAllMessagesWithAuthor(transaction: transaction, mediaBox: account.postbox.mediaBox, peerId: peerId, authorId: memberId, namespace: Namespaces.Message.Cloud) } } else { return .complete() diff --git a/submodules/TelegramCore/Sources/ProcessRemovedMedia.swift b/submodules/TelegramCore/Sources/ProcessRemovedMedia.swift deleted file mode 100644 index 725c1fb04b..0000000000 --- a/submodules/TelegramCore/Sources/ProcessRemovedMedia.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation -import Postbox - -import SyncCore - -func processRemovedMedia(_ mediaBox: MediaBox, _ media: Media) { - if let image = media as? TelegramMediaImage { - let _ = mediaBox.removeCachedResources(Set(image.representations.map({ WrappedMediaResourceId($0.resource.id) }))).start() - } else if let file = media as? TelegramMediaFile { - let _ = mediaBox.removeCachedResources(Set(file.previewRepresentations.map({ WrappedMediaResourceId($0.resource.id) }))).start() - let _ = mediaBox.removeCachedResources(Set([WrappedMediaResourceId(file.resource.id)])).start() - } -} diff --git a/submodules/TelegramCore/Sources/ScheduledMessages.swift b/submodules/TelegramCore/Sources/ScheduledMessages.swift index d40720f44c..f8500bb722 100644 --- a/submodules/TelegramCore/Sources/ScheduledMessages.swift +++ b/submodules/TelegramCore/Sources/ScheduledMessages.swift @@ -104,9 +104,13 @@ func managedApplyPendingScheduledMessagesActions(postbox: Postbox, network: Netw }) |> then( postbox.transaction { transaction -> Void in + var resourceIds: [WrappedMediaResourceId] = [] transaction.deleteMessages([entry.id], forEachMedia: { media in - processRemovedMedia(postbox.mediaBox, media) + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) + if !resourceIds.isEmpty { + let _ = postbox.mediaBox.removeCachedResources(Set(resourceIds)).start() + } } |> ignoreValues ) diff --git a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift index 6ba93d2ea0..d8318d1860 100644 --- a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift @@ -421,9 +421,13 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { + var resourceIds: [WrappedMediaResourceId] = [] transaction.deleteMessagesInRange(peerId: peerId, namespace: minAvailableMessageId.namespace, minId: 1, maxId: minAvailableMessageId.id, forEachMedia: { media in - processRemovedMedia(postbox.mediaBox, media) + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) + if !resourceIds.isEmpty { + let _ = postbox.mediaBox.removeCachedResources(Set(resourceIds)).start() + } } case .chatFull: break From d564302984df61a74a8fc7084b7e8bd1dc6ef67c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 12 Nov 2019 18:16:20 +0400 Subject: [PATCH 2/3] Fix accent color selection when auto-night mode is triggered --- .../Themes/ThemeAccentColorController.swift | 14 ++++++++++---- .../Sources/Themes/ThemeSettingsController.swift | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index e3470bac21..ec8f79206c 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -65,18 +65,24 @@ final class ThemeAccentColorController: ViewController { if let strongSelf = self { let context = strongSelf.context let _ = (updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in + let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered + var currentTheme = current.theme + if autoNightModeTriggered { + currentTheme = current.automaticThemeSwitchSetting.theme + } + var themeSpecificAccentColors = current.themeSpecificAccentColors let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color)) - themeSpecificAccentColors[current.theme.index] = color + themeSpecificAccentColors[currentTheme.index] = color var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers - let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) ?? defaultPresentationTheme + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) ?? defaultPresentationTheme var chatWallpaper = current.chatWallpaper - if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper { + if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.hasWallpaper { } else { chatWallpaper = theme.chat.defaultWallpaper - themeSpecificChatWallpapers[current.theme.index] = chatWallpaper + themeSpecificChatWallpapers[currentTheme.index] = chatWallpaper } return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: strongSelf.currentTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 714faf1251..2fc1621d03 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -441,18 +441,24 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The pushControllerImpl?(ThemeGridController(context: context)) }, selectAccentColor: { color in let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in - guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: current.theme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) else { + let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered + var currentTheme = current.theme + if autoNightModeTriggered { + currentTheme = current.automaticThemeSwitchSetting.theme + } + + guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) else { return current } var themeSpecificAccentColors = current.themeSpecificAccentColors - themeSpecificAccentColors[current.theme.index] = color + themeSpecificAccentColors[currentTheme.index] = color var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers var chatWallpaper = current.chatWallpaper - if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper { + if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.hasWallpaper { } else { chatWallpaper = theme.chat.defaultWallpaper - themeSpecificChatWallpapers[current.theme.index] = chatWallpaper + themeSpecificChatWallpapers[currentTheme.index] = chatWallpaper } return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) From ae19905555447eb500859439275c7d762cc531b7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 12 Nov 2019 18:17:32 +0400 Subject: [PATCH 3/3] Fix undo popup double dismissal --- submodules/UndoUI/Sources/UndoOverlayController.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index 49e1b0e111..fdb12ffcaf 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -21,6 +21,7 @@ public final class UndoOverlayController: ViewController { private var action: (Bool) -> Void private var didPlayPresentationAnimation = false + private var dismissed = false public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, animateInAsReplacement: Bool = false, action: @escaping (Bool) -> Void) { self.presentationData = presentationData @@ -75,6 +76,10 @@ public final class UndoOverlayController: ViewController { } override public func dismiss(completion: (() -> Void)? = nil) { + guard !self.dismissed else { + return + } + self.dismissed = true (self.displayNode as! UndoOverlayControllerNode).animateOut(completion: { [weak self] in self?.presentingViewController?.dismiss(animated: false, completion: nil) completion?()