mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Stories
This commit is contained in:
parent
aab73d6aeb
commit
f305edfb17
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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<Tuple4<Data?, Data?, ChatMessagePhotoQuality, Bool>, 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<Tuple4<Data?, Data?, ChatMessagePhotoQuality, Bool>, 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<Tuple3<Data?, Tuple2<Data, String>?, 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<Tuple3<Data?, Tuple2<Data, String>?, 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<Tuple3<Data?, Tuple2<Data, String>?, 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<Data?, Tuple2<Data, String>?, 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
|
||||
|
@ -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<Data>()
|
||||
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()
|
||||
})
|
||||
|
@ -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<FetchMediaDataState, NoError> = 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<Float, NoError> {
|
||||
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<Float, NoError> {
|
||||
return fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, mediaReference: mediaReference)
|
||||
|> mapToSignal { state, isImage -> Signal<Float, NoError> in
|
||||
switch state {
|
||||
case let .progress(value):
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
@ -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<AllStorageUsageStats, NoError> {
|
||||
/*let additionalStats = Signal<Int64, NoError> { 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<darwin_dirstat>.size)
|
||||
if result != -1 {
|
||||
return Int64(s.total_size)
|
||||
} else {
|
||||
result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ public enum MediaResourceUserContentType: UInt8, Equatable {
|
||||
case sticker = 6
|
||||
case avatar = 7
|
||||
case audioVideoMessage = 8
|
||||
case story = 9
|
||||
}
|
||||
|
||||
public extension MediaResourceUserContentType {
|
||||
|
@ -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<Never, NoError> 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<Never, NoError> 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
|
||||
|
@ -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<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||
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<TelegramAccountManagerTypes>) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.displayChatListArchiveTooltip(), entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<Empty>
|
||||
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 {
|
||||
|
@ -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<Never, NoError> 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<Never, NoError> 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<Never, NoError> 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<Never, NoError> in
|
||||
return .complete()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<MediaEditorScreen.Subject?, NoError> 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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user