From 8c033a37bc0dd0399a9607e44aecfba1e6725f10 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 3 Nov 2021 13:36:27 +0400 Subject: [PATCH 01/20] Fix channel request approval service message --- .../Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index ecf3569431..d0019fb934 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -490,9 +490,11 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch participantResult { case let .channelParticipant(participant, _, _): switch participant { - case let .channelParticipantSelf(_, _, inviterId, invitedDate): + case let .channelParticipantSelf(flags, _, inviterId, invitedDate): invitedBy = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(inviterId)) - invitedOn = invitedDate + if (flags & (1 << 0)) != 0 { + invitedOn = invitedDate + } default: break } From 9290df840d192cbcb8863b6eae5e06d7c2e0582b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:09:01 +0400 Subject: [PATCH 02/20] Compress logs --- submodules/DebugSettingsUI/BUILD | 1 + .../Sources/DebugController.swift | 178 +++++++++++------- 2 files changed, 116 insertions(+), 63 deletions(-) diff --git a/submodules/DebugSettingsUI/BUILD b/submodules/DebugSettingsUI/BUILD index f536359fa2..9c920b19db 100644 --- a/submodules/DebugSettingsUI/BUILD +++ b/submodules/DebugSettingsUI/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/OverlayStatusController:OverlayStatusController", "//submodules/AccountContext:AccountContext", "//submodules/AppBundle:AppBundle", + "//submodules/GZip:GZip" ], visibility = [ "//visibility:public", diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 72dc352b68..8b043d797e 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -13,6 +13,7 @@ import PresentationDataUtils import OverlayStatusController import AccountContext import AppBundle +import GZip @objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { @@ -219,42 +220,44 @@ private enum DebugControllerEntry: ItemListNodeEntry { |> deliverOnMainQueue).start(next: { logs in let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(presentationData: presentationData) - + var items: [ActionSheetButtonItem] = [] - + if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp { items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) controller.peerSelected = { [weak controller] peer in let peerId = peer.id - + if let strongController = controller { strongController.dismiss() - + let lineFeed = "\n".data(using: .utf8)! - var logData: Data = Data() + var rawLogData: Data = Data() for (name, path) in logs { - if !logData.isEmpty { - logData.append(lineFeed) - logData.append(lineFeed) + if !rawLogData.isEmpty { + rawLogData.append(lineFeed) + rawLogData.append(lineFeed) } - - logData.append("------ File: \(name) ------\n".data(using: .utf8)!) - + + rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!) + if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { - logData.append(data) + rawLogData.append(data) } } - + + let gzippedData = TGGZipData(rawLogData, 1.0) + let id = Int64.random(in: Int64.min ... Int64.max) - let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false) - context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData) - - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")]) + let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false) + context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData) + + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")]) let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) - + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } } @@ -263,7 +266,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { } items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - + let composeController = MFMailComposeViewController() composeController.mailComposeDelegate = arguments.mailComposeDelegate composeController.setSubject("Telegram Logs") @@ -274,12 +277,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { } arguments.getRootController()?.present(composeController, animated: true, completion: nil) })) - + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) + ])]) arguments.presentController(actionSheet, nil) }) }) @@ -371,42 +374,44 @@ private enum DebugControllerEntry: ItemListNodeEntry { |> deliverOnMainQueue).start(next: { logs in let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(presentationData: presentationData) - + var items: [ActionSheetButtonItem] = [] - + if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp { items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) controller.peerSelected = { [weak controller] peer in let peerId = peer.id - + if let strongController = controller { strongController.dismiss() - + let lineFeed = "\n".data(using: .utf8)! - var logData: Data = Data() + var rawLogData: Data = Data() for (name, path) in logs { - if !logData.isEmpty { - logData.append(lineFeed) - logData.append(lineFeed) + if !rawLogData.isEmpty { + rawLogData.append(lineFeed) + rawLogData.append(lineFeed) } - - logData.append("------ File: \(name) ------\n".data(using: .utf8)!) - + + rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!) + if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { - logData.append(data) + rawLogData.append(data) } } - + + let gzippedData = TGGZipData(rawLogData, 1.0) + let id = Int64.random(in: Int64.min ... Int64.max) - let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false) - context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData) - - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")]) + let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false) + context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData) + + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")]) let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) - + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } } @@ -415,7 +420,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { } items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - + let composeController = MFMailComposeViewController() composeController.mailComposeDelegate = arguments.mailComposeDelegate composeController.setSubject("Telegram Logs") @@ -426,39 +431,86 @@ private enum DebugControllerEntry: ItemListNodeEntry { } arguments.getRootController()?.present(composeController, animated: true, completion: nil) })) - + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) + ])]) arguments.presentController(actionSheet, nil) }) }) case .sendNotificationLogs: - return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs", label: "", sectionId: self.section, style: .blocks, action: { - let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notificationServiceLogs").collectLogs() + return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: { + let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notification-logs").collectLogs() |> deliverOnMainQueue).start(next: { logs in - guard let context = arguments.context else { - return - } - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) - controller.peerSelected = { [weak controller] peer in - let peerId = peer.id - - if let strongController = controller { - strongController.dismiss() - - let messages = logs.map { (name, path) -> EnqueueMessage in + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationData: presentationData) + + var items: [ActionSheetButtonItem] = [] + + if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp { + items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) + controller.peerSelected = { [weak controller] peer in + let peerId = peer.id + + if let strongController = controller { + strongController.dismiss() + + let lineFeed = "\n".data(using: .utf8)! + var rawLogData: Data = Data() + for (name, path) in logs { + if !rawLogData.isEmpty { + rawLogData.append(lineFeed) + rawLogData.append(lineFeed) + } + + rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!) + + if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { + rawLogData.append(data) + } + } + + let gzippedData = TGGZipData(rawLogData, 1.0) + let id = Int64.random(in: Int64.min ... Int64.max) - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) - return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) + let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false) + context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData) + + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")]) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) + + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } - let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start() + } + arguments.pushController(controller) + })) + } + items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let composeController = MFMailComposeViewController() + composeController.mailComposeDelegate = arguments.mailComposeDelegate + composeController.setSubject("Telegram Logs") + for (name, path) in logs { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { + composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) } } - arguments.pushController(controller) - }) + arguments.getRootController()?.present(composeController, animated: true, completion: nil) + })) + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + arguments.presentController(actionSheet, nil) + }) }) case .sendCriticalLogs: return ItemListDisclosureItem(presentationData: presentationData, title: "Send Critical Logs", label: "", sectionId: self.section, style: .blocks, action: { @@ -837,7 +889,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present // entries.append(.testStickerImport(presentationData.theme)) entries.append(.sendLogs(presentationData.theme)) - entries.append(.sendOneLog(presentationData.theme)) + //entries.append(.sendOneLog(presentationData.theme)) entries.append(.sendShareLogs) entries.append(.sendNotificationLogs(presentationData.theme)) entries.append(.sendCriticalLogs(presentationData.theme)) From ddb0999af043784b274cbfdaa09f0a010d2a16fa Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:09:34 +0400 Subject: [PATCH 03/20] Support refreshDueToExternalTransaction in more views --- .../Sources/AdditionalChatListItemsView.swift | 4 ++ .../Sources/AllChatListHolesView.swift | 4 ++ .../Postbox/Sources/CachedItemView.swift | 4 ++ .../Postbox/Sources/CachedPeerDataView.swift | 4 ++ .../Postbox/Sources/ContactPeersView.swift | 4 ++ .../Postbox/Sources/DeletedMessagesView.swift | 4 ++ .../Sources/GlobalMessageTagsView.swift | 19 ++++++ .../Postbox/Sources/HistoryTagInfoView.swift | 16 +++++ ...idatedMessageHistoryTagSummariesView.swift | 13 ++++ .../Sources/ItemCollectionIdsView.swift | 14 ++++ .../Sources/ItemCollectionInfoView.swift | 4 ++ .../Sources/ItemCollectionInfosView.swift | 6 +- .../Sources/LocalMessageTagsView.swift | 4 ++ .../MessageHistoryTagSummaryView.swift | 10 +++ .../Postbox/Sources/MessageHistoryView.swift | 2 +- .../Sources/MessageOfInterestHolesView.swift | 4 ++ submodules/Postbox/Sources/MessagesView.swift | 4 ++ .../Sources/MutableBasicPeerView.swift | 4 ++ .../MutablePeerChatInclusionView.swift | 4 ++ .../Postbox/Sources/OrderedItemListView.swift | 4 ++ .../Postbox/Sources/PeerChatStateTable.swift | 14 ++-- .../Postbox/Sources/PeerChatStateView.swift | 14 +++- ...icationSettingsBehaviorTimestampView.swift | 10 +++ .../PeerNotificationSettingsView.swift | 37 +++++++++++ .../Postbox/Sources/PeerPresencesView.swift | 34 ++++++++++ submodules/Postbox/Sources/PeerView.swift | 4 ++ .../PendingMessageActionsSummaryView.swift | 4 ++ .../Sources/PendingMessageActionsView.swift | 4 ++ .../PendingPeerNotificationSettingsView.swift | 4 ++ submodules/Postbox/Sources/Postbox.swift | 2 +- submodules/Postbox/Sources/PostboxView.swift | 11 ++++ .../Postbox/Sources/PreferencesEntry.swift | 26 ++++++++ .../Postbox/Sources/PreferencesView.swift | 15 +++++ .../SynchronizeGroupMessageStatsView.swift | 10 +++ .../Postbox/Sources/TopChatMessageView.swift | 35 ++++++++++ .../Sources/UnreadMessageCountsView.swift | 66 ++++++++++++++++++- 36 files changed, 407 insertions(+), 15 deletions(-) diff --git a/submodules/Postbox/Sources/AdditionalChatListItemsView.swift b/submodules/Postbox/Sources/AdditionalChatListItemsView.swift index 4c54a00236..6bf80cc6c9 100644 --- a/submodules/Postbox/Sources/AdditionalChatListItemsView.swift +++ b/submodules/Postbox/Sources/AdditionalChatListItemsView.swift @@ -14,6 +14,10 @@ final class MutableAdditionalChatListItemsView: MutablePostboxView { } return false } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return AdditionalChatListItemsView(self) diff --git a/submodules/Postbox/Sources/AllChatListHolesView.swift b/submodules/Postbox/Sources/AllChatListHolesView.swift index 4e582e9ed6..3cadfe60a7 100644 --- a/submodules/Postbox/Sources/AllChatListHolesView.swift +++ b/submodules/Postbox/Sources/AllChatListHolesView.swift @@ -48,6 +48,10 @@ final class MutableAllChatListHolesView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return AllChatListHolesView(self) diff --git a/submodules/Postbox/Sources/CachedItemView.swift b/submodules/Postbox/Sources/CachedItemView.swift index cbaefed63d..6da64b46ed 100644 --- a/submodules/Postbox/Sources/CachedItemView.swift +++ b/submodules/Postbox/Sources/CachedItemView.swift @@ -16,6 +16,10 @@ final class MutableCachedItemView: MutablePostboxView { } return false } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return CachedItemView(self) diff --git a/submodules/Postbox/Sources/CachedPeerDataView.swift b/submodules/Postbox/Sources/CachedPeerDataView.swift index c3cb02ac7b..9e432d5f25 100644 --- a/submodules/Postbox/Sources/CachedPeerDataView.swift +++ b/submodules/Postbox/Sources/CachedPeerDataView.swift @@ -17,6 +17,10 @@ final class MutableCachedPeerDataView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return CachedPeerDataView(self) diff --git a/submodules/Postbox/Sources/ContactPeersView.swift b/submodules/Postbox/Sources/ContactPeersView.swift index 341646e179..b48fe93722 100644 --- a/submodules/Postbox/Sources/ContactPeersView.swift +++ b/submodules/Postbox/Sources/ContactPeersView.swift @@ -70,6 +70,10 @@ final class MutableContactPeersView: MutablePostboxView { return updated } + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } + func immutableView() -> PostboxView { return ContactPeersView(self) } diff --git a/submodules/Postbox/Sources/DeletedMessagesView.swift b/submodules/Postbox/Sources/DeletedMessagesView.swift index 95c8e2d528..7f3b4edaaf 100644 --- a/submodules/Postbox/Sources/DeletedMessagesView.swift +++ b/submodules/Postbox/Sources/DeletedMessagesView.swift @@ -33,6 +33,10 @@ final class MutableDeletedMessagesView: MutablePostboxView { return updated } + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } + func immutableView() -> PostboxView { return DeletedMessagesView(self) } diff --git a/submodules/Postbox/Sources/GlobalMessageTagsView.swift b/submodules/Postbox/Sources/GlobalMessageTagsView.swift index 941f6c8096..6733a52339 100644 --- a/submodules/Postbox/Sources/GlobalMessageTagsView.swift +++ b/submodules/Postbox/Sources/GlobalMessageTagsView.swift @@ -202,6 +202,25 @@ final class MutableGlobalMessageTagsView: MutablePostboxView { return hasChanges } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let (entries, lower, upper) = postbox.messageHistoryTable.entriesAround(globalTagMask: globalTag, index: position, count: count) + + self.entries = entries.map { entry -> InternalGlobalMessageTagsEntry in + switch entry { + case let .message(message): + return .intermediateMessage(message) + case let .hole(index): + return .hole(index) + } + } + self.earlier = lower + self.later = upper + + self.render(postbox: postbox) + + return true + } private func add(_ entry: InternalGlobalMessageTagsEntry) -> Bool { if self.entries.count == 0 { diff --git a/submodules/Postbox/Sources/HistoryTagInfoView.swift b/submodules/Postbox/Sources/HistoryTagInfoView.swift index d3bd9de9a6..7892353518 100644 --- a/submodules/Postbox/Sources/HistoryTagInfoView.swift +++ b/submodules/Postbox/Sources/HistoryTagInfoView.swift @@ -61,6 +61,22 @@ final class MutableHistoryTagInfoView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var currentIndex: MessageIndex? + for namespace in postbox.messageHistoryIndexTable.existingNamespaces(peerId: self.peerId) { + if let index = postbox.messageHistoryTagsTable.latestIndex(tag: self.tag, peerId: self.peerId, namespace: namespace) { + currentIndex = index + break + } + } + if self.currentIndex != currentIndex { + self.currentIndex = currentIndex + return true + } else { + return false + } + } func immutableView() -> PostboxView { return HistoryTagInfoView(self) diff --git a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift index 7e082850ff..13224a2502 100644 --- a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift +++ b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift @@ -37,6 +37,19 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var entries = Set() + for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) { + entries.insert(entry) + } + if self.entries != entries { + self.entries = entries + return true + } else { + return false + } + } func immutableView() -> PostboxView { return InvalidatedMessageHistoryTagSummariesView(self) diff --git a/submodules/Postbox/Sources/ItemCollectionIdsView.swift b/submodules/Postbox/Sources/ItemCollectionIdsView.swift index fed265431f..232419de61 100644 --- a/submodules/Postbox/Sources/ItemCollectionIdsView.swift +++ b/submodules/Postbox/Sources/ItemCollectionIdsView.swift @@ -40,6 +40,20 @@ final class MutableItemCollectionIdsView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var idsByNamespace: [ItemCollectionId.Namespace: Set] = [:] + for namespace in namespaces { + let ids = postbox.itemCollectionInfoTable.getIds(namespace: namespace) + idsByNamespace[namespace] = Set(ids) + } + if self.idsByNamespace != idsByNamespace { + self.idsByNamespace = idsByNamespace + return true + } else { + return false + } + } func immutableView() -> PostboxView { return ItemCollectionIdsView(self) diff --git a/submodules/Postbox/Sources/ItemCollectionInfoView.swift b/submodules/Postbox/Sources/ItemCollectionInfoView.swift index 7ce6008419..6eb601595f 100644 --- a/submodules/Postbox/Sources/ItemCollectionInfoView.swift +++ b/submodules/Postbox/Sources/ItemCollectionInfoView.swift @@ -48,6 +48,10 @@ final class MutableItemCollectionInfoView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return ItemCollectionInfoView(self) diff --git a/submodules/Postbox/Sources/ItemCollectionInfosView.swift b/submodules/Postbox/Sources/ItemCollectionInfosView.swift index d82b6af593..c39cbe7164 100644 --- a/submodules/Postbox/Sources/ItemCollectionInfosView.swift +++ b/submodules/Postbox/Sources/ItemCollectionInfosView.swift @@ -90,12 +90,14 @@ final class MutableItemCollectionInfosView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return ItemCollectionInfosView(self) } - - } public final class ItemCollectionInfosView: PostboxView { diff --git a/submodules/Postbox/Sources/LocalMessageTagsView.swift b/submodules/Postbox/Sources/LocalMessageTagsView.swift index 356f6c8efb..10ec9cc8fa 100644 --- a/submodules/Postbox/Sources/LocalMessageTagsView.swift +++ b/submodules/Postbox/Sources/LocalMessageTagsView.swift @@ -50,6 +50,10 @@ final class MutableLocalMessageTagsView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return LocalMessageTagsView(self) diff --git a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift index b56759162c..4e62e434cf 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift @@ -25,6 +25,16 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView { return hasChanges } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace))?.count + if self.count != count { + self.count = count + return true + } else { + return false + } + } func immutableView() -> PostboxView { return MessageHistoryTagSummaryView(self) diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 7543827b81..280bdb4bf0 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -739,7 +739,7 @@ final class MutableMessageHistoryView { } case let .peerChatState(peerId, _): if transaction.currentUpdatedPeerChatStates.contains(peerId) { - updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId) as? PeerChatState) + updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId)?.getLegacy() as? PeerChatState) hasChanges = true } case .totalUnreadState: diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index ea2e2dbb41..033d9fec90 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -171,6 +171,10 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return MessageOfInterestHolesView(self) diff --git a/submodules/Postbox/Sources/MessagesView.swift b/submodules/Postbox/Sources/MessagesView.swift index ae4a1b4d94..91808bb160 100644 --- a/submodules/Postbox/Sources/MessagesView.swift +++ b/submodules/Postbox/Sources/MessagesView.swift @@ -51,6 +51,10 @@ final class MutableMessagesView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return MessagesView(self) diff --git a/submodules/Postbox/Sources/MutableBasicPeerView.swift b/submodules/Postbox/Sources/MutableBasicPeerView.swift index a2f30f8f6b..0236960e1d 100644 --- a/submodules/Postbox/Sources/MutableBasicPeerView.swift +++ b/submodules/Postbox/Sources/MutableBasicPeerView.swift @@ -42,6 +42,10 @@ final class MutableBasicPeerView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return BasicPeerView(self) diff --git a/submodules/Postbox/Sources/MutablePeerChatInclusionView.swift b/submodules/Postbox/Sources/MutablePeerChatInclusionView.swift index 9b7d5d1952..5c5daec04e 100644 --- a/submodules/Postbox/Sources/MutablePeerChatInclusionView.swift +++ b/submodules/Postbox/Sources/MutablePeerChatInclusionView.swift @@ -22,6 +22,10 @@ final class MutablePeerChatInclusionView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return PeerChatInclusionView(self) diff --git a/submodules/Postbox/Sources/OrderedItemListView.swift b/submodules/Postbox/Sources/OrderedItemListView.swift index bdd066a8fc..d8bf403a00 100644 --- a/submodules/Postbox/Sources/OrderedItemListView.swift +++ b/submodules/Postbox/Sources/OrderedItemListView.swift @@ -51,6 +51,10 @@ final class MutableOrderedItemListView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return OrderedItemListView(self) diff --git a/submodules/Postbox/Sources/PeerChatStateTable.swift b/submodules/Postbox/Sources/PeerChatStateTable.swift index b0f059fc17..716d025d4b 100644 --- a/submodules/Postbox/Sources/PeerChatStateTable.swift +++ b/submodules/Postbox/Sources/PeerChatStateTable.swift @@ -5,7 +5,7 @@ final class PeerChatStateTable: Table { return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false) } - private var cachedPeerChatStates: [PeerId: PostboxCoding?] = [:] + private var cachedPeerChatStates: [PeerId: CodableEntry?] = [:] private var updatedPeerIds = Set() private let sharedKey = ValueBoxKey(length: 8) @@ -15,11 +15,12 @@ final class PeerChatStateTable: Table { return self.sharedKey } - func get(_ id: PeerId) -> PostboxCoding? { + func get(_ id: PeerId) -> CodableEntry? { if let state = self.cachedPeerChatStates[id] { return state } else { - if let value = self.valueBox.get(self.table, key: self.key(id)), let state = PostboxDecoder(buffer: value).decodeRootObject() { + if let value = self.valueBox.get(self.table, key: self.key(id)) { + let state = CodableEntry(data: value.makeData()) self.cachedPeerChatStates[id] = state return state } else { @@ -29,7 +30,7 @@ final class PeerChatStateTable: Table { } } - func set(_ id: PeerId, state: PostboxCoding?) { + func set(_ id: PeerId, state: CodableEntry?) { self.cachedPeerChatStates[id] = state self.updatedPeerIds.insert(id) } @@ -41,12 +42,9 @@ final class PeerChatStateTable: Table { override func beforeCommit() { if !self.updatedPeerIds.isEmpty { - let sharedEncoder = PostboxEncoder() for id in self.updatedPeerIds { if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState { - sharedEncoder.reset() - sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: ReadBuffer(data: state.data)) } else { self.valueBox.remove(self.table, key: self.key(id), secure: false) } diff --git a/submodules/Postbox/Sources/PeerChatStateView.swift b/submodules/Postbox/Sources/PeerChatStateView.swift index 3c0edec8d6..df81cba5b3 100644 --- a/submodules/Postbox/Sources/PeerChatStateView.swift +++ b/submodules/Postbox/Sources/PeerChatStateView.swift @@ -2,7 +2,7 @@ import Foundation final class MutablePeerChatStateView: MutablePostboxView { let peerId: PeerId - var chatState: PostboxCoding? + var chatState: CodableEntry? init(postbox: PostboxImpl, peerId: PeerId) { self.peerId = peerId @@ -17,6 +17,16 @@ final class MutablePeerChatStateView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let chatState = postbox.peerChatStateTable.get(self.peerId) + if self.chatState != chatState { + self.chatState = chatState + return true + } else { + return false + } + } func immutableView() -> PostboxView { return PeerChatStateView(self) @@ -25,7 +35,7 @@ final class MutablePeerChatStateView: MutablePostboxView { public final class PeerChatStateView: PostboxView { public let peerId: PeerId - public let chatState: PostboxCoding? + public let chatState: CodableEntry? init(_ view: MutablePeerChatStateView) { self.peerId = view.peerId diff --git a/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift b/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift index dd6448f5b8..a00d2b33b6 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift @@ -19,6 +19,16 @@ final class MutablePeerNotificationSettingsBehaviorTimestampView: MutablePostbox return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let earliestTimestamp = postbox.peerNotificationSettingsBehaviorTable.getEarliest()?.1 + if self.earliestTimestamp != earliestTimestamp { + self.earliestTimestamp = earliestTimestamp + return true + } else { + return false + } + } func immutableView() -> PostboxView { return PeerNotificationSettingsBehaviorTimestampView(self) diff --git a/submodules/Postbox/Sources/PeerNotificationSettingsView.swift b/submodules/Postbox/Sources/PeerNotificationSettingsView.swift index a1521e17a0..2d85dcf466 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettingsView.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettingsView.swift @@ -36,6 +36,43 @@ final class MutablePeerNotificationSettingsView: MutablePostboxView { return false } } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var notificationSettings: [PeerId: PeerNotificationSettings] = [:] + for peerId in self.peerIds { + var notificationPeerId = peerId + if let peer = postbox.peerTable.get(peerId), let associatedPeerId = peer.associatedPeerId { + notificationPeerId = associatedPeerId + } + if let settings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId) { + notificationSettings[peerId] = settings + } + } + + var updated = false + if self.notificationSettings.count != notificationSettings.count { + updated = true + } else { + for (key, value) in self.notificationSettings { + if let other = notificationSettings[key] { + if !other.isEqual(to: value) { + updated = true + break + } + } else { + updated = true + break + } + } + } + + if updated { + self.notificationSettings = notificationSettings + return true + } else { + return false + } + } func immutableView() -> PostboxView { return PeerNotificationSettingsView(self) diff --git a/submodules/Postbox/Sources/PeerPresencesView.swift b/submodules/Postbox/Sources/PeerPresencesView.swift index 4f280d944b..51f4a32c03 100644 --- a/submodules/Postbox/Sources/PeerPresencesView.swift +++ b/submodules/Postbox/Sources/PeerPresencesView.swift @@ -26,6 +26,40 @@ final class MutablePeerPresencesView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var presences: [PeerId: PeerPresence] = [:] + + for id in self.ids { + if let presence = postbox.peerPresenceTable.get(id) { + presences[id] = presence + } + } + + var updated = false + if self.presences.count != presences.count { + updated = true + } else { + for (key, value) in self.presences { + if let other = presences[key] { + if !other.isEqual(to: value) { + updated = true + break + } + } else { + updated = true + break + } + } + } + + if updated { + self.presences = presences + return true + } else { + return false + } + } func immutableView() -> PostboxView { return PeerPresencesView(self) diff --git a/submodules/Postbox/Sources/PeerView.swift b/submodules/Postbox/Sources/PeerView.swift index 9fb5046de6..6db9f56c81 100644 --- a/submodules/Postbox/Sources/PeerView.swift +++ b/submodules/Postbox/Sources/PeerView.swift @@ -253,6 +253,10 @@ final class MutablePeerView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return PeerView(self) diff --git a/submodules/Postbox/Sources/PendingMessageActionsSummaryView.swift b/submodules/Postbox/Sources/PendingMessageActionsSummaryView.swift index dbfa7c7e56..79161a526d 100644 --- a/submodules/Postbox/Sources/PendingMessageActionsSummaryView.swift +++ b/submodules/Postbox/Sources/PendingMessageActionsSummaryView.swift @@ -16,6 +16,10 @@ final class MutablePendingMessageActionsSummaryView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return PendingMessageActionsSummaryView(self) diff --git a/submodules/Postbox/Sources/PendingMessageActionsView.swift b/submodules/Postbox/Sources/PendingMessageActionsView.swift index 87662edaef..1b24f63f63 100644 --- a/submodules/Postbox/Sources/PendingMessageActionsView.swift +++ b/submodules/Postbox/Sources/PendingMessageActionsView.swift @@ -38,6 +38,10 @@ final class MutablePendingMessageActionsView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return PendingMessageActionsView(self) diff --git a/submodules/Postbox/Sources/PendingPeerNotificationSettingsView.swift b/submodules/Postbox/Sources/PendingPeerNotificationSettingsView.swift index dfc7e22d22..d7da2420e9 100644 --- a/submodules/Postbox/Sources/PendingPeerNotificationSettingsView.swift +++ b/submodules/Postbox/Sources/PendingPeerNotificationSettingsView.swift @@ -25,6 +25,10 @@ final class MutablePendingPeerNotificationSettingsView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } func immutableView() -> PostboxView { return PendingPeerNotificationSettingsView(self) diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index ea1b24e9da..321edd1ef9 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -2212,7 +2212,7 @@ final class PostboxImpl { } fileprivate func setPeerChatState(_ id: PeerId, state: PeerChatState) { - self.peerChatStateTable.set(id, state: state) + self.peerChatStateTable.set(id, state: CodableEntry(legacyValue: state)) self.currentUpdatedPeerChatStates.insert(id) } diff --git a/submodules/Postbox/Sources/PostboxView.swift b/submodules/Postbox/Sources/PostboxView.swift index effaf9752c..710fb0b21a 100644 --- a/submodules/Postbox/Sources/PostboxView.swift +++ b/submodules/Postbox/Sources/PostboxView.swift @@ -5,6 +5,7 @@ public protocol PostboxView { protocol MutablePostboxView { func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool func immutableView() -> PostboxView } @@ -24,6 +25,16 @@ final class CombinedMutableView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var updated = false + for (_, view) in self.views { + if view.refreshDueToExternalTransaction(postbox: postbox) { + updated = true + } + } + return updated + } func immutableView() -> CombinedView { var result: [PostboxViewKey: PostboxView] = [:] diff --git a/submodules/Postbox/Sources/PreferencesEntry.swift b/submodules/Postbox/Sources/PreferencesEntry.swift index a38f5cae4b..125a6ed5ae 100644 --- a/submodules/Postbox/Sources/PreferencesEntry.swift +++ b/submodules/Postbox/Sources/PreferencesEntry.swift @@ -13,11 +13,37 @@ public final class CodableEntry: Equatable { self.data = encoder.makeData() } + public init(legacyValue: PostboxCoding) { + let encoder = PostboxEncoder() + encoder.encodeRootObject(legacyValue) + self.data = encoder.makeData() + } + public func get(_ type: T.Type) -> T? { let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data)) return decoder.decode(T.self, forKey: "_") } + public func getLegacy(_ type: T.Type) -> T? { + let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data)) + let object = decoder.decodeRootObject() + if let object = object as? T { + return object + } else { + return nil + } + } + + public func getLegacy() -> PostboxCoding? { + let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data)) + let object = decoder.decodeRootObject() + if let object = object { + return object + } else { + return nil + } + } + public static func ==(lhs: CodableEntry, rhs: CodableEntry) -> Bool { return lhs.data == rhs.data } diff --git a/submodules/Postbox/Sources/PreferencesView.swift b/submodules/Postbox/Sources/PreferencesView.swift index 1fa4b3f348..faec5ead7c 100644 --- a/submodules/Postbox/Sources/PreferencesView.swift +++ b/submodules/Postbox/Sources/PreferencesView.swift @@ -44,6 +44,21 @@ final class MutablePreferencesView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var values: [ValueBoxKey: PreferencesEntry] = [:] + for key in self.keys { + if let value = postbox.preferencesTable.get(key: key) { + values[key] = value + } + } + if self.values != values { + self.values = values + return true + } else { + return false + } + } func immutableView() -> PostboxView { return PreferencesView(self) diff --git a/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift b/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift index a2865e2b50..af39f00534 100644 --- a/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift +++ b/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift @@ -26,6 +26,16 @@ final class MutableSynchronizeGroupMessageStatsView: MutablePostboxView { } return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let groupsAndNamespaces = postbox.synchronizeGroupMessageStatsTable.get() + if self.groupsAndNamespaces != groupsAndNamespaces { + self.groupsAndNamespaces = groupsAndNamespaces + return true + } else { + return false + } + } func immutableView() -> PostboxView { return SynchronizeGroupMessageStatsView(self) diff --git a/submodules/Postbox/Sources/TopChatMessageView.swift b/submodules/Postbox/Sources/TopChatMessageView.swift index 9f276873f9..d348c6dfb1 100644 --- a/submodules/Postbox/Sources/TopChatMessageView.swift +++ b/submodules/Postbox/Sources/TopChatMessageView.swift @@ -29,6 +29,41 @@ final class MutableTopChatMessageView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + var messages: [PeerId: Message] = [:] + + for peerId in self.peerIds { + if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex { + messages[peerId] = postbox.getMessage(index.id) + } + } + + var updated = false + + if self.messages.count != messages.count { + updated = true + } else { + for (key, value) in self.messages { + if let other = messages[key] { + if other.stableId != value.stableId || other.stableVersion != value.stableVersion { + updated = true + break + } + } else { + updated = true + break + } + } + } + + if updated { + self.messages = messages + return true + } else { + return false + } + } func immutableView() -> PostboxView { return TopChatMessageView(self) diff --git a/submodules/Postbox/Sources/UnreadMessageCountsView.swift b/submodules/Postbox/Sources/UnreadMessageCountsView.swift index 8fe50bc914..8e2d19a12b 100644 --- a/submodules/Postbox/Sources/UnreadMessageCountsView.swift +++ b/submodules/Postbox/Sources/UnreadMessageCountsView.swift @@ -6,10 +6,42 @@ public enum UnreadMessageCountsItem: Equatable { case peer(PeerId) } -private enum MutableUnreadMessageCountsItemEntry { +private enum MutableUnreadMessageCountsItemEntry: Equatable { case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState) case totalInGroup(PeerGroupId, ChatListTotalUnreadState) case peer(PeerId, CombinedPeerReadState?) + + static func ==(lhs: MutableUnreadMessageCountsItemEntry, rhs: MutableUnreadMessageCountsItemEntry) -> Bool { + switch lhs { + case let .total(lhsKeyAndEntry, lhsUnreadState): + if case let .total(rhsKeyAndEntry, rhsUnreadState) = rhs { + if lhsKeyAndEntry?.0 != rhsKeyAndEntry?.0 { + return false + } + if lhsKeyAndEntry?.1 != rhsKeyAndEntry?.1 { + return false + } + if lhsUnreadState != rhsUnreadState { + return false + } + return true + } else { + return false + } + case let .totalInGroup(groupId, state): + if case .totalInGroup(groupId, state) = rhs { + return true + } else { + return false + } + case let .peer(peerId, readState): + if case .peer(peerId, readState) = rhs { + return true + } else { + return false + } + } + } } public enum UnreadMessageCountsItemEntry { @@ -19,9 +51,12 @@ public enum UnreadMessageCountsItemEntry { } final class MutableUnreadMessageCountsView: MutablePostboxView { + private let items: [UnreadMessageCountsItem] fileprivate var entries: [MutableUnreadMessageCountsItemEntry] init(postbox: PostboxImpl, items: [UnreadMessageCountsItem]) { + self.items = items + self.entries = items.map { item in switch item { case let .total(preferencesKey): @@ -80,6 +115,25 @@ final class MutableUnreadMessageCountsView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let entries: [MutableUnreadMessageCountsItemEntry] = self.items.map { item -> MutableUnreadMessageCountsItemEntry in + switch item { + case let .total(preferencesKey): + return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root)) + case let .totalInGroup(groupId): + return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId)) + case let .peer(peerId): + return .peer(peerId, postbox.readStateTable.getCombinedState(peerId)) + } + } + if self.entries != entries { + self.entries = entries + return true + } else { + return false + } + } func immutableView() -> PostboxView { return UnreadMessageCountsView(self) @@ -151,6 +205,16 @@ final class MutableCombinedReadStateView: MutablePostboxView { return updated } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + let state = postbox.readStateTable.getCombinedState(self.peerId) + if state != self.state { + self.state = state + return true + } else { + return false + } + } func immutableView() -> PostboxView { return CombinedReadStateView(self) From 146b458f8a7ab07878ea3d157340bd3dc58015cf Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:10:38 +0400 Subject: [PATCH 04/20] Prepare for chat background optimization --- .../BubbleSettingsController.swift | 2 +- .../ForwardPrivacyChatPreviewItem.swift | 2 +- .../TextSizeSelectionController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 4 +- .../Themes/ThemePreviewControllerNode.swift | 4 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 6 +- .../Sources/ShareController.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 2 +- .../Sources/ChatControllerNode.swift | 4 +- .../Sources/ChatMessageActionItemNode.swift | 4 +- .../ChatMessageAnimatedStickerItemNode.swift | 4 +- .../Sources/ChatMessageBubbleBackdrop.swift | 10 +- .../Sources/ChatMessageStickerItemNode.swift | 4 +- .../Sources/ChatOverscrollControl.swift | 4 +- .../ChatRecentActionsControllerNode.swift | 2 +- .../Sources/ChatReplyCountItem.swift | 4 +- .../Sources/WallpaperBackgroundNode.swift | 234 +++++++++++++++--- 18 files changed, 232 insertions(+), 64 deletions(-) diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 11f8e2384c..3c0b2ef2c5 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel self.scrollNode = ASScrollNode() - self.chatBackgroundNode = WallpaperBackgroundNode(context: context) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) self.chatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index f9acade286..6db765af58 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { - currentBackgroundNode = WallpaperBackgroundNode(context: item.context) + currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) } currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index a60c77180c..ea3fb677f8 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4)) self.chatListBackgroundNode = ASDisplayNode() - self.chatBackgroundNode = WallpaperBackgroundNode(context: context) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) self.chatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 85ef7f6e27..08afc2236b 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.backgroundContainerNode = ASDisplayNode() self.backgroundContainerNode.clipsToBounds = true self.backgroundWrapperNode = ASDisplayNode() - self.backgroundNode = WallpaperBackgroundNode(context: context) + self.backgroundNode = createWallpaperBackgroundNode(context: context) self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode.clipsToBounds = true @@ -1354,7 +1354,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate @objc private func playPressed() { if self.state.backgroundColors.count >= 3 || self.state.messagesColors.count >= 3 { - self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring)) + self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false) } else { self.updateState({ state in var state = state diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 81426ab556..7cb2af7755 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context) + self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) self.instantChatBackgroundNode.displaysAsynchronously = false self.ready.set(.single(true)) @@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.blurredNode = BlurredImageNode() self.blurredNode.blurView.contentMode = .scaleAspectFill - self.wallpaperNode = WallpaperBackgroundNode(context: context) + self.wallpaperNode = createWallpaperBackgroundNode(context: context) self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 464280933b..064fa3e245 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { - currentBackgroundNode = WallpaperBackgroundNode(context: item.context) + currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) } currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 55a93b4304..8d5a1921dc 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.wrapperNode = ASDisplayNode() self.imageNode = TransformImageNode() self.imageNode.contentAnimations = .subsequentUpdates - self.nativeNode = WallpaperBackgroundNode(context: context) + self.nativeNode = createWallpaperBackgroundNode(context: context) self.cropNode = WallpaperCropNode() self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6)) self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter) @@ -812,7 +812,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { switch wallpaper { case let .gradient(gradient): if gradient.colors.count >= 3 { - self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring)) + self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false) } else { let rotation = gradient.settings.rotation ?? 0 self.requestRotateGradient?((rotation + 90) % 360) @@ -820,7 +820,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { case let .file(file): if file.isPattern { if file.settings.colors.count >= 3 { - self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring)) + self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false) } else { let rotation = file.settings.rotation ?? 0 self.requestRotateGradient?((rotation + 90) % 360) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 5af7a8504e..424eb02b38 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -964,7 +964,7 @@ final class MessageStoryRenderer { self.containerNode = ASDisplayNode() - self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context) + self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) self.instantChatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 72986c4a17..208e5d0d30 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G default: break } - self.chatBackgroundNode = WallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase) self.wallpaperReady.set(self.chatBackgroundNode.isReady) var locationBroadcastPanelSource: LocationBroadcastPanelSource diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 492a7e02f4..d71507c2f3 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -370,7 +370,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion { return } - strongSelf.backgroundNode.animateEvent(transition: transition) + strongSelf.backgroundNode.animateEvent(transition: transition, extendAnimation: false) } getMessageTransitionNode = { [weak self] in @@ -1648,7 +1648,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion { return } - self.backgroundNode.animateEvent(transition: transition) + self.backgroundNode.animateEvent(transition: transition, extendAnimation: false) } //self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil) } diff --git a/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift index 0559c304d1..2bcb2e1bf1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift @@ -24,7 +24,7 @@ private func attributedServiceMessageString(theme: ChatPresentationThemeData, st class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { let labelNode: TextNode - var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode? + var backgroundNode: WallpaperBubbleBackgroundNode? var backgroundColorNode: ASDisplayNode let backgroundMaskNode: ASImageNode var linkHighlightingNode: LinkHighlightingNode? @@ -327,7 +327,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { var backgroundFrame = backgroundNode.frame backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundNode.update(rect: backgroundFrame, within: containerSize) + backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index e12c5e7da6..58a6272838 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -165,7 +165,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private let containerNode: ContextControllerSourceNode let imageNode: TransformImageNode private var enableSynchronousImageApply: Bool = false - private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode? + private var backgroundNode: WallpaperBubbleBackgroundNode? private(set) var placeholderNode: StickerShimmerEffectNode private(set) var animationNode: GenericAnimatedStickerNode? private var animationSize: CGSize? @@ -650,7 +650,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize) if let backgroundNode = self.backgroundNode { - backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize) + backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift index f6ffd5ad36..a07d186ee2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift @@ -55,7 +55,7 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe } final class ChatMessageBubbleBackdrop: ASDisplayNode { - private var backgroundContent: WallpaperBackgroundNode.BubbleBackgroundNode? + private var backgroundContent: WallpaperBubbleBackgroundNode? private var currentType: ChatMessageBackgroundType? private var currentMaskMode: Bool? @@ -86,7 +86,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { var backgroundFrame = backgroundContent.frame backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundContent.update(rect: backgroundFrame, within: containerSize) + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } } } @@ -142,7 +142,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { var backgroundFrame = backgroundContent.frame backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundContent.update(rect: backgroundFrame, within: containerSize) + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } } @@ -162,7 +162,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { var backgroundFrame = backgroundContent.frame backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundContent.update(rect: backgroundFrame, within: containerSize) + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } self.backgroundContent = backgroundContent self.insertSubnode(backgroundContent, at: 0) @@ -174,7 +174,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { var backgroundFrame = backgroundContent.frame backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundContent.update(rect: backgroundFrame, within: containerSize) + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } self.backgroundContent = backgroundContent self.insertSubnode(backgroundContent, at: 0) diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 0cd36aa674..df013a2e16 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -22,7 +22,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let contextSourceNode: ContextExtractedContentContainingNode private let containerNode: ContextControllerSourceNode let imageNode: TransformImageNode - private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode? + private var backgroundNode: WallpaperBubbleBackgroundNode? private var placeholderNode: StickerShimmerEffectNode var textNode: TextNode? @@ -250,7 +250,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize) if let backgroundNode = self.backgroundNode { - backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize) + backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate) } } } diff --git a/submodules/TelegramUI/Sources/ChatOverscrollControl.swift b/submodules/TelegramUI/Sources/ChatOverscrollControl.swift index 9f8ed51efd..42f36a207b 100644 --- a/submodules/TelegramUI/Sources/ChatOverscrollControl.swift +++ b/submodules/TelegramUI/Sources/ChatOverscrollControl.swift @@ -353,7 +353,7 @@ final class BadgeComponent: CombinedComponent { if lhs.withinSize != rhs.withinSize { return false } - if lhs.wallpaperNode != rhs.wallpaperNode { + if lhs.wallpaperNode !== rhs.wallpaperNode { return false } return true @@ -564,7 +564,7 @@ final class AvatarComponent: Component { } private final class WallpaperBlurNode: ASDisplayNode { - private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode? + private var backgroundNode: WallpaperBubbleBackgroundNode? private let colorNode: ASDisplayNode override init() { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 2b4d58df4b..7268d74939 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 } - self.backgroundNode = WallpaperBackgroundNode(context: context) + self.backgroundNode = createWallpaperBackgroundNode(context: context) self.backgroundNode.isUserInteractionEnabled = false self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor) diff --git a/submodules/TelegramUI/Sources/ChatReplyCountItem.swift b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift index 36aee95303..90eba140c6 100644 --- a/submodules/TelegramUI/Sources/ChatReplyCountItem.swift +++ b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift @@ -64,7 +64,7 @@ class ChatReplyCountItem: ListViewItem { class ChatReplyCountItemNode: ListViewItemNode { var item: ChatReplyCountItem? private let labelNode: TextNode - private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode? + private var backgroundNode: WallpaperBubbleBackgroundNode? private let backgroundColorNode: ASDisplayNode private var theme: ChatPresentationThemeData? @@ -201,7 +201,7 @@ class ChatReplyCountItemNode: ListViewItemNode { backgroundFrame.origin.x += rect.minX backgroundFrame.origin.y += rect.minY - backgroundNode.update(rect: backgroundFrame, within: containerSize) + backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate) } } diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index c150c744e9..47fc43592d 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -27,25 +27,47 @@ private func generateBlurredContents(image: UIImage) -> UIImage? { return context.generateImage() } -public final class WallpaperBackgroundNode: ASDisplayNode { - public final class BubbleBackgroundNode: ASDisplayNode { - public enum BubbleType { - case incoming - case outgoing - case free - } +public enum WallpaperBubbleType { + case incoming + case outgoing + case free +} - private let bubbleType: BubbleType +public protocol WallpaperBubbleBackgroundNode: ASDisplayNode { + var frame: CGRect { get set } + + func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) + func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) + func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) + func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) +} + +public protocol WallpaperBackgroundNode: ASDisplayNode { + var isReady: Signal { get } + var rotation: CGFloat { get set } + + func update(wallpaper: TelegramWallpaper) + func _internalUpdateIsSettingUpWallpaper() + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) + func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) + func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) + func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool + func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? +} + +final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode { + final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode { + private let bubbleType: WallpaperBubbleType private let contentNode: ASImageNode private var cleanWallpaperNode: ASDisplayNode? private var gradientWallpaperNode: GradientBackgroundNode.CloneNode? - private weak var backgroundNode: WallpaperBackgroundNode? - private var index: SparseBag.Index? + private weak var backgroundNode: WallpaperBackgroundNodeImpl? + private var index: SparseBag.Index? private var currentLayout: (rect: CGRect, containerSize: CGSize)? - public override var frame: CGRect { + override var frame: CGRect { didSet { if oldValue.size != self.bounds.size { self.contentNode.frame = self.bounds @@ -59,7 +81,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - init(backgroundNode: WallpaperBackgroundNode, bubbleType: BubbleType) { + init(backgroundNode: WallpaperBackgroundNodeImpl, bubbleType: WallpaperBubbleType) { self.backgroundNode = backgroundNode self.bubbleType = bubbleType @@ -210,7 +232,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) { + func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) { self.currentLayout = (rect, containerSize) let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height)) @@ -233,7 +255,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) { + func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) { self.currentLayout = (rect, containerSize) let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height)) @@ -250,7 +272,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { guard let (_, containerSize) = self.currentLayout else { return } @@ -267,7 +289,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { + func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { guard let (_, containerSize) = self.currentLayout else { return } @@ -285,9 +307,9 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } private final class BubbleBackgroundNodeReference { - weak var node: BubbleBackgroundNode? + weak var node: BubbleBackgroundNodeImpl? - init(node: BubbleBackgroundNode) { + init(node: BubbleBackgroundNodeImpl) { self.node = node } } @@ -303,6 +325,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { private let patternImageNode: ASImageNode private var isGeneratingPatternImage: Bool = false + private let bakedBackgroundView: UIImageView + private var validLayout: CGSize? private var wallpaper: TelegramWallpaper? private var isSettingUpWallpaper: Bool = false @@ -367,7 +391,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public var rotation: CGFloat = 0.0 { + var rotation: CGFloat = 0.0 { didSet { var fromValue: CGFloat = 0.0 if let value = (self.layer.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue { @@ -400,11 +424,11 @@ public final class WallpaperBackgroundNode: ASDisplayNode { private static var cachedSharedPattern: (PatternKey, UIImage)? private let _isReady = ValuePromise(false, ignoreRepeated: true) - public var isReady: Signal { + var isReady: Signal { return self._isReady.get() } - public init(context: AccountContext, useSharedAnimationPhase: Bool = false) { + init(context: AccountContext, useSharedAnimationPhase: Bool) { self.context = context self.useSharedAnimationPhase = useSharedAnimationPhase self.imageContentMode = .scaleAspectFill @@ -413,6 +437,9 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.contentNode.contentMode = self.imageContentMode self.patternImageNode = ASImageNode() + + self.bakedBackgroundView = UIImageView() + self.bakedBackgroundView.isHidden = true super.init() @@ -420,6 +447,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.contentNode.frame = self.bounds self.addSubnode(self.contentNode) self.addSubnode(self.patternImageNode) + + //self.view.addSubview(self.bakedBackgroundView) } deinit { @@ -428,7 +457,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.imageDisposable.dispose() } - public func update(wallpaper: TelegramWallpaper) { + func update(wallpaper: TelegramWallpaper) { if self.wallpaper == wallpaper { return } @@ -539,7 +568,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func _internalUpdateIsSettingUpWallpaper() { + func _internalUpdateIsSettingUpWallpaper() { self.isSettingUpWallpaper = true } @@ -622,7 +651,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.validPatternGeneratedImage = nil self.validPatternImage = nil - if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper { + if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper { self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, generate: cachedValidPatternImage.generate) } else { func reference(for resource: EngineMediaResource, media: EngineMedia) -> MediaResourceReference { @@ -688,7 +717,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { if self.validPatternGeneratedImage != updatedGeneratedImage { self.validPatternGeneratedImage = updatedGeneratedImage - if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage { + if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage { self.patternImageNode.image = cachedValidPatternImage.image self.updatePatternPresentation() } else { @@ -700,7 +729,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.updatePatternPresentation() if self.useSharedAnimationPhase { - WallpaperBackgroundNode.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) + WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) } } else { self.updatePatternPresentation() @@ -721,7 +750,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { strongSelf.updatePatternPresentation() if let image = image, strongSelf.useSharedAnimationPhase { - WallpaperBackgroundNode.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) + WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) } } } @@ -743,7 +772,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size)) } - public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.validLayout == nil self.validLayout = size @@ -767,12 +796,25 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) { + func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) } - public func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { + private func updateBakedBackground() { + guard let size = self.validLayout else { + return + } + let context = DrawingContext(size: size, scale: UIScreenScale, opaque: true) + + context.withContext { context in + context.clear(CGRect(origin: CGPoint(), size: size)) + } + + self.bakedBackgroundView.image = context.generateImage() + } + + func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners { self.bubbleTheme = bubbleTheme self.bubbleCorners = bubbleCorners @@ -801,7 +843,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } - public func hasBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> Bool { + func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool { guard let bubbleTheme = self.bubbleTheme, let bubbleCorners = self.bubbleCorners else { return false } @@ -846,12 +888,138 @@ public final class WallpaperBackgroundNode: ASDisplayNode { return false } - public func makeBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> WallpaperBackgroundNode.BubbleBackgroundNode? { + func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? { if !self.hasBubbleBackground(for: type) { return nil } - let node = WallpaperBackgroundNode.BubbleBackgroundNode(backgroundNode: self, bubbleType: type) + let node = WallpaperBackgroundNodeImpl.BubbleBackgroundNodeImpl(backgroundNode: self, bubbleType: type) node.updateContents() return node } } + +final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode { + final class SharedStorage { + } + + private class WallpaperComponentView: UIView { + let updated: () -> Void + + init(updated: @escaping () -> Void) { + self.updated = updated + + super.init(frame: CGRect()) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + + private final class WallpaperGradiendComponentView: WallpaperComponentView { + struct Spec { + var colors: [UInt32] + } + } + + private final class WallpaperPatternComponentView: WallpaperComponentView { + struct Spec { + } + } + + private let context: AccountContext + private let storage: SharedStorage + + private let staticView: UIImageView + private var gradient: WallpaperGradiendComponentView? + private var pattern: WallpaperPatternComponentView? + + private let _isReady = ValuePromise(false, ignoreRepeated: true) + var isReady: Signal { + return self._isReady.get() + } + + var rotation: CGFloat = 0.0 { + didSet { + } + } + + init(context: AccountContext, storage: SharedStorage?) { + self.context = context + self.storage = storage ?? SharedStorage() + + self.staticView = UIImageView() + + super.init() + + self.view.addSubview(self.staticView) + } + + func update(wallpaper: TelegramWallpaper) { + var gradientSpec: WallpaperGradiendComponentView.Spec? + + switch wallpaper { + case let .builtin(wallpaperSettings): + let _ = wallpaperSettings + case let .color(color): + let _ = color + case let .gradient(gradient): + if gradient.colors.count >= 3 { + gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors) + } + case let .image(representations, settings): + let _ = representations + let _ = settings + case let .file(file): + if file.settings.colors.count >= 3 { + gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors) + } + } + + if let gradientSpec = gradientSpec { + let gradient: WallpaperGradiendComponentView + if let current = self.gradient { + gradient = current + } else { + gradient = WallpaperGradiendComponentView(updated: { [weak self] in + self?.componentsUpdated() + }) + } + let _ = gradient + let _ = gradientSpec + } else if let gradient = self.gradient { + self.gradient = nil + gradient.removeFromSuperview() + } + } + + private func componentsUpdated() { + } + + func _internalUpdateIsSettingUpWallpaper() { + } + + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + + } + + func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { + + } + + func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { + + } + + func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool { + return false + } + + func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? { + return nil + } +} + +public func createWallpaperBackgroundNode(context: AccountContext, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode { + return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) +} From 4b7e4ac2c70bb657d9fc0e1bb6816d10b640018d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:11:01 +0400 Subject: [PATCH 05/20] Fix excessive looping in mark all unseen code --- ...nchronizeMarkAllUnseenPersonalMessagesOperations.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift index bdbf6a3984..ccee06a92a 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift @@ -181,8 +181,12 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox ) |> mapToSignal { resultId -> Signal in if let resultId = resultId { - let _ = currentMaxId.swap(resultId) - return .complete() + let previous = currentMaxId.swap(resultId) + if previous == resultId { + return .fail(.done) + } else { + return .complete() + } } else { return .fail(.done) } From 4d862270f7803e40bb602e5b09009590276d3175 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:11:24 +0400 Subject: [PATCH 06/20] Refresh notification badge when entering foreground --- .../TelegramUI/Sources/AppDelegate.swift | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index e90da4c911..e2ff733bca 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -1174,17 +1174,7 @@ private func extractAccountManagerState(records: AccountRecordsView mapToSignal { context -> Signal in - if let context = context { - return context.applicationBadge - } else { - return .single(0) - } - } - |> deliverOnMainQueue).start(next: { count in - UIApplication.shared.applicationIconBadgeNumber = Int(count) - })) + self.resetBadge() if #available(iOS 9.1, *) { self.quickActionsDisposable.set((self.context.get() @@ -1281,6 +1271,20 @@ private func extractAccountManagerState(records: AccountRecordsView mapToSignal { context -> Signal in + if let context = context { + return context.applicationBadge + } else { + return .single(0) + } + } + |> deliverOnMainQueue).start(next: { count in + UIApplication.shared.applicationIconBadgeNumber = Int(count) + })) + } + func applicationWillResignActive(_ application: UIApplication) { self.isActiveValue = false self.isActivePromise.set(false) @@ -1361,6 +1365,8 @@ private func extractAccountManagerState(records: AccountRecordsView Date: Thu, 4 Nov 2021 21:11:39 +0400 Subject: [PATCH 07/20] NotificationService: add more logs --- .../Sources/NotificationService.swift | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 2837bcbd8d..7b335f548b 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -424,7 +424,7 @@ private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) - } @available(iOSApplicationExtension 10.0, iOS 10.0, *) -private struct NotificationContent { +private struct NotificationContent: CustomStringConvertible { var title: String? var subtitle: String? var body: String? @@ -438,6 +438,21 @@ private struct NotificationContent { var senderPerson: INPerson? var senderImage: INImage? + var description: String { + var string = "{" + string += " title: \(String(describing: self.title))\n" + string += " subtitle: \(String(describing: self.subtitle))\n" + string += " body: \(String(describing: self.body)),\n" + string += " threadId: \(String(describing: self.threadId)),\n" + string += " sound: \(String(describing: self.sound)),\n" + string += " badge: \(String(describing: self.badge)),\n" + string += " category: \(String(describing: self.category)),\n" + string += " userInfo: \(String(describing: self.userInfo)),\n" + string += " senderImage: \(self.senderImage != nil ? "non-empty" : "empty"),\n" + string += "}" + return string + } + mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) { if #available(iOS 15.0, *) { let image = peerAvatar(mediaBox: mediaBox, accountPeerId: accountPeerId, peer: peer) @@ -548,6 +563,8 @@ private final class NotificationServiceHandler { init?(queue: Queue, updateCurrentContent: @escaping (NotificationContent) -> Void, completed: @escaping () -> Void, payload: [AnyHashable: Any]) { self.queue = queue + let episode = String(UInt32.random(in: 0 ..< UInt32.max), radix: 16) + guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { return nil } @@ -586,7 +603,10 @@ private final class NotificationServiceHandler { let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil) + Logger.shared.log("NotificationService \(episode)", "Begin processing payload \(payload)") + guard var encryptedPayload = payload["p"] as? String else { + Logger.shared.log("NotificationService \(episode)", "Invalid payload 1") return nil } encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+") @@ -595,6 +615,7 @@ private final class NotificationServiceHandler { encryptedPayload.append("=") } guard let payloadData = Data(base64Encoded: encryptedPayload) else { + Logger.shared.log("NotificationService \(episode)", "Invalid payload 2") return nil } @@ -621,6 +642,8 @@ private final class NotificationServiceHandler { } guard let strongSelf = self, let recordId = recordId else { + Logger.shared.log("NotificationService \(episode)", "Couldn't find a matching decryption key") + let content = NotificationContent() updateCurrentContent(content) completed() @@ -641,6 +664,8 @@ private final class NotificationServiceHandler { return } guard let stateManager = stateManager else { + Logger.shared.log("NotificationService \(episode)", "Didn't receive stateManager") + let content = NotificationContent() updateCurrentContent(content) completed() @@ -658,6 +683,8 @@ private final class NotificationServiceHandler { return } guard let notificationsKey = notificationsKey else { + Logger.shared.log("NotificationService \(episode)", "Didn't receive decryption key") + let content = NotificationContent() updateCurrentContent(content) completed() @@ -665,6 +692,8 @@ private final class NotificationServiceHandler { return } guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else { + Logger.shared.log("NotificationService \(episode)", "Couldn't decrypt payload") + let content = NotificationContent() updateCurrentContent(content) completed() @@ -672,6 +701,8 @@ private final class NotificationServiceHandler { return } guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else { + Logger.shared.log("NotificationService \(episode)", "Couldn't process payload as JSON") + let content = NotificationContent() updateCurrentContent(content) completed() @@ -679,6 +710,8 @@ private final class NotificationServiceHandler { return } + Logger.shared.log("NotificationService \(episode)", "Decrypted payload: \(payloadJson)") + var peerId: PeerId? var messageId: MessageId.Id? var mediaAttachment: Media? @@ -842,8 +875,10 @@ private final class NotificationServiceHandler { if let action = action { switch action { case .logout: + Logger.shared.log("NotificationService \(episode)", "Will logout") completed() case let .poll(peerId, initialContent): + Logger.shared.log("NotificationService \(episode)", "Will poll") if let stateManager = strongSelf.stateManager { let pollCompletion: (NotificationContent) -> Void = { content in var content = content @@ -912,6 +947,7 @@ private final class NotificationServiceHandler { } } + Logger.shared.log("NotificationService \(episode)", "Will fetch media") let _ = (fetchMediaSignal |> timeout(10.0, queue: queue, alternate: .single(nil)) |> deliverOn(queue)).start(next: { mediaData in @@ -920,6 +956,9 @@ private final class NotificationServiceHandler { return } + Logger.shared.log("NotificationService \(episode)", "Did fetch media \(mediaData == nil ? "Non-empty" : "Empty")") + + Logger.shared.log("NotificationService \(episode)", "Will get unread count") let _ = (getCurrentRenderedTotalUnreadCount( accountManager: strongSelf.accountManager, postbox: stateManager.postbox @@ -934,6 +973,8 @@ private final class NotificationServiceHandler { content.badge = Int(value.0) } + Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)") + if let image = mediaAttachment as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource { if let mediaData = mediaData { stateManager.postbox.mediaBox.storeResourceData(resource.id, data: mediaData, synchronous: true) @@ -988,6 +1029,8 @@ private final class NotificationServiceHandler { } } + Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)") + updateCurrentContent(content) completed() @@ -1000,6 +1043,8 @@ private final class NotificationServiceHandler { stateManager.network.shouldKeepConnection.set(.single(true)) if peerId.namespace == Namespaces.Peer.CloudChannel { + Logger.shared.log("NotificationService \(episode)", "Will poll channel \(peerId)") + pollSignal = standalonePollChannelOnce( postbox: stateManager.postbox, network: stateManager.network, @@ -1007,6 +1052,7 @@ private final class NotificationServiceHandler { stateManager: stateManager ) } else { + Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference") enum ControlError { case restart } @@ -1054,6 +1100,7 @@ private final class NotificationServiceHandler { completed() } case let .deleteMessage(ids): + Logger.shared.log("NotificationService \(episode)", "Will delete messages \(ids)") let mediaBox = stateManager.postbox.mediaBox let _ = (stateManager.postbox.transaction { transaction -> Void in _internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, deleteMedia: true) @@ -1084,6 +1131,8 @@ private final class NotificationServiceHandler { if isCurrentAccount { content.badge = Int(value.0) } + Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)") + Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)") updateCurrentContent(content) @@ -1092,6 +1141,7 @@ private final class NotificationServiceHandler { } if !removeIdentifiers.isEmpty { + Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications") UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers) queue.after(1.0, { completeRemoval() @@ -1102,6 +1152,7 @@ private final class NotificationServiceHandler { }) }) case let .readMessage(id): + Logger.shared.log("NotificationService \(episode)", "Will read message \(id)") let _ = (stateManager.postbox.transaction { transaction -> Void in transaction.applyIncomingReadMaxId(id) } @@ -1130,6 +1181,9 @@ private final class NotificationServiceHandler { content.badge = Int(value.0) } + Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)") + Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)") + updateCurrentContent(content) completed() @@ -1137,6 +1191,7 @@ private final class NotificationServiceHandler { } if !removeIdentifiers.isEmpty { + Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications") UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers) queue.after(1.0, { completeRemoval() From d75257df430e37b0e68d61ab51176db162dc3b71 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 21:13:13 +0400 Subject: [PATCH 08/20] Limit PiP for native videos --- .../Sources/Items/UniversalVideoGalleryItem.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index c41a279029..2e12e5845f 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -2064,6 +2064,18 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } @objc func pictureInPictureButtonPressed() { + var isNativePictureInPictureSupported = false + switch self.item?.contentInfo { + case let .message(message): + for media in message.media { + if let media = media as? TelegramMediaFile, media.isVideo { + isNativePictureInPictureSupported = true + } + } + default: + break + } + if let item = self.item, let videoNode = self.videoNode, let overlayController = self.context.sharedContext.mediaManager.overlayMediaManager.controller { videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false) @@ -2071,7 +2083,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { let baseNavigationController = self.baseNavigationController() let playbackRate = self.playbackRate - if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported() { + if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported(), isNativePictureInPictureSupported { self.disablePictureInPicturePlaceholder = true let overlayVideoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: self.context.sharedContext.mediaManager.audioSession, manager: self.context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .overlay) From 2f6d84fabf389a7b50f07842e4c72117063e2db1 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 4 Nov 2021 21:43:51 +0400 Subject: [PATCH 09/20] Various Fixes --- .../Sources/ItemListPeerActionItem.swift | 7 +- .../Sources/ChannelAdminsController.swift | 2 +- .../Sources/ChannelMembersController.swift | 14 ++- .../RecentActionsIcon.imageset/Contents.json | 12 ++ .../RecentActionsIcon.imageset/Icon-38.pdf | 113 ++++++++++++++++++ 5 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index b3ce620fd0..c3856edc08 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -255,15 +255,14 @@ class ItemListPeerActionItemNode: ListViewItemNode { case .sameSection(false): bottomStripeInset = leftInset + editingOffset bottomStripeOffset = -separatorHeight + strongSelf.bottomStripeNode.isHidden = !item.hasSeparator default: bottomStripeInset = 0.0 bottomStripeOffset = 0.0 hasBottomCorners = true - strongSelf.bottomStripeNode.isHidden = hasCorners + strongSelf.bottomStripeNode.isHidden = hasCorners || !item.hasSeparator } - - strongSelf.bottomStripeNode.isHidden = strongSelf.bottomStripeNode.isHidden || !item.hasSeparator - + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index c700fe1955..8fa6c3f0b1 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -178,7 +178,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { let arguments = arguments as! ChannelAdminsControllerArguments switch self { case let .recentActions(_, text): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: { + return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), title: text, label: "", sectionId: self.section, style: .blocks, action: { arguments.openRecentActions() }) case let .adminsHeader(_, title): diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift index 0984ab1e88..4ad1acfcd9 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift @@ -12,6 +12,7 @@ import AccountContext import AlertUI import PresentationDataUtils import ItemListPeerItem +import ItemListPeerActionItem import InviteLinksUI private final class ChannelMembersControllerArguments { @@ -21,8 +22,9 @@ private final class ChannelMembersControllerArguments { let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void let removePeer: (PeerId) -> Void let openPeer: (Peer) -> Void - let inviteViaLink: ()->Void - init(context: AccountContext, addMember: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (Peer) -> Void, inviteViaLink: @escaping()->Void) { + let inviteViaLink: () -> Void + + init(context: AccountContext, addMember: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (Peer) -> Void, inviteViaLink: @escaping () -> Void) { self.context = context self.addMember = addMember self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions @@ -155,12 +157,12 @@ private enum ChannelMembersEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! ChannelMembersControllerArguments switch self { - case let .addMember(_, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + case let .addMember(theme, text): + return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: { arguments.addMember() }) - case let .inviteLink(_, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + case let .inviteLink(theme, text): + return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: { arguments.inviteViaLink() }) case let .addMemberInfo(_, text): diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json new file mode 100644 index 0000000000..9b4d786a5e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon-38.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf new file mode 100644 index 0000000000..df0a75d54e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf @@ -0,0 +1,113 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 0.584314 0.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.267334 8.000000 cm +1.000000 1.000000 1.000000 scn +10.732674 14.000000 m +5.052428 14.000000 1.569850 9.951639 0.290127 8.129647 c +-0.083405 7.597835 -0.096524 6.911736 0.252348 6.363431 c +1.487609 4.422029 4.937309 0.000000 10.732674 0.000000 c +16.528038 0.000000 19.977737 4.422029 21.212999 6.363431 c +21.561871 6.911736 21.548754 7.597834 21.175220 8.129646 c +19.895500 9.951638 16.412922 14.000000 10.732674 14.000000 c +h +15.732676 7.000000 m +15.732676 4.238577 13.494099 2.000000 10.732676 2.000000 c +7.971252 2.000000 5.732676 4.238577 5.732676 7.000000 c +5.732676 9.761423 7.971252 12.000000 10.732676 12.000000 c +13.494099 12.000000 15.732676 9.761423 15.732676 7.000000 c +h +10.732676 4.000000 m +12.389530 4.000000 13.732676 5.343145 13.732676 7.000000 c +13.732676 8.656855 12.389530 10.000000 10.732676 10.000000 c +10.644694 10.000000 10.557597 9.996212 10.471539 9.988792 c +10.637726 9.697166 10.732672 9.359671 10.732672 9.000002 c +10.732672 7.895432 9.837242 7.000001 8.732672 7.000001 c +8.373003 7.000001 8.035508 7.094942 7.743883 7.261129 c +7.736463 7.175073 7.732676 7.087979 7.732676 7.000000 c +7.732676 5.343145 9.075821 4.000000 10.732676 4.000000 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2162 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002252 00000 n +0000002275 00000 n +0000002448 00000 n +0000002522 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2581 +%%EOF \ No newline at end of file From 01c1b29abb5b120b867148ca81e300253f392929 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 4 Nov 2021 22:58:11 +0400 Subject: [PATCH 10/20] Don't display avatars in notifications if display names on lock screen is off --- .../Sources/NotificationService.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 7b335f548b..ebb1c2e791 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -619,9 +619,12 @@ private final class NotificationServiceHandler { return nil } - let _ = (self.accountManager.accountRecords() + let _ = (combineLatest(queue: self.queue, + self.accountManager.accountRecords(), + self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings]) + ) |> take(1) - |> deliverOn(self.queue)).start(next: { [weak self] records in + |> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in var recordId: AccountRecordId? var isCurrentAccount: Bool = false @@ -641,6 +644,8 @@ private final class NotificationServiceHandler { } } + let inAppNotificationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings + guard let strongSelf = self, let recordId = recordId else { Logger.shared.log("NotificationService \(episode)", "Couldn't find a matching decryption key") @@ -1075,7 +1080,7 @@ private final class NotificationServiceHandler { pollWithUpdatedContent = stateManager.postbox.transaction { transaction -> NotificationContent in var content = initialContent - if let peer = transaction.getPeer(interactionAuthorId) { + if inAppNotificationSettings.displayNameOnLockscreen, let peer = transaction.getPeer(interactionAuthorId) { content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer) } From 5befa9e1c8a890ce9c2646add0bf0c875c494186 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 4 Nov 2021 23:44:42 +0400 Subject: [PATCH 11/20] Fix initial presentation data for preset themes --- .../Sources/PresentationData.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index d008b0e1eb..27550e256a 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -285,18 +285,29 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager Date: Fri, 5 Nov 2021 01:08:12 +0400 Subject: [PATCH 12/20] Disallow ad selection --- submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 11e6b6bec4..44d85a960f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -3317,6 +3317,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } } + if message.adAttribute != nil { + canHaveSelection = false + } default: break } From 62b259b4d30e7726b11f107ff5570b58c95773a0 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 5 Nov 2021 02:14:43 +0400 Subject: [PATCH 13/20] Various Fixes --- .../PublicHeaders/LegacyComponents/TGCameraCapturedVideo.h | 2 +- submodules/LegacyComponents/Sources/TGCameraCapturedVideo.m | 5 +++++ .../Sources/TGMediaPickerGalleryVideoItemView.m | 6 +++++- submodules/LegacyComponents/Sources/TGMediaVideoConverter.m | 3 +++ .../TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift | 4 ++-- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraCapturedVideo.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraCapturedVideo.h index a9031abd86..724895304a 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraCapturedVideo.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraCapturedVideo.h @@ -13,7 +13,7 @@ @property (nonatomic, readonly) bool isAnimation; @property (nonatomic, readonly) TGMediaAsset *originalAsset; @property (nonatomic, readonly) CGSize dimensions; - +@property (nonatomic, readonly) NSString *uniformTypeIdentifier; - (instancetype)initWithURL:(NSURL *)url; - (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto; diff --git a/submodules/LegacyComponents/Sources/TGCameraCapturedVideo.m b/submodules/LegacyComponents/Sources/TGCameraCapturedVideo.m index f33b10a365..c8a9f6ee35 100644 --- a/submodules/LegacyComponents/Sources/TGCameraCapturedVideo.m +++ b/submodules/LegacyComponents/Sources/TGCameraCapturedVideo.m @@ -88,6 +88,11 @@ return _cachedAVAsset; } +- (NSString *)uniformTypeIdentifier +{ + return nil; +} + - (SSignal *)avAsset { if (_originalAsset != nil) { if (_cachedAVAsset != nil) { diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m index 24c07a0634..12ee190c90 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m @@ -1107,7 +1107,11 @@ } else if (self.item.avAsset != nil) { itemSignal = [self.item.avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { - return [SSignal single:[AVPlayerItem playerItemWithAsset:avAsset]]; + if ([avAsset isKindOfClass:[AVAsset class]]) { + return [SSignal single:[AVPlayerItem playerItemWithAsset:avAsset]]; + } else { + return [SSignal never]; + } }]; } diff --git a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m index f0afb18ed1..b8ec8db47a 100644 --- a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m +++ b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m @@ -108,6 +108,9 @@ + (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id)entityRenderer { + if ([avAsset isKindOfClass:[NSURL class]]) { + avAsset = [[AVURLAsset alloc] initWithURL:(NSURL *)avAsset options:nil]; + } SQueue *queue = [[SQueue alloc] init]; return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index ee6328cdb9..013a48fca5 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -74,10 +74,10 @@ public struct Namespaces { public static let cachedPollResults: Int8 = 9 public static let cachedContextResults: Int8 = 10 public static let proximityNotificationStoredState: Int8 = 11 - public static let cachedPeerInvitationImporters: Int8 = 12 - public static let cachedPeerExportedInvitations: Int8 = 13 public static let cachedGroupCallDisplayAsPeers: Int8 = 14 public static let cachedAdMessageStates: Int8 = 15 + public static let cachedPeerInvitationImporters: Int8 = 16 + public static let cachedPeerExportedInvitations: Int8 = 17 } public struct UnorderedItemList { From 556572dd6d9f704c2569350560e304da0f184d2b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 14:04:04 +0400 Subject: [PATCH 14/20] Check for invalid data --- .../AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift index 70658638c4..695a4a2e86 100644 --- a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift @@ -26,7 +26,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { guard let baseAddress = bytes.baseAddress else { return } - if bytesPerRow * height > bytes.count { + if bytesPerRow <= 0 || height <= 0 || width <= 0 || bytesPerRow * height > bytes.count { assert(false) return } From aaeea229a4fc0dc90826e2ae28bea1bcb858b479 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 14:05:55 +0400 Subject: [PATCH 15/20] Fix PeerChatState casting --- submodules/Postbox/Sources/Postbox.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 321edd1ef9..37a84f0a7c 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -272,7 +272,7 @@ public final class Transaction { public func getPeerChatState(_ id: PeerId) -> PeerChatState? { assert(!self.disposed) - return self.postbox?.peerChatStateTable.get(id) as? PeerChatState + return self.postbox?.peerChatStateTable.get(id)?.getLegacy() as? PeerChatState } public func setPeerChatState(_ id: PeerId, state: PeerChatState) { @@ -2715,7 +2715,7 @@ final class PostboxImpl { let messages = self.getMessageGroup(at: id) additionalDataEntries.append(.message(id, messages ?? [])) case let .peerChatState(peerId): - additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId) as? PeerChatState)) + additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId)?.getLegacy() as? PeerChatState)) case .totalUnreadState: additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root))) case let .peerNotificationSettings(peerId): From 37148c70d4b6c37f47bcea6dee4825560f7ac026 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 17:36:45 +0400 Subject: [PATCH 16/20] Fix bytesPerRow --- .../AnimatedStickerNode/Sources/AnimatedStickerNode.swift | 5 ++++- .../Sources/SoftwareAnimationRenderer.swift | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index e79a6d12c1..9d058ef231 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -81,6 +81,7 @@ public final class AnimatedStickerFrame { self.type = type self.width = width self.height = height + assert(bytesPerRow > 0) self.bytesPerRow = bytesPerRow self.index = index self.isLastFrame = isLastFrame @@ -687,7 +688,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource self.currentFrame += 1 if draw { if let cache = self.cache, let yuvData = cache.readUncompressedYuvFrame(index: frameIndex) { - return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: 0, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount) + return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.width * 2, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount) } else { var frameData = Data(count: self.bytesPerRow * self.height) frameData.withUnsafeMutableBytes { buffer -> Void in @@ -1134,6 +1135,8 @@ public final class AnimatedStickerNode: ASDisplayNode { guard let strongSelf = self else { return } + + assert(frame.bytesPerRow != 0) strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: { guard let strongSelf = self else { diff --git a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift index 695a4a2e86..9a742f1679 100644 --- a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift @@ -10,6 +10,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { private var highlightedColor: UIColor? func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) { + assert(bytesPerRow > 0) queue.async { [weak self] in switch type { case .argb: From 0679e4dd7e52d9c1c74fe43fda6a85c871fb269c Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 18:26:08 +0400 Subject: [PATCH 17/20] Roll back some refreshDueToExternalTransaction implementations --- submodules/Postbox/Sources/GlobalMessageTagsView.swift | 5 +++-- submodules/Postbox/Sources/HistoryTagInfoView.swift | 5 +++-- .../Sources/InvalidatedMessageHistoryTagSummariesView.swift | 5 +++-- submodules/Postbox/Sources/ItemCollectionIdsView.swift | 5 +++-- .../Postbox/Sources/MessageHistoryTagSummaryView.swift | 5 +++-- submodules/Postbox/Sources/PeerChatStateView.swift | 5 +++-- .../PeerNotificationSettingsBehaviorTimestampView.swift | 5 +++-- .../Postbox/Sources/PeerNotificationSettingsView.swift | 5 +++-- submodules/Postbox/Sources/PeerPresencesView.swift | 5 +++-- submodules/Postbox/Sources/PreferencesView.swift | 5 +++-- .../Postbox/Sources/SynchronizeGroupMessageStatsView.swift | 5 +++-- submodules/Postbox/Sources/TopChatMessageView.swift | 5 +++-- 12 files changed, 36 insertions(+), 24 deletions(-) diff --git a/submodules/Postbox/Sources/GlobalMessageTagsView.swift b/submodules/Postbox/Sources/GlobalMessageTagsView.swift index 6733a52339..c67851ba44 100644 --- a/submodules/Postbox/Sources/GlobalMessageTagsView.swift +++ b/submodules/Postbox/Sources/GlobalMessageTagsView.swift @@ -204,7 +204,7 @@ final class MutableGlobalMessageTagsView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let (entries, lower, upper) = postbox.messageHistoryTable.entriesAround(globalTagMask: globalTag, index: position, count: count) + /*let (entries, lower, upper) = postbox.messageHistoryTable.entriesAround(globalTagMask: globalTag, index: position, count: count) self.entries = entries.map { entry -> InternalGlobalMessageTagsEntry in switch entry { @@ -219,7 +219,8 @@ final class MutableGlobalMessageTagsView: MutablePostboxView { self.render(postbox: postbox) - return true + return true*/ + return false } private func add(_ entry: InternalGlobalMessageTagsEntry) -> Bool { diff --git a/submodules/Postbox/Sources/HistoryTagInfoView.swift b/submodules/Postbox/Sources/HistoryTagInfoView.swift index 7892353518..912b4a86b0 100644 --- a/submodules/Postbox/Sources/HistoryTagInfoView.swift +++ b/submodules/Postbox/Sources/HistoryTagInfoView.swift @@ -63,7 +63,7 @@ final class MutableHistoryTagInfoView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var currentIndex: MessageIndex? + /*var currentIndex: MessageIndex? for namespace in postbox.messageHistoryIndexTable.existingNamespaces(peerId: self.peerId) { if let index = postbox.messageHistoryTagsTable.latestIndex(tag: self.tag, peerId: self.peerId, namespace: namespace) { currentIndex = index @@ -75,7 +75,8 @@ final class MutableHistoryTagInfoView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift index 13224a2502..3ae4264e78 100644 --- a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift +++ b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift @@ -39,7 +39,7 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var entries = Set() + /*var entries = Set() for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) { entries.insert(entry) } @@ -48,7 +48,8 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/ItemCollectionIdsView.swift b/submodules/Postbox/Sources/ItemCollectionIdsView.swift index 232419de61..02950b33b4 100644 --- a/submodules/Postbox/Sources/ItemCollectionIdsView.swift +++ b/submodules/Postbox/Sources/ItemCollectionIdsView.swift @@ -42,7 +42,7 @@ final class MutableItemCollectionIdsView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var idsByNamespace: [ItemCollectionId.Namespace: Set] = [:] + /*var idsByNamespace: [ItemCollectionId.Namespace: Set] = [:] for namespace in namespaces { let ids = postbox.itemCollectionInfoTable.getIds(namespace: namespace) idsByNamespace[namespace] = Set(ids) @@ -52,7 +52,8 @@ final class MutableItemCollectionIdsView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift index 4e62e434cf..8c2d9bd6ab 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift @@ -27,13 +27,14 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace))?.count + /*let count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace))?.count if self.count != count { self.count = count return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/PeerChatStateView.swift b/submodules/Postbox/Sources/PeerChatStateView.swift index df81cba5b3..fbcdd4a4bd 100644 --- a/submodules/Postbox/Sources/PeerChatStateView.swift +++ b/submodules/Postbox/Sources/PeerChatStateView.swift @@ -19,13 +19,14 @@ final class MutablePeerChatStateView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let chatState = postbox.peerChatStateTable.get(self.peerId) + /*let chatState = postbox.peerChatStateTable.get(self.peerId) if self.chatState != chatState { self.chatState = chatState return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift b/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift index a00d2b33b6..606ae7ac2d 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettingsBehaviorTimestampView.swift @@ -21,13 +21,14 @@ final class MutablePeerNotificationSettingsBehaviorTimestampView: MutablePostbox } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let earliestTimestamp = postbox.peerNotificationSettingsBehaviorTable.getEarliest()?.1 + /*let earliestTimestamp = postbox.peerNotificationSettingsBehaviorTable.getEarliest()?.1 if self.earliestTimestamp != earliestTimestamp { self.earliestTimestamp = earliestTimestamp return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/PeerNotificationSettingsView.swift b/submodules/Postbox/Sources/PeerNotificationSettingsView.swift index 2d85dcf466..c73b3eb6da 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettingsView.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettingsView.swift @@ -38,7 +38,7 @@ final class MutablePeerNotificationSettingsView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var notificationSettings: [PeerId: PeerNotificationSettings] = [:] + /*var notificationSettings: [PeerId: PeerNotificationSettings] = [:] for peerId in self.peerIds { var notificationPeerId = peerId if let peer = postbox.peerTable.get(peerId), let associatedPeerId = peer.associatedPeerId { @@ -71,7 +71,8 @@ final class MutablePeerNotificationSettingsView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/PeerPresencesView.swift b/submodules/Postbox/Sources/PeerPresencesView.swift index 51f4a32c03..d31011cf57 100644 --- a/submodules/Postbox/Sources/PeerPresencesView.swift +++ b/submodules/Postbox/Sources/PeerPresencesView.swift @@ -28,7 +28,7 @@ final class MutablePeerPresencesView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var presences: [PeerId: PeerPresence] = [:] + /*var presences: [PeerId: PeerPresence] = [:] for id in self.ids { if let presence = postbox.peerPresenceTable.get(id) { @@ -58,7 +58,8 @@ final class MutablePeerPresencesView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/PreferencesView.swift b/submodules/Postbox/Sources/PreferencesView.swift index faec5ead7c..d739bb97a4 100644 --- a/submodules/Postbox/Sources/PreferencesView.swift +++ b/submodules/Postbox/Sources/PreferencesView.swift @@ -46,7 +46,7 @@ final class MutablePreferencesView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var values: [ValueBoxKey: PreferencesEntry] = [:] + /*var values: [ValueBoxKey: PreferencesEntry] = [:] for key in self.keys { if let value = postbox.preferencesTable.get(key: key) { values[key] = value @@ -57,7 +57,8 @@ final class MutablePreferencesView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift b/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift index af39f00534..088151545f 100644 --- a/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift +++ b/submodules/Postbox/Sources/SynchronizeGroupMessageStatsView.swift @@ -28,13 +28,14 @@ final class MutableSynchronizeGroupMessageStatsView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let groupsAndNamespaces = postbox.synchronizeGroupMessageStatsTable.get() + /*let groupsAndNamespaces = postbox.synchronizeGroupMessageStatsTable.get() if self.groupsAndNamespaces != groupsAndNamespaces { self.groupsAndNamespaces = groupsAndNamespaces return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { diff --git a/submodules/Postbox/Sources/TopChatMessageView.swift b/submodules/Postbox/Sources/TopChatMessageView.swift index d348c6dfb1..b157e01ef7 100644 --- a/submodules/Postbox/Sources/TopChatMessageView.swift +++ b/submodules/Postbox/Sources/TopChatMessageView.swift @@ -31,7 +31,7 @@ final class MutableTopChatMessageView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - var messages: [PeerId: Message] = [:] + /*var messages: [PeerId: Message] = [:] for peerId in self.peerIds { if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex { @@ -62,7 +62,8 @@ final class MutableTopChatMessageView: MutablePostboxView { return true } else { return false - } + }*/ + return false } func immutableView() -> PostboxView { From cdc04c733c5f52156a4c0b92313e555752cd2ccb Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 20:35:30 +0400 Subject: [PATCH 18/20] Wallpaper experiment --- .../Sources/DebugController.swift | 14 +- .../Items/UniversalVideoGalleryItem.swift | 56 +- .../Sources/SoftwareGradientBackground.swift | 23 +- .../Sources/PasscodeEntryControllerNode.swift | 16 +- .../BubbleSettingsController.swift | 2 +- .../ForwardPrivacyChatPreviewItem.swift | 2 +- .../TextSizeSelectionController.swift | 2 +- .../Themes/SettingsThemeWallpaperNode.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 4 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 2 +- .../Sources/ShareController.swift | 2 +- submodules/Svg/PublicHeaders/Svg/Svg.h | 2 +- submodules/Svg/Sources/Svg.m | 6 +- .../State/AccountStateManagementUtils.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 2 +- .../Sources/ChatHistoryListNode.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 2 +- .../Sources/ExperimentalUISettings.swift | 12 +- submodules/WallpaperBackgroundNode/BUILD | 3 + .../Sources/WallpaperBackgroundNode.swift | 772 ++++++++++++++++-- .../Sources/WallpaperResources.swift | 2 +- 23 files changed, 835 insertions(+), 99 deletions(-) diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 8b043d797e..3ada505314 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -80,7 +80,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case experimentalCompatibility(Bool) case enableDebugDataDisplay(Bool) case acceleratedStickers(Bool) - case mockICE(Bool) + case experimentalBackground(Bool) case playerEmbedding(Bool) case playlistPlayback(Bool) case voiceConference @@ -102,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logging.rawValue case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: return DebugControllerSection.experiments.rawValue - case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .mockICE: + case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground: return DebugControllerSection.experiments.rawValue case .preferredVideoCodec: return DebugControllerSection.videoExperiments.rawValue @@ -171,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 27 case .acceleratedStickers: return 29 - case .mockICE: + case .experimentalBackground: return 30 case .playerEmbedding: return 31 @@ -804,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .mockICE(value): - return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .experimentalBackground(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings - settings.mockICE = value + settings.experimentalBackground = value return PreferencesEntry(settings) }) }).start() @@ -926,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility)) entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers)) - entries.append(.mockICE(experimentalSettings.mockICE)) + entries.append(.experimentalBackground(experimentalSettings.experimentalBackground)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 2e12e5845f..7da6c75921 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -518,7 +518,22 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte } public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) { - completionHandler() + let node = self.node + let _ = (self.node.status + |> take(1) + |> deliverOnMainQueue).start(next: { [weak node] status in + if let node = node, let timestamp = status?.timestamp, let duration = status?.duration { + let nextTimestamp = timestamp + skipInterval.seconds + if nextTimestamp > duration { + node.seek(0.0) + node.pause() + } else { + node.seek(min(duration, nextTimestamp)) + } + } + + completionHandler() + }) } public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool { @@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte private var hiddenMediaManagerIndex: Int? - init(overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) { + private var messageRemovedDisposable: Disposable? + + init(context: AccountContext, overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, canSkip: Bool, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) { self.overlayController = overlayController self.mediaManager = mediaManager self.node = videoNode @@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte contentDelegate.pictureInPictureController = pictureInPictureController pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false - pictureInPictureController.requiresLinearPlayback = true + pictureInPictureController.requiresLinearPlayback = !canSkip pictureInPictureController.delegate = self self.pictureInPictureController = pictureInPictureController let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in @@ -586,9 +603,31 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte } }) } + + if let (messageId, _) = hiddenMedia { + self.messageRemovedDisposable = (context.account.postbox.combinedView(keys: [PostboxViewKey.messages([messageId])]) + |> map { views -> Bool in + if let view = views.views[PostboxViewKey.messages([messageId])] as? MessagesView { + if view.messages[messageId] == nil { + return true + } + } + return false + } + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + guard let strongSelf = self else { + return + } + overlayController.removePictureInPictureContent(content: strongSelf) + strongSelf.node.canAttachContent = false + }) + } } deinit { + self.messageRemovedDisposable?.dispose() self.pictureInPictureTimer?.invalidate() self.node.setCanPlaybackWithoutHierarchy(false) @@ -698,6 +737,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private var dismissOnOrientationChange = false private var keepSoundOnDismiss = false private var hasPictureInPicture = false + + private var pictureInPictureButton: UIBarButtonItem? private var requiresDownload = false @@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) { let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) + self.pictureInPictureButton = rightBarButtonItem barButtonItems.append(rightBarButtonItem) self.hasPictureInPicture = true } else { @@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { pictureInPictureNode.removeFromSupernode() self.videoNode?.backgroundColor = .black } + + self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil } private func shouldAutoplayOnCentrality() -> Bool { @@ -2069,7 +2113,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { case let .message(message): for media in message.media { if let media = media as? TelegramMediaFile, media.isVideo { - isNativePictureInPictureSupported = true + if message.id.namespace == Namespaces.Message.Cloud { + isNativePictureInPictureSupported = true + } } } default: @@ -2106,7 +2152,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { break } - let content = PictureInPictureContentImpl(overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, willBegin: { [weak self] content in + let content = PictureInPictureContentImpl(context: self.context, overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, canSkip: true, willBegin: { [weak self] content in guard let strongSelf = self else { return } diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift index d9fa846ecf..0c22d1e273 100644 --- a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode { deinit { } - public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { + public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) { let sizeUpdated = self.validLayout != size self.validLayout = size @@ -372,6 +372,10 @@ public final class GradientBackgroundNode: ASDisplayNode { animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25 } + animation.completion = { _ in + completion() + } + self.contentView.layer.removeAnimation(forKey: "contents") self.contentView.layer.add(animation, forKey: "contents") @@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode { for cloneNode in self.cloneNodes { cloneNode.value?.image = dimmedImage } + + completion() } + } else { + completion() } } else if sizeUpdated { let image = generateGradient(size: imageSize, colors: self.colors, positions: positions) @@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode { } self.validPhase = self.phase + + completion() + } else { + completion() } transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) @@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode { self.colors = colors self.invalidated = true if let size = self.validLayout { - self.updateLayout(size: size, transition: .immediate) + self.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {}) } } } - public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { + public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) { guard case let .animated(duration, _) = transition, duration > 0.001 else { + completion() return } @@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode { GradientBackgroundNode.sharedPhase = self.phase } if let size = self.validLayout { - self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards) + self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion) + } else { + completion() } } } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 43f382b3cc..fea2c0773c 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -94,7 +94,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let strongSelf = self { strongSelf.inputFieldNode.append(character) if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring)) + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {}) } } } @@ -102,7 +102,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let strongSelf = self { let _ = strongSelf.inputFieldNode.delete() if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true) + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) } } } @@ -167,7 +167,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.hapticFeedback.tap() let result = self.inputFieldNode.delete() if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true) + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) } } @@ -337,7 +337,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true) + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {}) } } self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none) @@ -355,7 +355,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring)) + gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {}) self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } if !iconFrame.isEmpty { @@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring)) + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {}) } self.inputFieldNode.animateIn() self.keyboardNode.animateIn() @@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.hapticFeedback.error() if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true) + gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {}) } } @@ -440,7 +440,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let backgroundCustomNode = self.backgroundCustomNode { transition.updateFrame(node: backgroundCustomNode, frame: bounds) if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode { - gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition) + gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition, extendAnimation: false, backwards: false, completion: {}) } } transition.updateFrame(view: self.effectView, frame: bounds) diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 3c0b2ef2c5..a45d4764e9 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel self.scrollNode = ASScrollNode() - self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.chatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 6db765af58..2bab48b1c3 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { - currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) + currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) } currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index ea3fb677f8..f3db9bc29d 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4)) self.chatListBackgroundNode = ASDisplayNode() - self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.chatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift index 6ed9876771..a1c8bb2e8b 100644 --- a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift +++ b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift @@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { if let gradientNode = self.gradientNode { gradientNode.frame = CGRect(origin: CGPoint(), size: size) - gradientNode.updateLayout(size: size, transition: .immediate) + gradientNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {}) } let progressDiameter: CGFloat = 50.0 diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 08afc2236b..ddac2cdc46 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.backgroundContainerNode = ASDisplayNode() self.backgroundContainerNode.clipsToBounds = true self.backgroundWrapperNode = ASDisplayNode() - self.backgroundNode = createWallpaperBackgroundNode(context: context) + self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode.clipsToBounds = true diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 7cb2af7755..850fad8014 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) + self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.instantChatBackgroundNode.displaysAsynchronously = false self.ready.set(.single(true)) @@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.blurredNode = BlurredImageNode() self.blurredNode.blurView.contentMode = .scaleAspectFill - self.wallpaperNode = createWallpaperBackgroundNode(context: context) + self.wallpaperNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 064fa3e245..bbbfedb957 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { - currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) + currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) } currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 8d5a1921dc..68f9a32b3b 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.wrapperNode = ASDisplayNode() self.imageNode = TransformImageNode() self.imageNode.contentAnimations = .subsequentUpdates - self.nativeNode = createWallpaperBackgroundNode(context: context) + self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.cropNode = WallpaperCropNode() self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6)) self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 424eb02b38..2e7780300f 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -964,7 +964,7 @@ final class MessageStoryRenderer { self.containerNode = ASDisplayNode() - self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) + self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false) self.instantChatBackgroundNode.displaysAsynchronously = false self.messagesContainerNode = ASDisplayNode() diff --git a/submodules/Svg/PublicHeaders/Svg/Svg.h b/submodules/Svg/PublicHeaders/Svg/Svg.h index 952fecb850..48e51ef4ff 100755 --- a/submodules/Svg/PublicHeaders/Svg/Svg.h +++ b/submodules/Svg/PublicHeaders/Svg/Svg.h @@ -7,6 +7,6 @@ NSData * _Nullable prepareSvgImage(NSData * _Nonnull data); UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size); -UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor); +UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque); #endif /* Lottie_h */ diff --git a/submodules/Svg/Sources/Svg.m b/submodules/Svg/Sources/Svg.m index 2e8e07a2ee..23fd601265 100755 --- a/submodules/Svg/Sources/Svg.m +++ b/submodules/Svg/Sources/Svg.m @@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) { @end -UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor) { +UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor, bool opaque) { NSDate *startTime = [NSDate date]; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; @@ -115,8 +115,8 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b printf("parseTime = %f\n", deltaTime); startTime = [NSDate date]; - - UIGraphicsBeginImageContextWithOptions(size, true, 1.0); + + UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, backgroundColor.CGColor); CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 9d600828e6..76a5bcdf76 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2001,7 +2001,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat } else { pollPts = 1 } - return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: pollPts, limit: limit)) + return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: max(pollPts, 1), limit: limit)) |> map(Optional.init) |> `catch` { error -> Signal in switch error.errorDescription { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 208e5d0d30..f4fb597d67 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G default: break } - self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase) + self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground) self.wallpaperReady.set(self.chatBackgroundNode.isReady) var locationBroadcastPanelSource: LocationBroadcastPanelSource diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index ec89174c88..3f9511ba86 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { return } var adPeerId: PeerId? - adPeerId = nil + adPeerId = messages.first?.author?.id if strongSelf.preloadAdPeerId != adPeerId { strongSelf.preloadAdPeerId = adPeerId diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 7268d74939..82a45ea6ee 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 } - self.backgroundNode = createWallpaperBackgroundNode(context: context) + self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true) self.backgroundNode.isUserInteractionEnabled = false self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor) diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 6aa915a490..0fad7a6334 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -18,7 +18,7 @@ public struct ExperimentalUISettings: Codable, Equatable { public var experimentalCompatibility: Bool public var enableDebugDataDisplay: Bool public var acceleratedStickers: Bool - public var mockICE: Bool + public var experimentalBackground: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable { experimentalCompatibility: false, enableDebugDataDisplay: false, acceleratedStickers: false, - mockICE: false + experimentalBackground: false ) } @@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable { experimentalCompatibility: Bool, enableDebugDataDisplay: Bool, acceleratedStickers: Bool, - mockICE: Bool + experimentalBackground: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.experimentalCompatibility = experimentalCompatibility self.enableDebugDataDisplay = enableDebugDataDisplay self.acceleratedStickers = acceleratedStickers - self.mockICE = mockICE + self.experimentalBackground = experimentalBackground } public init(from decoder: Decoder) throws { @@ -91,7 +91,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0 self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0 self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0 - self.mockICE = (try container.decodeIfPresent(Int32.self, forKey: "mockICE") ?? 0) != 0 + self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0 } public func encode(to encoder: Encoder) throws { @@ -111,7 +111,7 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility") try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay") try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers") - try container.encode((self.mockICE ? 1 : 0) as Int32, forKey: "mockICE") + try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground") } } diff --git a/submodules/WallpaperBackgroundNode/BUILD b/submodules/WallpaperBackgroundNode/BUILD index 574f61d5db..1a0c4147ac 100644 --- a/submodules/WallpaperBackgroundNode/BUILD +++ b/submodules/WallpaperBackgroundNode/BUILD @@ -19,6 +19,9 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/WallpaperResources:WallpaperResources", "//submodules/FastBlur:FastBlur", + "//submodules/Svg:Svg", + "//submodules/GZip:GZip", + "//submodules/AppBundle:AppBundle", ], visibility = [ "//visibility:public", diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 47fc43592d..453b6ca4e5 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -9,6 +9,9 @@ import AccountContext import SwiftSignalKit import WallpaperResources import FastBlur +import Svg +import GZip +import AppBundle private let motionAmount: CGFloat = 32.0 @@ -781,12 +784,12 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode if let gradientBackgroundNode = self.gradientBackgroundNode { transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - gradientBackgroundNode.updateLayout(size: size, transition: transition) + gradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {}) } if let outgoingBubbleGradientBackgroundNode = self.outgoingBubbleGradientBackgroundNode { transition.updateFrame(node: outgoingBubbleGradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition) + outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {}) } self.loadPatternForSizeIfNeeded(size: size, transition: transition) @@ -797,21 +800,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { - self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) - self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) - } - - private func updateBakedBackground() { - guard let size = self.validLayout else { - return - } - let context = DrawingContext(size: size, scale: UIScreenScale, opaque: true) - - context.withContext { context in - context.clear(CGRect(origin: CGPoint(), size: size)) - } - - self.bakedBackgroundView.image = context.generateImage() + self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {}) + self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {}) } func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { @@ -824,7 +814,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false) if let size = self.validLayout { outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) - outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate) + outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {}) } self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode } @@ -898,32 +888,473 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } } +private protocol WallpaperComponentView: AnyObject { + var view: UIView { get } + + func update(size: CGSize, transition: ContainedViewLayoutTransition) +} + final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode { final class SharedStorage { } - private class WallpaperComponentView: UIView { - let updated: () -> Void + final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode { + private let bubbleType: WallpaperBubbleType + private let contentNode: ASImageNode - init(updated: @escaping () -> Void) { - self.updated = updated + private var cleanWallpaperNode: ASDisplayNode? + private var gradientWallpaperNode: GradientBackgroundNode.CloneNode? + private weak var backgroundNode: WallpaperBackgroundNodeMergedImpl? + private var index: SparseBag.Index? - super.init(frame: CGRect()) + private var currentLayout: (rect: CGRect, containerSize: CGSize)? + + override var frame: CGRect { + didSet { + if oldValue.size != self.bounds.size { + self.contentNode.frame = self.bounds + if let cleanWallpaperNode = self.cleanWallpaperNode { + cleanWallpaperNode.frame = self.bounds + } + if let gradientWallpaperNode = self.gradientWallpaperNode { + gradientWallpaperNode.frame = self.bounds + } + } + } + } + + init(backgroundNode: WallpaperBackgroundNodeMergedImpl, bubbleType: WallpaperBubbleType) { + self.backgroundNode = backgroundNode + self.bubbleType = bubbleType + + self.contentNode = ASImageNode() + self.contentNode.displaysAsynchronously = false + self.contentNode.isUserInteractionEnabled = false + + super.init() + + self.addSubnode(self.contentNode) + + self.index = backgroundNode.bubbleBackgroundNodeReferences.add(BubbleBackgroundNodeReference(node: self)) + } + + deinit { + if let index = self.index, let backgroundNode = self.backgroundNode { + backgroundNode.bubbleBackgroundNodeReferences.remove(index) + } + } + + func updateContents() { + guard let backgroundNode = self.backgroundNode else { + return + } + + if let bubbleTheme = backgroundNode.bubbleTheme, let bubbleCorners = backgroundNode.bubbleCorners { + let wallpaper = backgroundNode.wallpaper ?? bubbleTheme.chat.defaultWallpaper + + let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: wallpaper, bubbleCorners: bubbleCorners) + var needsCleanBackground = false + switch self.bubbleType { + case .incoming: + self.contentNode.image = graphics.incomingBubbleGradientImage + if graphics.incomingBubbleGradientImage == nil { + self.contentNode.backgroundColor = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill[0] + } else { + self.contentNode.backgroundColor = nil + } + needsCleanBackground = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) + case .outgoing: + if backgroundNode.outgoingBubbleGradientBackgroundNode != nil { + self.contentNode.image = nil + self.contentNode.backgroundColor = nil + } else { + self.contentNode.image = graphics.outgoingBubbleGradientImage + if graphics.outgoingBubbleGradientImage == nil { + self.contentNode.backgroundColor = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill[0] + } else { + self.contentNode.backgroundColor = nil + } + needsCleanBackground = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) + } + case .free: + self.contentNode.image = nil + self.contentNode.backgroundColor = nil + needsCleanBackground = true + } + + var isInvertedGradient = false + var hasComplexGradient = false + switch wallpaper { + case let .file(file): + hasComplexGradient = file.settings.colors.count >= 3 + if let intensity = file.settings.intensity, intensity < 0 { + isInvertedGradient = true + } + case let .gradient(gradient): + hasComplexGradient = gradient.colors.count >= 3 + default: + break + } + + var needsGradientBackground = false + var needsWallpaperBackground = false + + if isInvertedGradient { + switch self.bubbleType { + case .free: + needsCleanBackground = false + case .incoming, .outgoing: + break + } + } + + if needsCleanBackground { + if hasComplexGradient { + needsGradientBackground = backgroundNode.gradient != nil + } else { + needsWallpaperBackground = true + } + } + + var gradientBackgroundSource: GradientBackgroundNode? = backgroundNode.gradient?.gradientBackground + + if case .outgoing = self.bubbleType { + if let outgoingBubbleGradientBackgroundNode = backgroundNode.outgoingBubbleGradientBackgroundNode { + gradientBackgroundSource = outgoingBubbleGradientBackgroundNode + needsWallpaperBackground = false + needsGradientBackground = true + } + } + + if needsWallpaperBackground { + if self.cleanWallpaperNode == nil { + let cleanWallpaperNode = ASImageNode() + cleanWallpaperNode.displaysAsynchronously = false + self.cleanWallpaperNode = cleanWallpaperNode + cleanWallpaperNode.frame = self.bounds + self.insertSubnode(cleanWallpaperNode, at: 0) + } + if let blurredBackgroundContents = backgroundNode.blurredBackgroundContents { + self.cleanWallpaperNode?.contents = blurredBackgroundContents.cgImage + self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor + } else { + self.cleanWallpaperNode?.contents = nil + self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor + } + } else { + if let cleanWallpaperNode = self.cleanWallpaperNode { + self.cleanWallpaperNode = nil + cleanWallpaperNode.removeFromSupernode() + } + } + + if needsGradientBackground, let gradientBackgroundNode = gradientBackgroundSource { + if self.gradientWallpaperNode == nil { + let gradientWallpaperNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode) + gradientWallpaperNode.frame = self.bounds + self.gradientWallpaperNode = gradientWallpaperNode + self.insertSubnode(gradientWallpaperNode, at: 0) + } + } else { + if let gradientWallpaperNode = self.gradientWallpaperNode { + self.gradientWallpaperNode = nil + gradientWallpaperNode.removeFromSupernode() + } + } + } else { + self.contentNode.image = nil + if let cleanWallpaperNode = self.cleanWallpaperNode { + self.cleanWallpaperNode = nil + cleanWallpaperNode.removeFromSupernode() + } + } + + if let (rect, containerSize) = self.currentLayout { + self.update(rect: rect, within: containerSize) + } + } + + func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) { + self.currentLayout = (rect, containerSize) + + let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height)) + + transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds) + transition.animateView { + self.contentNode.layer.contentsRect = shiftedContentsRect + } + if let cleanWallpaperNode = self.cleanWallpaperNode { + transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds) + transition.animateView { + cleanWallpaperNode.layer.contentsRect = shiftedContentsRect + } + } + if let gradientWallpaperNode = self.gradientWallpaperNode { + transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds) + transition.animateView { + gradientWallpaperNode.layer.contentsRect = shiftedContentsRect + } + } + } + + func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) { + self.currentLayout = (rect, containerSize) + + let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height)) + + transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds) + self.contentNode.layer.contentsRect = shiftedContentsRect + if let cleanWallpaperNode = self.cleanWallpaperNode { + transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds) + cleanWallpaperNode.layer.contentsRect = shiftedContentsRect + } + if let gradientWallpaperNode = self.gradientWallpaperNode { + transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds) + gradientWallpaperNode.layer.contentsRect = shiftedContentsRect + } + } + + func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + guard let (_, containerSize) = self.currentLayout else { + return + } + let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve) + + let scaledOffset = CGPoint(x: value.x / containerSize.width, y: value.y / containerSize.height) + transition.animateContentsRectPositionAdditive(layer: self.contentNode.layer, offset: scaledOffset) + + if let cleanWallpaperNode = self.cleanWallpaperNode { + transition.animateContentsRectPositionAdditive(layer: cleanWallpaperNode.layer, offset: scaledOffset) + } + if let gradientWallpaperNode = self.gradientWallpaperNode { + transition.animateContentsRectPositionAdditive(layer: gradientWallpaperNode.layer, offset: scaledOffset) + } + } + + func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { + guard let (_, containerSize) = self.currentLayout else { + return + } + + let scaledOffset = CGPoint(x: 0.0, y: -value / containerSize.height) + + self.contentNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true) + if let cleanWallpaperNode = self.cleanWallpaperNode { + cleanWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true) + } + if let gradientWallpaperNode = self.gradientWallpaperNode { + gradientWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true) + } + } + } + + private final class BubbleBackgroundNodeReference { + weak var node: BubbleBackgroundNodeImpl? + + init(node: BubbleBackgroundNodeImpl) { + self.node = node + } + } + + private final class WallpaperGradiendComponentView: WallpaperComponentView { + struct Spec: Equatable { + var colors: [UInt32] + } + + let spec: Spec + let gradientBackground: GradientBackgroundNode + + var view: UIView { + return self.gradientBackground.view + } + + init(spec: Spec, updated: @escaping () -> Void) { + self.spec = spec + + self.gradientBackground = GradientBackgroundNode(colors: spec.colors.map(UIColor.init(rgb:)), useSharedAnimationPhase: true, adjustSaturation: false) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - } - private final class WallpaperGradiendComponentView: WallpaperComponentView { - struct Spec { - var colors: [UInt32] + func update(size: CGSize, transition: ContainedViewLayoutTransition) { + self.gradientBackground.frame = CGRect(origin: CGPoint(), size: size) + self.gradientBackground.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {}) } } - private final class WallpaperPatternComponentView: WallpaperComponentView { - struct Spec { + private final class WallpaperColorComponentView: WallpaperComponentView { + struct Spec: Equatable { + var color: UInt32 + } + + let spec: Spec + let backgroundView: UIView + + var view: UIView { + return self.backgroundView + } + + init(spec: Spec, updated: @escaping () -> Void) { + self.spec = spec + + self.backgroundView = UIView() + self.backgroundView.backgroundColor = UIColor(rgb: spec.color) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, transition: ContainedViewLayoutTransition) { + self.backgroundView.frame = CGRect(origin: CGPoint(), size: size) + } + } + + private final class WallpaperImageComponentView: WallpaperComponentView { + enum Spec: Equatable { + case image( + representation: TelegramMediaImageRepresentation, + isPattern: Bool, + intensity: CGFloat + ) + case builtin + } + + let spec: Spec + let updated: () -> Void + let imageView: UIImageView + var fetchDisposable: Disposable? + var dataDisposable: Disposable? + + var imageData: Data? + + private var validSize: CGSize? + + var view: UIView { + return self.imageView + } + + init(context: AccountContext, spec: Spec, updated: @escaping () -> Void) { + self.spec = spec + self.updated = updated + + self.imageView = UIImageView() + self.imageView.contentMode = .scaleAspectFill + + switch spec { + case let .image(representation, _, _): + self.fetchDisposable = (fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: MediaResourceReference.standalone(resource: representation.resource)) + |> deliverOnMainQueue).start() + self.dataDisposable = (context.account.postbox.mediaBox.resourceData(representation.resource) + |> deliverOnMainQueue).start(next: { [weak self] dataValue in + guard let strongSelf = self else { + return + } + + if dataValue.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: dataValue.path)) { + strongSelf.imageData = data + if let size = strongSelf.validSize { + strongSelf.updateImage(size: size, data: data) + } + } + }) + case .builtin: + if let filePath = getAppBundle().path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) { + self.imageData = data + if let size = self.validSize { + self.updateImage(size: size, data: data) + } + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.fetchDisposable?.dispose() + self.dataDisposable?.dispose() + } + + func update(size: CGSize, transition: ContainedViewLayoutTransition) { + let sizeUpdated = self.validSize != size + self.validSize = size + + self.imageView.frame = CGRect(origin: CGPoint(), size: size) + + if sizeUpdated || self.imageView.image == nil { + if let imageData = self.imageData { + self.updateImage(size: size, data: imageData) + } + } + } + + private func updateImage(size: CGSize, data: Data) { + let scale: CGFloat + if UIScreenScale >= 2.9 { + scale = 2.5 + } else { + scale = UIScreenScale + } + + switch self.spec { + case let .image(_, isPattern, intensity): + if isPattern { + let patternBackgroundColor: UIColor + let patternForegroundColor: UIColor + if intensity < 0.0 { + patternBackgroundColor = .clear + patternForegroundColor = .black + } else { + patternBackgroundColor = .clear + patternForegroundColor = .black + } + + if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let patternImage = drawSvgImage(unpackedData, CGSize(width: floor(size.width * scale), height: floor(size.height * scale)), patternBackgroundColor, patternForegroundColor, false) { + if intensity < 0.0 { + self.imageView.image = generateImage(patternImage.size, scale: patternImage.scale, rotatedContext: { size, context in + context.setFillColor(UIColor.black.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + + if let cgImage = patternImage.cgImage { + context.setBlendMode(.destinationOut) + context.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size)) + } + }) + self.imageView.alpha = 1.0 + self.imageView.layer.compositingFilter = nil + self.imageView.backgroundColor = UIColor(white: 0.0, alpha: 1.0 - abs(intensity)) + } else { + self.imageView.image = patternImage + self.imageView.alpha = abs(intensity) + self.imageView.layer.compositingFilter = "softLightBlendMode" + self.imageView.backgroundColor = nil + } + } + + self.updated() + } else if let image = UIImage(data: data) { + self.imageView.image = image + self.imageView.layer.compositingFilter = nil + self.imageView.alpha = 1.0 + + self.updated() + } + case .builtin: + if let image = UIImage(data: data) { + self.imageView.image = image + self.imageView.layer.compositingFilter = nil + self.imageView.alpha = 1.0 + + self.updated() + } + } } } @@ -931,8 +1362,17 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun private let storage: SharedStorage private let staticView: UIImageView + private let dynamicView: UIView + private var color: WallpaperColorComponentView? private var gradient: WallpaperGradiendComponentView? - private var pattern: WallpaperPatternComponentView? + private var image: WallpaperImageComponentView? + + private var blurredBackgroundContents: UIImage? + + private var isSettingUpWallpaper: Bool = false + + private var wallpaper: TelegramWallpaper? + private var validLayout: CGSize? private let _isReady = ValuePromise(false, ignoreRepeated: true) var isReady: Signal { @@ -944,11 +1384,19 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun } } + private var isAnimating: Bool = false + + private var bubbleTheme: PresentationTheme? + private var bubbleCorners: PresentationChatBubbleCorners? + private var bubbleBackgroundNodeReferences = SparseBag() + private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode? + init(context: AccountContext, storage: SharedStorage?) { self.context = context self.storage = storage ?? SharedStorage() self.staticView = UIImageView() + self.dynamicView = UIView() super.init() @@ -956,70 +1404,294 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun } func update(wallpaper: TelegramWallpaper) { + self.wallpaper = wallpaper + + var colorSpec: WallpaperColorComponentView.Spec? var gradientSpec: WallpaperGradiendComponentView.Spec? + var imageSpec: WallpaperImageComponentView.Spec? switch wallpaper { - case let .builtin(wallpaperSettings): - let _ = wallpaperSettings + case .builtin: + imageSpec = WallpaperImageComponentView.Spec.builtin case let .color(color): - let _ = color + colorSpec = WallpaperColorComponentView.Spec(color: color) case let .gradient(gradient): if gradient.colors.count >= 3 { gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors) } case let .image(representations, settings): - let _ = representations + if let representation = representations.last { + imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: false, intensity: 1.0) + } let _ = settings case let .file(file): if file.settings.colors.count >= 3 { gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors) } + if let dimensions = file.file.dimensions { + let representation = TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: file.file.immediateThumbnailData) + imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: file.isPattern, intensity: CGFloat(file.settings.intensity ?? 100) / 100.0) + } } - if let gradientSpec = gradientSpec { - let gradient: WallpaperGradiendComponentView - if let current = self.gradient { - gradient = current - } else { - gradient = WallpaperGradiendComponentView(updated: { [weak self] in - self?.componentsUpdated() - }) + if self.color?.spec != colorSpec { + if let color = self.color { + self.color = nil + color.view.removeFromSuperview() + } + if let colorSpec = colorSpec { + let color = WallpaperColorComponentView(spec: colorSpec, updated: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.componentsUpdated() + }) + self.color = color + if let size = self.validLayout { + color.update(size: size, transition: .immediate) + } + self.dynamicView.insertSubview(color.view, at: 0) + + self.componentsUpdated() + } + } + + if self.gradient?.spec != gradientSpec { + if let gradient = self.gradient { + self.gradient = nil + gradient.view.removeFromSuperview() + } + if let gradientSpec = gradientSpec { + let gradient = WallpaperGradiendComponentView(spec: gradientSpec, updated: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.componentsUpdated() + }) + self.gradient = gradient + if let size = self.validLayout { + gradient.update(size: size, transition: .immediate) + } + self.dynamicView.insertSubview(gradient.view, at: 0) + } + } + + if self.image?.spec != imageSpec { + if let image = self.image { + self.image = nil + image.view.removeFromSuperview() + } + if let imageSpec = imageSpec { + let image = WallpaperImageComponentView(context: self.context, spec: imageSpec, updated: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.componentsUpdated() + }) + self.image = image + if let size = self.validLayout { + image.update(size: size, transition: .immediate) + } + if let gradient = self.gradient { + self.dynamicView.insertSubview(image.view, aboveSubview: gradient.view) + } else { + self.dynamicView.insertSubview(image.view, at: 0) + } } - let _ = gradient - let _ = gradientSpec - } else if let gradient = self.gradient { - self.gradient = nil - gradient.removeFromSuperview() } } private func componentsUpdated() { + if self.isAnimating { + if self.dynamicView.superview == nil { + self.view.addSubview(self.dynamicView) + self.staticView.isHidden = true + } + self._isReady.set(true) + } else { + self.staticView.isHidden = false + self.dynamicView.removeFromSuperview() + + if let size = self.validLayout { + if let color = self.color { + self.staticView.image = nil + self.staticView.backgroundColor = color.backgroundView.backgroundColor + } else { + let gradientImage = self.gradient?.gradientBackground.contentView.image + let gradientFrame = self.gradient?.gradientBackground.frame + + let imageImage = self.image?.imageView.image + let imageBackgroundColor = self.image?.imageView.backgroundColor + let imageFrame = self.image?.imageView.frame + let imageAlpha = self.image?.imageView.alpha + let imageFilter = self.image?.imageView.layer.compositingFilter as? String + + self.staticView.image = generateImage(size, opaque: true, scale: nil, rotatedContext: { size, context in + UIGraphicsPushContext(context) + + if let gradientImage = gradientImage, let gradientFrame = gradientFrame { + gradientImage.draw(in: gradientFrame) + } + + if let imageImage = imageImage, let imageFrame = imageFrame, let imageAlpha = imageAlpha { + if imageFilter == "softLightBlendMode" { + context.setBlendMode(.softLight) + } + + if let imageBackgroundColor = imageBackgroundColor { + context.setFillColor(imageBackgroundColor.cgColor) + context.fill(imageFrame) + } + + context.setAlpha(imageAlpha) + + context.translateBy(x: imageFrame.midX, y: imageFrame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY) + if let cgImage = imageImage.cgImage { + let drawingSize = imageImage.size.aspectFilled(imageFrame.size) + context.draw(cgImage, in: CGRect(origin: CGPoint(x: imageFrame.minX + (imageFrame.width - drawingSize.width) / 2.0, y: imageFrame.minX + (imageFrame.height - drawingSize.height) / 2.0), size: drawingSize)) + } + context.translateBy(x: imageFrame.midX, y: imageFrame.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY) + + context.setBlendMode(.normal) + context.setAlpha(1.0) + } + + UIGraphicsPopContext() + }) + } + + self._isReady.set(true) + } + } } func _internalUpdateIsSettingUpWallpaper() { + self.isSettingUpWallpaper = true } func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + self.validLayout = size + self.staticView.frame = CGRect(origin: CGPoint(), size: size) + + if let gradient = self.gradient { + gradient.update(size: size, transition: transition) + } + if let image = self.image { + image.update(size: size, transition: transition) + } } func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { - + if let gradient = self.gradient { + self.isAnimating = true + self.componentsUpdated() + gradient.gradientBackground.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.isAnimating = false + strongSelf.componentsUpdated() + }) + } else { + self.isAnimating = false + } } func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { + if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners { + self.bubbleTheme = bubbleTheme + self.bubbleCorners = bubbleCorners + if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && bubbleTheme.chat.animateMessageColors { + if self.outgoingBubbleGradientBackgroundNode == nil { + let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false) + if let size = self.validLayout { + outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) + outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {}) + } + self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode + } + self.outgoingBubbleGradientBackgroundNode?.updateColors(colors: bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill) + } else if let _ = self.outgoingBubbleGradientBackgroundNode { + self.outgoingBubbleGradientBackgroundNode = nil + } + + self.updateBubbles() + } + } + + private func updateBubbles() { + for reference in self.bubbleBackgroundNodeReferences { + reference.node?.updateContents() + } } func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool { + guard let bubbleTheme = self.bubbleTheme, let bubbleCorners = self.bubbleCorners else { + return false + } + if self.wallpaper == nil && !self.isSettingUpWallpaper { + return false + } + + var hasPlainWallpaper = false + let graphicsWallpaper: TelegramWallpaper + if let wallpaper = self.wallpaper { + switch wallpaper { + case .color: + hasPlainWallpaper = true + default: + break + } + graphicsWallpaper = wallpaper + } else { + graphicsWallpaper = bubbleTheme.chat.defaultWallpaper + } + + let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: graphicsWallpaper, bubbleCorners: bubbleCorners) + switch type { + case .incoming: + if graphics.incomingBubbleGradientImage != nil { + return true + } + if bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) { + return !hasPlainWallpaper + } + case .outgoing: + if graphics.outgoingBubbleGradientImage != nil { + return true + } + if bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) { + return !hasPlainWallpaper + } + case .free: + return true + } + return false } func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? { - return nil + if !self.hasBubbleBackground(for: type) { + return nil + } + let node = WallpaperBackgroundNodeMergedImpl.BubbleBackgroundNodeImpl(backgroundNode: self, bubbleType: type) + node.updateContents() + return node } } -public func createWallpaperBackgroundNode(context: AccountContext, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode { +private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage() + +public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false, useExperimentalImplementation: Bool = false) -> WallpaperBackgroundNode { + if forChatDisplay && useExperimentalImplementation { + return WallpaperBackgroundNodeMergedImpl(context: context, storage: useSharedAnimationPhase ? sharedStorage : nil) + } + return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) } diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index fa925fec9b..b6e7549af1 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -1272,7 +1272,7 @@ public func themeImage(account: Account, accountManager: AccountManager Date: Fri, 5 Nov 2021 20:45:04 +0400 Subject: [PATCH 19/20] Adjust ad seen processing --- submodules/TelegramUI/Sources/ChatController.swift | 11 ++++++++++- .../TelegramUI/Sources/ChatHistoryListNode.swift | 8 +------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f4fb597d67..69e465dd12 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4975,8 +4975,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var minOffsetForNavigation: CGFloat = 40.0 strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in if let itemNode = itemNode as? ChatMessageBubbleItemNode { - if itemNode.item?.content.firstMessage.adAttribute != nil { + if let message = itemNode.item?.content.firstMessage, message.adAttribute != nil { minOffsetForNavigation += itemNode.bounds.height + + switch offset { + case let .known(offset): + if offset <= itemNode.bounds.height / 2.0 { + strongSelf.chatDisplayNode.historyNode.adSeenProcessingManager.add([message.id]) + } + default: + break + } } } return false diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 3f9511ba86..414a9b0879 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -469,7 +469,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private let galleryHiddenMesageAndMediaDisposable = MetaDisposable() private let messageProcessingManager = ChatMessageThrottledProcessingManager() - private let adSeenProcessingManager = ChatMessageThrottledProcessingManager() + let adSeenProcessingManager = ChatMessageThrottledProcessingManager() private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager() private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager() private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager() @@ -1563,7 +1563,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let toLaterRange = (historyView.filteredEntries.count - 1 - (visible.firstIndex - 1), historyView.filteredEntries.count - 1) var messageIdsWithViewCount: [MessageId] = [] - var messageIdsWithAds: [MessageId] = [] var messageIdsWithLiveLocation: [MessageId] = [] var messageIdsWithUnsupportedMedia: [MessageId] = [] var messageIdsWithRefreshMedia: [MessageId] = [] @@ -1589,8 +1588,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if message.id.namespace == Namespaces.Message.Cloud { messageIdsWithViewCount.append(message.id) } - } else if attribute is AdMessageAttribute { - messageIdsWithAds.append(message.id) } else if attribute is ReplyThreadMessageAttribute { if message.id.namespace == Namespaces.Message.Cloud { messageIdsWithViewCount.append(message.id) @@ -1754,9 +1751,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if !messageIdsWithViewCount.isEmpty { self.messageProcessingManager.add(messageIdsWithViewCount) } - if !messageIdsWithAds.isEmpty { - self.adSeenProcessingManager.add(messageIdsWithAds) - } if !messageIdsWithLiveLocation.isEmpty { self.seenLiveLocationProcessingManager.add(messageIdsWithLiveLocation) } From 1462366056b085c59b4ed5dbe88f33a86c7e96b8 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 5 Nov 2021 21:07:51 +0400 Subject: [PATCH 20/20] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index e735941e1c..e24a082d5d 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "8.2", + "app": "8.2.1", "bazel": "4.0.0", "xcode": "13.0" }