From f305edfb17ba1774ddb9f642c15544c12a7debf6 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 14 Jul 2023 20:49:24 +0400 Subject: [PATCH] Stories --- .../Sources/ChatListController.swift | 59 ++++++---- submodules/Display/Source/ListView.swift | 12 +- .../FFMpegMediaFrameSourceContext.swift | 11 ++ .../Sources/PhotoResources.swift | 30 ++--- .../Sources/StorageBox/StorageBox.swift | 63 +++++++++-- .../Sources/SaveToCameraRoll.swift | 9 +- .../StorageUsageExceptionsScreen.swift | 2 + .../SyncCore_CacheStorageSettings.swift | 4 +- .../Resources/CollectCacheUsageStats.swift | 80 +------------ .../Resources/TelegramEngineResources.swift | 1 + .../Utils/AutomaticCacheEviction.swift | 56 ++++++++- .../TelegramNotices/Sources/Notices.swift | 26 +++++ .../Sources/ArchiveInfoScreen.swift | 17 ++- .../Sources/StorageUsageScreen.swift | 107 ++++++++++++------ .../Sources/StoryChatContent.swift | 8 +- .../Sources/StoryItemContentComponent.swift | 4 +- .../Sources/StoryItemImageView.swift | 2 +- .../StoryItemSetContainerComponent.swift | 7 +- .../Sources/StorySetIndicatorComponent.swift | 14 ++- .../Sources/ChatMessageDateHeader.swift | 39 ++++++- .../Sources/NativeVideoContent.swift | 10 +- 21 files changed, 377 insertions(+), 184 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 086beed7d0..6d1350d3e0 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1065,28 +1065,45 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.chatListDisplayNode.mainContainerNode.groupSelected = { [weak self] groupId in - if let strongSelf = self { - if let navigationController = strongSelf.navigationController as? NavigationController { - let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupId), controlsHistoryPreload: false, enableDebugActions: false) - chatListController.navigationPresentation = .master - #if DEBUG && false - navigationController.pushViewController(chatListController, animated: false, completion: {}) - chatListController.onDidAppear = { [weak chatListController] in - Queue.mainQueue().after(0.1, { - guard let chatListController else { - return - } - if chatListController.hasStorySubscriptions { - chatListController.scrollToStoriesAnimated() - } - }) - } - #else - navigationController.pushViewController(chatListController) - #endif - strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) - } + guard let self else { + return } + + let _ = (combineLatest( + ApplicationSpecificNotice.displayChatListArchiveTooltip(accountManager: self.context.sharedContext.accountManager), + self.context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.GlobalPrivacy() + ) + ) + |> deliverOnMainQueue).start(next: { [weak self] didDisplayTip, settings in + guard let self else { + return + } + + self.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) + + if !didDisplayTip { + let _ = ApplicationSpecificNotice.setDisplayChatListArchiveTooltip(accountManager: self.context.sharedContext.accountManager).start() + + self.push(ArchiveInfoScreen(context: self.context, settings: settings, buttonAction: { [weak self] in + guard let self else { + return + } + + if let navigationController = self.navigationController as? NavigationController { + let chatListController = ChatListControllerImpl(context: self.context, location: .chatList(groupId: groupId), controlsHistoryPreload: false, enableDebugActions: false) + chatListController.navigationPresentation = .master + navigationController.pushViewController(chatListController) + } + })) + } else { + if let navigationController = self.navigationController as? NavigationController { + let chatListController = ChatListControllerImpl(context: self.context, location: .chatList(groupId: groupId), controlsHistoryPreload: false, enableDebugActions: false) + chatListController.navigationPresentation = .master + navigationController.pushViewController(chatListController) + } + } + }) } self.chatListDisplayNode.mainContainerNode.updatePeerGrouping = { [weak self] peerId, group in diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 5daeb253d2..6672f43915 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -1173,6 +1173,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } + private func getAdjustedContentHeight(effectiveInsets: UIEdgeInsets) -> CGFloat { + if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset, !keepMinimalScrollHeightWithTopInset.isZero { + return self.visibleSize.height + effectiveInsets.top + effectiveInsets.bottom// - 137.0 + } else { + return 0.0 + } + } + private func snapToBounds(snapTopItem: Bool, stackFromBottom: Bool, updateSizeAndInsets: ListViewUpdateSizeAndInsets? = nil, scrollToItem: ListViewScrollToItem? = nil, isExperimentalSnapToScrollToItem: Bool = false, insetDeltaOffsetFix: CGFloat) -> (snappedTopInset: CGFloat, offset: CGFloat) { if self.itemNodes.count == 0 { return (0.0, 0.0) @@ -1236,7 +1244,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset, topItemFound { if !self.stackFromBottom { if !keepMinimalScrollHeightWithTopInset.isZero { - completeHeight = max(completeHeight, self.visibleSize.height + effectiveInsets.top + effectiveInsets.bottom) + completeHeight = max(completeHeight, self.getAdjustedContentHeight(effectiveInsets: effectiveInsets)) } //completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset - effectiveInsets.bottom - effectiveInsets.top) bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) @@ -1651,7 +1659,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset { if !self.stackFromBottom { if !keepMinimalScrollHeightWithTopInset.isZero { - completeHeight = max(completeHeight, self.visibleSize.height + effectiveInsets.top + effectiveInsets.bottom) + completeHeight = max(completeHeight, self.getAdjustedContentHeight(effectiveInsets: effectiveInsets)) } //completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset) bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift index ff0f2f6a92..b52a8e0522 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift @@ -341,6 +341,17 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.statsCategory = video ? .video : .audio self.userLocation = userLocation self.userContentType = video ? .video : .audio + switch resourceReference { + case let .media(media, _): + switch media { + case .story: + self.userContentType = .story + default: + break + } + default: + break + } self.preferSoftwareDecoding = preferSoftwareDecoding self.fetchAutomatically = fetchAutomatically self.maximumFetchSize = maximumFetchSize diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index fccefeffe6..4cad354c05 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -55,7 +55,7 @@ public func representationFetchRangeForDisplayAtSize(representation: TelegramMed return nil } -public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false, forceThumbnail: Bool = false, automaticFetch: Bool = true) -> Signal, NoError> { +public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false, forceThumbnail: Bool = false, automaticFetch: Bool = true) -> Signal, NoError> { if !forceThumbnail, let progressiveRepresentation = progressiveImageRepresentation(photoReference.media.representations), progressiveRepresentation.progressiveSizes.count > 1 { enum SizeSource { case miniThumbnail(data: Data) @@ -131,9 +131,9 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU var fetchDisposable: Disposable? if automaticFetch { if autoFetchFullSize { - fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(largestByteSize), .default), statsCategory: .image).start() + fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(largestByteSize), .default), statsCategory: .image).start() } else if useMiniThumbnailIfAvailable { - fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(thumbnailByteSize), .default), statsCategory: .image).start() + fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(thumbnailByteSize), .default), statsCategory: .image).start() } } @@ -163,9 +163,9 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU if let _ = decodedThumbnailData { fetchedThumbnail = .complete() } else { - fetchedThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image) + fetchedThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image) } - let fetchedFullSize = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image) + let fetchedFullSize = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image) let anyThumbnail: [Signal<(MediaResourceData, ChatMessagePhotoQuality), NoError>] if tryAdditionalRepresentations { @@ -444,7 +444,7 @@ private func chatMessageImageFileThumbnailDatas(account: Account, userLocation: return signal } -private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, forceThumbnail: Bool = false) -> Signal?, Bool>, NoError> { +private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, forceThumbnail: Bool = false) -> Signal?, Bool>, NoError> { let fullSizeResource = fileReference.media.resource var reducedSizeResource: MediaResource? if let videoThumbnail = fileReference.media.videoThumbnails.first { @@ -474,7 +474,7 @@ private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResource } else if let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) { if autoFetchFullSizeThumbnail, let thumbnailRepresentation = thumbnailRepresentation, (thumbnailRepresentation.dimensions.width > 200 || thumbnailRepresentation.dimensions.height > 200) { thumbnail = Signal { subscriber in - let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailRepresentation.resource), statsCategory: .video).start() + let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailRepresentation.resource), statsCategory: .video).start() let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailRepresentation.resource, attemptSynchronously: synchronousLoad).start(next: { next in let data: Data? = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) if let data { @@ -494,7 +494,7 @@ private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResource } } else if let thumbnailResource = thumbnailResource { thumbnail = Signal { subscriber in - let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video).start() + let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video).start() let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource, attemptSynchronously: synchronousLoad).start(next: { next in subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) }, error: subscriber.putError, completed: subscriber.putCompletion) @@ -593,8 +593,8 @@ public func rawMessagePhoto(postbox: Postbox, userLocation: MediaResourceUserLoc } } -public func chatMessagePhoto(postbox: Postbox, userLocation: MediaResourceUserLocation, photoReference: ImageMediaReference, synchronousLoad: Bool = false, highQuality: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - return chatMessagePhotoInternal(photoData: chatMessagePhotoDatas(postbox: postbox, userLocation: userLocation, photoReference: photoReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad), synchronousLoad: synchronousLoad) +public func chatMessagePhoto(postbox: Postbox, userLocation: MediaResourceUserLocation, userContentType customUserContentType: MediaResourceUserContentType? = nil, photoReference: ImageMediaReference, synchronousLoad: Bool = false, highQuality: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + return chatMessagePhotoInternal(photoData: chatMessagePhotoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, photoReference: photoReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad), synchronousLoad: synchronousLoad) |> map { _, _, generate in return generate } @@ -1548,17 +1548,17 @@ public func gifPaneVideoThumbnail(account: Account, videoReference: FileMediaRef } } -public func mediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, videoReference: FileMediaReference, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - return internalMediaGridMessageVideo(postbox: postbox, userLocation: userLocation, videoReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, overlayColor: overlayColor, nilForEmptyResult: nilForEmptyResult, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable) +public func mediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, userContentType customUserContentType: MediaResourceUserContentType? = nil, videoReference: FileMediaReference, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + return internalMediaGridMessageVideo(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, videoReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, overlayColor: overlayColor, nilForEmptyResult: nilForEmptyResult, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable) |> map { return $0.1 } } -public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> { +public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false, blurred: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> { let signal: Signal?, Bool>, NoError> if let imageReference = imageReference { - signal = chatMessagePhotoDatas(postbox: postbox, userLocation: userLocation, photoReference: imageReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad, forceThumbnail: blurred) + signal = chatMessagePhotoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, photoReference: imageReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad, forceThumbnail: blurred) |> map { value -> Tuple3?, Bool> in let thumbnailData = value._0 let fullSizeData = value._1 @@ -1566,7 +1566,7 @@ public func internalMediaGridMessageVideo(postbox: Postbox, userLocation: MediaR return Tuple(thumbnailData, fullSizeData.flatMap({ Tuple($0, "") }), fullSizeComplete) } } else { - signal = chatMessageVideoDatas(postbox: postbox, userLocation: userLocation, fileReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, forceThumbnail: blurred) + signal = chatMessageVideoDatas(postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, fileReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, forceThumbnail: blurred) } return signal diff --git a/submodules/Postbox/Sources/StorageBox/StorageBox.swift b/submodules/Postbox/Sources/StorageBox/StorageBox.swift index 25bf19f8af..2cd2172c73 100644 --- a/submodules/Postbox/Sources/StorageBox/StorageBox.swift +++ b/submodules/Postbox/Sources/StorageBox/StorageBox.swift @@ -611,7 +611,7 @@ public final class StorageBox { } } - private func allInternal(peerId: PeerId) -> [Data] { + private func allInternal(peerId: PeerId, excludeType: UInt8) -> [Data] { var hashIds: [Data] = [] let peerIdIdKey = ValueBoxKey(length: 8) peerIdIdKey.setInt64(0, value: peerId.toInt64()) @@ -626,18 +626,56 @@ public final class StorageBox { mainKey.setData(0, value: hashId) if let infoValue = self.valueBox.get(self.hashIdToInfoTable, key: mainKey) { let info = ItemInfo(buffer: infoValue) - result.append(info.id) + if info.contentType != excludeType { + result.append(info.id) + } } } return result } - func all(peerId: PeerId, completion: @escaping ([Data]) -> Void) { + private func allInternal(peerId: PeerId, onlyType: UInt8) -> [Data] { + var hashIds: [Data] = [] + let peerIdIdKey = ValueBoxKey(length: 8) + peerIdIdKey.setInt64(0, value: peerId.toInt64()) + self.valueBox.range(self.peerIdToIdTable, start: peerIdIdKey, end: peerIdIdKey.successor, keys: { key in + hashIds.append(key.getData(8, length: 16)) + return true + }, limit: 0) + + var result: [Data] = [] + let mainKey = ValueBoxKey(length: 16) + for hashId in hashIds { + mainKey.setData(0, value: hashId) + if let infoValue = self.valueBox.get(self.hashIdToInfoTable, key: mainKey) { + let info = ItemInfo(buffer: infoValue) + if info.contentType == onlyType { + result.append(info.id) + } + } + } + + return result + } + + func all(peerId: PeerId, excludeType: UInt8, completion: @escaping ([Data]) -> Void) { self.beginInternalTransaction { self.valueBox.begin() - let result = self.allInternal(peerId: peerId) + let result = self.allInternal(peerId: peerId, excludeType: excludeType) + + self.valueBox.commit() + + completion(result) + } + } + + func all(peerId: PeerId, onlyType: UInt8, completion: @escaping ([Data]) -> Void) { + self.beginInternalTransaction { + self.valueBox.begin() + + let result = self.allInternal(peerId: peerId, onlyType: onlyType) self.valueBox.commit() @@ -932,7 +970,7 @@ public final class StorageBox { var scannedIds = Set() for peerId in peerIds { - scannedIds.formUnion(self.allInternal(peerId: peerId)) + scannedIds.formUnion(self.allInternal(peerId: peerId, excludeType: UInt8.max)) } for id in includeIds { @@ -1047,9 +1085,20 @@ public final class StorageBox { } } - public func all(peerId: PeerId) -> Signal<[Data], NoError> { + public func all(peerId: PeerId, excludeType: UInt8) -> Signal<[Data], NoError> { return self.impl.signalWith { impl, subscriber in - impl.all(peerId: peerId, completion: { result in + impl.all(peerId: peerId, excludeType: excludeType, completion: { result in + subscriber.putNext(result) + subscriber.putCompletion() + }) + + return EmptyDisposable + } + } + + public func all(peerId: PeerId, onlyType: UInt8) -> Signal<[Data], NoError> { + return self.impl.signalWith { impl, subscriber in + impl.all(peerId: peerId, onlyType: onlyType, completion: { result in subscriber.putNext(result) subscriber.putCompletion() }) diff --git a/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift b/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift index 9a5f64a162..e4c44da7b9 100644 --- a/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift +++ b/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift @@ -15,7 +15,7 @@ public enum FetchMediaDataState { case data(MediaResourceData) } -public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, mediaReference: AnyMediaReference, forceVideo: Bool = false) -> Signal<(FetchMediaDataState, Bool), NoError> { +public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, mediaReference: AnyMediaReference, forceVideo: Bool = false) -> Signal<(FetchMediaDataState, Bool), NoError> { var resource: MediaResource? var isImage = true var fileExtension: String? @@ -50,6 +50,9 @@ public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocati } } } + if let customUserContentType { + userContentType = customUserContentType + } if let resource = resource { let fetchedData: Signal = Signal { subscriber in @@ -86,8 +89,8 @@ public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocati } } -public func saveToCameraRoll(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, mediaReference: AnyMediaReference) -> Signal { - return fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, mediaReference: mediaReference) +public func saveToCameraRoll(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, mediaReference: AnyMediaReference) -> Signal { + return fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, mediaReference: mediaReference) |> mapToSignal { state, isImage -> Signal in switch state { case let .progress(value): diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift index 3d8ec5d6c1..659760bbe1 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift @@ -325,6 +325,8 @@ public func storageUsageExceptionsScreen( filter.insert(.excludeSecretChats) case .channels: filter.insert(.onlyChannels) + case .stories: + filter.insert(.onlyPrivateChats) } let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle)) controller.peerSelected = { [weak controller] peer, _ in diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CacheStorageSettings.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CacheStorageSettings.swift index 2d0d6ef90b..74e73a0fb2 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CacheStorageSettings.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CacheStorageSettings.swift @@ -6,6 +6,7 @@ public struct CacheStorageSettings: Codable, Equatable { case privateChats = "privateChats" case groups = "groups" case channels = "channels" + case stories = "stories" } private struct CategoryStorageTimeoutRepresentation: Codable { @@ -25,7 +26,8 @@ public struct CacheStorageSettings: Codable, Equatable { categoryStorageTimeout: [ .privateChats: Int32.max, .groups: Int32(31 * 24 * 60 * 60), - .channels: Int32(7 * 24 * 60 * 60) + .channels: Int32(7 * 24 * 60 * 60), + .stories: Int32(2 * 24 * 60 * 60) ] ) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift index 67a3b370a5..291009d8be 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift @@ -62,6 +62,7 @@ public final class StorageUsageStats { case stickers case avatars case misc + case stories } public struct CategoryData { @@ -127,6 +128,8 @@ private extension StorageUsageStats { mappedCategory = .misc case MediaResourceUserContentType.audioVideoMessage.rawValue: mappedCategory = .misc + case MediaResourceUserContentType.story.rawValue: + mappedCategory = .stories default: mappedCategory = .misc } @@ -211,81 +214,6 @@ public func collectRawStorageUsageReport(containerPath: String) -> String { } func _internal_collectStorageUsageStats(account: Account) -> Signal { - /*let additionalStats = Signal { subscriber in - DispatchQueue.global().async { - var totalSize: Int64 = 0 - - let additionalPaths: [String] = [ - "cache", - "animation-cache", - "short-cache", - ] - - func statForDirectory(path: String) -> Int64 { - var s = darwin_dirstat() - var result = dirstat_np(path, 1, &s, MemoryLayout.size) - if result != -1 { - return Int64(s.total_size) - } else { - result = dirstat_np(path, 0, &s, MemoryLayout.size) - if result != -1 { - return Int64(s.total_size) - } else { - return 0 - } - } - } - - var delayedDirs: [String] = [] - - for path in additionalPaths { - let fullPath: String - if path.isEmpty { - fullPath = account.postbox.mediaBox.basePath - } else { - fullPath = account.postbox.mediaBox.basePath + "/\(path)" - } - - if path == "animation-cache" { - if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: fullPath), includingPropertiesForKeys: [.isDirectoryKey], options: .skipsSubdirectoryDescendants) { - for url in enumerator { - guard let url = url as? URL else { - continue - } - delayedDirs.append(fullPath + "/" + url.lastPathComponent) - } - } - } else { - totalSize += statForDirectory(path: fullPath) - } - } - - if !delayedDirs.isEmpty { - let concurrentSize = Atomic<[Int64]>(value: []) - - DispatchQueue.concurrentPerform(iterations: delayedDirs.count, execute: { index in - let directorySize = statForDirectory(path: delayedDirs[index]) - let result = concurrentSize.modify { current in - return current + [directorySize] - } - if result.count == delayedDirs.count { - var aggregatedCount: Int64 = 0 - for item in result { - aggregatedCount += item - } - subscriber.putNext(totalSize + aggregatedCount) - subscriber.putCompletion() - } - }) - } else { - subscriber.putNext(totalSize) - subscriber.putCompletion() - } - } - - return EmptyDisposable - }*/ - let additionalStats = account.postbox.mediaBox.cacheStorageBox.totalSize() |> take(1) return combineLatest( @@ -425,6 +353,8 @@ func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories // Legacy value for Gif mappedContentTypes.append(5) + case .stories: + mappedContentTypes.append(MediaResourceUserContentType.story.rawValue) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index 91ef9b2342..122421d87b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -12,6 +12,7 @@ public enum MediaResourceUserContentType: UInt8, Equatable { case sticker = 6 case avatar = 7 case audioVideoMessage = 8 + case story = 9 } public extension MediaResourceUserContentType { diff --git a/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift b/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift index 02c414460d..e4773b42e8 100644 --- a/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift +++ b/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift @@ -141,7 +141,7 @@ final class AutomaticCacheEvictionContext { let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - timeout //let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - return mediaBox.storageBox.all(peerId: peerId) + let allSignal = mediaBox.storageBox.all(peerId: peerId, excludeType: MediaResourceUserContentType.story.rawValue) |> mapToSignal { peerResourceIds -> Signal in return Signal { subscriber in var isCancelled = false @@ -192,6 +192,60 @@ final class AutomaticCacheEvictionContext { } } } + + let storySignal = mediaBox.storageBox.all(peerId: peerId, onlyType: MediaResourceUserContentType.story.rawValue) + |> mapToSignal { peerResourceIds -> Signal in + return Signal { subscriber in + var isCancelled = false + + processingQueue.justDispatch { + var removeIds: [MediaResourceId] = [] + var removeRawIds: [Data] = [] + var localCounter = 0 + for resourceId in peerResourceIds { + localCounter += 1 + if localCounter % 100 == 0 { + if isCancelled { + subscriber.putCompletion() + return + } + } + + removeRawIds.append(resourceId) + let id = MediaResourceId(String(data: resourceId, encoding: .utf8)!) + let resourceTimestamp = mediaBox.resourceUsageWithInfo(id: id) + if resourceTimestamp != 0 && resourceTimestamp < minPeerTimestamp { + removeIds.append(id) + } + } + + if !removeIds.isEmpty { + Logger.shared.log("AutomaticCacheEviction", "peer \(peerId): cleaning \(removeIds.count) resources") + + let _ = mediaBox.removeCachedResourcesWithResult(removeIds).start(next: { actualIds in + var actualRawIds: [Data] = [] + for id in actualIds { + if let data = id.stringRepresentation.data(using: .utf8) { + actualRawIds.append(data) + } + } + + mediaBox.storageBox.remove(ids: actualRawIds) + + subscriber.putCompletion() + }) + } else { + subscriber.putCompletion() + } + } + + return ActionDisposable { + isCancelled = true + } + } + } + + return allSignal |> then(storySignal) } return signals diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 8b6ab9edd0..4a914141bc 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -176,6 +176,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case displayChatListStoriesTooltip = 42 case storiesCameraTooltip = 43 case storiesDualCameraTooltip = 44 + case displayChatListArchiveTooltip = 45 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -409,6 +410,10 @@ private struct ApplicationSpecificNoticeKeys { static func storiesDualCameraTooltip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.storiesDualCameraTooltip.key) } + + static func displayChatListArchiveTooltip() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.displayChatListArchiveTooltip.key) + } } public struct ApplicationSpecificNotice { @@ -1528,4 +1533,25 @@ public struct ApplicationSpecificNotice { return accountManager.transaction { transaction -> Void in } } + + public static func displayChatListArchiveTooltip(accountManager: AccountManager) -> Signal { + return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.displayChatListArchiveTooltip()) + |> map { view -> Bool in + if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + } + |> take(1) + } + + public static func setDisplayChatListArchiveTooltip(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Void in + if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { + transaction.setNotice(ApplicationSpecificNoticeKeys.displayChatListArchiveTooltip(), entry) + } + } + |> ignoreValues + } } diff --git a/submodules/TelegramUI/Components/Settings/ArchiveInfoScreen/Sources/ArchiveInfoScreen.swift b/submodules/TelegramUI/Components/Settings/ArchiveInfoScreen/Sources/ArchiveInfoScreen.swift index 72057776ed..a28728c9d2 100644 --- a/submodules/TelegramUI/Components/Settings/ArchiveInfoScreen/Sources/ArchiveInfoScreen.swift +++ b/submodules/TelegramUI/Components/Settings/ArchiveInfoScreen/Sources/ArchiveInfoScreen.swift @@ -133,13 +133,16 @@ private final class ArchiveInfoScreenComponent: Component { let context: AccountContext let settings: GlobalPrivacySettings + let buttonAction: (() -> Void)? init( context: AccountContext, - settings: GlobalPrivacySettings + settings: GlobalPrivacySettings, + buttonAction: (() -> Void)? ) { self.context = context self.settings = settings + self.buttonAction = buttonAction } static func ==(lhs: ArchiveInfoScreenComponent, rhs: ArchiveInfoScreenComponent) -> Bool { @@ -212,10 +215,15 @@ private final class ArchiveInfoScreenComponent: Component { guard let self else { return } - self.sheetAnimateOut.invoke(Action { _ in + self.sheetAnimateOut.invoke(Action { [weak self] _ in if let controller = environment.controller() { controller.dismiss(completion: nil) } + + guard let self else { + return + } + self.component?.buttonAction?() }) } )), @@ -249,10 +257,11 @@ private final class ArchiveInfoScreenComponent: Component { } public class ArchiveInfoScreen: ViewControllerComponentContainer { - public init(context: AccountContext, settings: GlobalPrivacySettings) { + public init(context: AccountContext, settings: GlobalPrivacySettings, buttonAction: (() -> Void)? = nil) { super.init(context: context, component: ArchiveInfoScreenComponent( context: context, - settings: settings + settings: settings, + buttonAction: buttonAction ), navigationBarAppearance: .none) self.statusBar.statusBarStyle = .Ignore diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 11b12db4bb..3779bd27c1 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -106,6 +106,8 @@ private extension StorageUsageScreenComponent.Category { self = .avatars case .misc: self = .misc + case .stories: + self = .stories } } } @@ -264,6 +266,7 @@ final class StorageUsageScreenComponent: Component { case stickers case avatars case misc + case stories var color: UIColor { switch self { @@ -283,6 +286,8 @@ final class StorageUsageScreenComponent: Component { return UIColor(rgb: 0xAF52DE) case .misc: return UIColor(rgb: 0xFF9500) + case .stories: + return UIColor(rgb: 0x3478F6) } } @@ -304,6 +309,9 @@ final class StorageUsageScreenComponent: Component { return strings.StorageManagement_SectionAvatars case .misc: return strings.StorageManagement_SectionMiscellaneous + case .stories: + //TODO:localize + return "Stories" } } @@ -325,6 +333,8 @@ final class StorageUsageScreenComponent: Component { return "Settings/Storage/ParticleAvatars" case .misc: return "Settings/Storage/ParticleOther" + case .stories: + return "Settings/Storage/ParticleOther" } } } @@ -1254,7 +1264,8 @@ final class StorageUsageScreenComponent: Component { .music, .stickers, .avatars, - .misc + .misc, + .stories ] var listCategories: [StorageCategoriesComponent.CategoryData] = [] @@ -1286,6 +1297,8 @@ final class StorageUsageScreenComponent: Component { mappedCategory = .avatars case .misc: mappedCategory = .misc + case .stories: + mappedCategory = .stories case .other: continue } @@ -1773,7 +1786,7 @@ final class StorageUsageScreenComponent: Component { contentHeight += 8.0 var keepContentHeight: CGFloat = 0.0 - for i in 0 ..< 3 { + for i in 0 ..< 4 { let item: ComponentView if let current = self.keepDurationItems[i] { item = current @@ -1795,6 +1808,11 @@ final class StorageUsageScreenComponent: Component { iconName = "Settings/Menu/GroupChats" title = environment.strings.Notifications_GroupChats mappedCategory = .groups + case 3: + iconName = "Settings/Menu/Stories" + //TODO:localized + title = "Stories" + mappedCategory = .stories default: iconName = "Settings/Menu/Channels" title = environment.strings.Notifications_Channels @@ -1810,8 +1828,10 @@ final class StorageUsageScreenComponent: Component { } var subtitle: String? - if let cacheSettingsExceptionCount = self.cacheSettingsExceptionCount, let categoryCount = cacheSettingsExceptionCount[mappedCategory] { - subtitle = environment.strings.CacheEvictionMenu_CategoryExceptions(Int32(categoryCount)) + if mappedCategory != .stories { + if let cacheSettingsExceptionCount = self.cacheSettingsExceptionCount, let categoryCount = cacheSettingsExceptionCount[mappedCategory] { + subtitle = environment.strings.CacheEvictionMenu_CategoryExceptions(Int32(categoryCount)) + } } let itemSize = item.update( @@ -1822,7 +1842,7 @@ final class StorageUsageScreenComponent: Component { title: title, subtitle: subtitle, value: optionText, - hasNext: i != 3 - 1, + hasNext: i != 4 - 1, action: { [weak self] sourceView in guard let self else { return @@ -2869,6 +2889,8 @@ final class StorageUsageScreenComponent: Component { mappedCategories.append(.avatars) case .misc: mappedCategories.append(.misc) + case .stories: + mappedCategories.append(.stories) } } @@ -2918,6 +2940,8 @@ final class StorageUsageScreenComponent: Component { mappedCategories.append(.avatars) case .misc: mappedCategories.append(.misc) + case .stories: + mappedCategories.append(.stories) } } @@ -2947,6 +2971,8 @@ final class StorageUsageScreenComponent: Component { mappedCategory = .avatars case .misc: mappedCategory = .misc + case .stories: + mappedCategory = .stories } if let value = contextStats.categories[mappedCategory] { @@ -3149,12 +3175,23 @@ final class StorageUsageScreenComponent: Component { var subItems: [ContextMenuItem] = [] let presentationData = context.sharedContext.currentPresentationData.with { $0 } - var presetValues: [Int32] = [ - Int32.max, - 31 * 24 * 60 * 60, - 7 * 24 * 60 * 60, - 1 * 24 * 60 * 60 - ] + var presetValues: [Int32] + + if case .stories = mappedCategory { + presetValues = [ + 7 * 24 * 60 * 60, + 2 * 24 * 60 * 60, + 1 * 24 * 60 * 60 + ] + } else { + presetValues = [ + Int32.max, + 31 * 24 * 60 * 60, + 7 * 24 * 60 * 60, + 1 * 24 * 60 * 60 + ] + } + if currentValue != 0 && !presetValues.contains(currentValue) { presetValues.append(currentValue) presetValues.sort(by: >) @@ -3181,30 +3218,32 @@ final class StorageUsageScreenComponent: Component { subItems.append(.separator) - if peerExceptions.isEmpty { - let exceptionsText = presentationData.strings.GroupInfo_Permissions_AddException - subItems.append(.action(ContextMenuActionItem(text: exceptionsText, icon: { theme in - if case .privateChats = mappedCategory { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Location/CreateGroupIcon"), color: theme.contextMenu.primaryColor) - } - }, action: { _, f in - f(.default) - - if let exceptionsController = makeStorageUsageExceptionsScreen(mappedCategory) { - pushControllerImpl?(exceptionsController) - } - }))) - } else { - subItems.append(.custom(MultiplePeerAvatarsContextItem(context: context, peers: peerExceptions.prefix(3).map { EnginePeer($0.peer.peer) }, totalCount: peerExceptions.count, action: { c, _ in - c.dismiss(completion: { + if mappedCategory != .stories { + if peerExceptions.isEmpty { + let exceptionsText = presentationData.strings.GroupInfo_Permissions_AddException + subItems.append(.action(ContextMenuActionItem(text: exceptionsText, icon: { theme in + if case .privateChats = mappedCategory { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Location/CreateGroupIcon"), color: theme.contextMenu.primaryColor) + } + }, action: { _, f in + f(.default) - }) - if let exceptionsController = makeStorageUsageExceptionsScreen(mappedCategory) { - pushControllerImpl?(exceptionsController) - } - }), false)) + if let exceptionsController = makeStorageUsageExceptionsScreen(mappedCategory) { + pushControllerImpl?(exceptionsController) + } + }))) + } else { + subItems.append(.custom(MultiplePeerAvatarsContextItem(context: context, peers: peerExceptions.prefix(3).map { EnginePeer($0.peer.peer) }, totalCount: peerExceptions.count, action: { c, _ in + c.dismiss(completion: { + + }) + if let exceptionsController = makeStorageUsageExceptionsScreen(mappedCategory) { + pushControllerImpl?(exceptionsController) + } + }), false)) + } } if let sourceLabelView = sourceView.labelView { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 273cd394b3..b9423975b0 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -1401,7 +1401,7 @@ public func preloadStoryMedia(context: AccountContext, peer: PeerReference, stor switch media { case let .image(image): if let representation = largestImageRepresentation(image.representations) { - signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .other, reference: .media(media: .story(peer: peer, id: storyId, media: media._asMedia()), resource: representation.resource), range: nil) + signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peer, id: storyId, media: media._asMedia()), resource: representation.resource), range: nil) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -1418,7 +1418,7 @@ public func preloadStoryMedia(context: AccountContext, peer: PeerReference, stor } } - signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .other, reference: .media(media: .story(peer: peer, id: storyId, media: media._asMedia()), resource: file.resource), range: fetchRange) + signals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peer, id: storyId, media: media._asMedia()), resource: file.resource), range: fetchRange) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -1459,7 +1459,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine |> ignoreValues ) - loadSignals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .other, reference: .media(media: .story(peer: peer, id: storyItem.id, media: storyItem.media._asMedia()), resource: representation.resource), range: nil) + loadSignals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peer, id: storyItem.id, media: storyItem.media._asMedia()), resource: representation.resource), range: nil) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -1489,7 +1489,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine |> ignoreValues ) - loadSignals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .other, reference: .media(media: .story(peer: peer, id: storyItem.id, media: storyItem.media._asMedia()), resource: file.resource), range: fetchRange) + loadSignals.append(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peer, id: storyItem.id, media: storyItem.media._asMedia()), resource: file.resource), range: fetchRange) |> ignoreValues |> `catch` { _ -> Signal in return .complete() diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index b199043c38..d43ccbb6c1 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -136,7 +136,7 @@ final class StoryItemContentComponent: Component { decoration: StoryVideoDecoration(), content: NativeVideoContent( id: .contextResult(0, "\(UInt64.random(in: 0 ... UInt64.max))"), - userLocation: .other, + userLocation: .peer(peerReference.id), fileReference: .story(peer: peerReference, id: component.item.id, media: file), imageReference: nil, streamVideo: .story, @@ -470,7 +470,7 @@ final class StoryItemContentComponent: Component { fetchSignal = fetchedMediaResource( mediaBox: component.context.account.postbox.mediaBox, userLocation: .other, - userContentType: .image, + userContentType: .story, reference: FileMediaReference.story(peer: peerReference, id: component.item.id, media: file).resourceReference(file.resource) ) |> ignoreValues diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift index 109d29ab80..2354131bf7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift @@ -102,7 +102,7 @@ final class StoryItemImageView: UIView { } if let peerReference = PeerReference(peer._asPeer()) { - self.fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .image, reference: .media(media: .story(peer: peerReference, id: storyId, media: media._asMedia()), resource: representation.resource), ranges: nil).start() + self.fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peerReference, id: storyId, media: media._asMedia()), resource: representation.resource), ranges: nil).start() } self.disposable = (context.account.postbox.mediaBox.resourceData(representation.resource, option: .complete(waitUntilFetchStatus: false)) |> map { result -> UIImage? in diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index c22f9e329b..d9712c4fc8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -938,9 +938,6 @@ public final class StoryItemSetContainerComponent: Component { if self.sendMessageContext.shareController != nil { return .pause } - if self.sendMessageContext.tooltipScreen != nil { - return .pause - } if let navigationController = component.controller()?.navigationController as? NavigationController { let topViewController = navigationController.topViewController if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) && !(topViewController is ShareWithPeersScreen) && !(topViewController is AttachmentController) { @@ -3380,7 +3377,7 @@ public final class StoryItemSetContainerComponent: Component { if let file = media as? TelegramMediaFile { duration = file.duration } - subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: media)) + subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .peer(peerReference.id), customUserContentType: .story, mediaReference: .story(peer: peerReference, id: item.id, media: media)) |> mapToSignal { (value, isImage) -> Signal in guard case let .data(data) = value, data.complete else { return .complete() @@ -3553,7 +3550,7 @@ public final class StoryItemSetContainerComponent: Component { let saveScreen = SaveProgressScreen(context: component.context, content: .progress("Saving", 0.0)) component.controller()?.present(saveScreen, in: .current) - let disposable = (saveToCameraRoll(context: component.context, postbox: component.context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: component.slice.item.storyItem.id, media: component.slice.item.storyItem.media._asMedia())) + let disposable = (saveToCameraRoll(context: component.context, postbox: component.context.account.postbox, userLocation: .peer(peerReference.id), customUserContentType: .story, mediaReference: .story(peer: peerReference, id: component.slice.item.storyItem.id, media: component.slice.item.storyItem.media._asMedia())) |> deliverOnMainQueue).start(next: { [weak saveScreen] progress in guard let saveScreen else { return diff --git a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift index 9534a88132..98856c4da9 100644 --- a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift @@ -167,7 +167,8 @@ public final class StorySetIndicatorComponent: Component { case let .image(image): signal = chatMessagePhoto( postbox: context.account.postbox, - userLocation: .other, + userLocation: .peer(peerReference.id), + userContentType: .story, photoReference: .story(peer: peerReference, id: item.id, media: image), synchronousLoad: false, highQuality: true @@ -175,8 +176,8 @@ public final class StorySetIndicatorComponent: Component { if let representation = image.representations.last { fetchSignal = fetchedMediaResource( mediaBox: context.account.postbox.mediaBox, - userLocation: .other, - userContentType: .image, + userLocation: .peer(peerReference.id), + userContentType: .story, reference: ImageMediaReference.story(peer: peerReference, id: item.id, media: image).resourceReference(representation.resource) ) |> ignoreValues @@ -187,7 +188,8 @@ public final class StorySetIndicatorComponent: Component { case let .file(file): signal = mediaGridMessageVideo( postbox: context.account.postbox, - userLocation: .other, + userLocation: .peer(peerReference.id), + userContentType: .story, videoReference: .story(peer: peerReference, id: item.id, media: file), onlyFullSize: false, useLargeThumbnail: true, @@ -200,8 +202,8 @@ public final class StorySetIndicatorComponent: Component { ) fetchSignal = fetchedMediaResource( mediaBox: context.account.postbox.mediaBox, - userLocation: .other, - userContentType: .image, + userLocation: .peer(peerReference.id), + userContentType: .story, reference: FileMediaReference.story(peer: peerReference, id: item.id, media: file).resourceReference(file.resource) ) |> ignoreValues diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index 7465e2ca1f..91490333d2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -450,6 +450,8 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { private var cachedDataDisposable = MetaDisposable() private var hierarchyTrackingLayer: HierarchyTrackingLayer? + private var backgroundContent: WallpaperBubbleBackgroundNode? + private var trackingIsInHierarchy: Bool = false { didSet { if self.trackingIsInHierarchy != oldValue { @@ -606,7 +608,40 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { } func updateStoryStats(storyStats: PeerStoryStats?, theme: PresentationTheme, force: Bool) { + /*if storyStats != nil { + var backgroundContent: WallpaperBubbleBackgroundNode? + if let current = self.backgroundContent { + backgroundContent = current + } else { + if let backgroundContentValue = self.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + backgroundContentValue.clipsToBounds = true + self.backgroundContent = backgroundContentValue + backgroundContent = backgroundContentValue + self.containerNode.insertSubnode(backgroundContentValue, belowSubnode: self.avatarNode) + + let maskLayer = SimpleShapeLayer() + maskLayer.fillColor = nil + maskLayer.strokeColor = UIColor.white.cgColor + maskLayer.lineWidth = 2.0 + maskLayer.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)).insetBy(dx: 1.0, dy: 1.0)).cgPath + backgroundContentValue.layer.mask = maskLayer + } + } + + if let backgroundContent { + backgroundContent.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)) + backgroundContent.cornerRadius = backgroundContent.bounds.width * 0.5 + } + } else { + if let backgroundContent = self.backgroundContent { + self.backgroundContent = nil + backgroundContent.removeFromSupernode() + } + } + if self.storyStats != storyStats || self.presentationData.theme.theme !== theme || force { + var colors = AvatarNode.Colors(theme: theme) + colors.seenColors = [UIColor(white: 1.0, alpha: 0.2), UIColor(white: 1.0, alpha: 0.2)] self.avatarNode.setStoryStats(storyStats: storyStats.flatMap { storyStats in return AvatarNode.StoryStats( totalCount: storyStats.totalCount != 0 ? 1 : 0, @@ -614,11 +649,11 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends ) }, presentationParams: AvatarNode.StoryPresentationParams( - colors: AvatarNode.Colors(theme: theme), + colors: colors, lineWidth: 2.0, inactiveLineWidth: 2.0 ), transition: .immediate) - } + }*/ } override func didLoad() { diff --git a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift index 545a773150..ca27dd7f71 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift @@ -198,7 +198,15 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent self.imageNode = TransformImageNode() - self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, userLocation: userLocation, userContentType: MediaResourceUserContentType(file: fileReference.media), resourceReference: fileReference.resourceReference(fileReference.media.resource), tempFilePath: tempFilePath, streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, ambient: beginWithAmbientSound, mixWithOthers: mixWithOthers, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession, storeAfterDownload: storeAfterDownload, isAudioVideoMessage: isAudioVideoMessage) + var userContentType = MediaResourceUserContentType(file: fileReference.media) + switch fileReference { + case .story: + userContentType = .story + default: + break + } + + self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, userLocation: userLocation, userContentType: userContentType, resourceReference: fileReference.resourceReference(fileReference.media.resource), tempFilePath: tempFilePath, streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, ambient: beginWithAmbientSound, mixWithOthers: mixWithOthers, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession, storeAfterDownload: storeAfterDownload, isAudioVideoMessage: isAudioVideoMessage) var actionAtEndImpl: (() -> Void)? if enableSound && !loopVideo {