Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2022-12-31 18:54:14 +04:00
commit 96b4880961
48 changed files with 2625 additions and 840 deletions

View File

@ -8609,3 +8609,16 @@ Sorry for the inconvenience.";
"GroupMembers.HideMembers" = "Hide Members";
"GroupMembers.MembersHiddenOn" = "Switch this off to show the list of members in this group.";
"GroupMembers.MembersHiddenOff" = "Switch this on to hide the list of members in this group. Admins will remain visible.";
"StorageManagement.ClearCache" = "Clear Cache";
"ChatList.StorageHintTitle" = "Free up to %@";
"ChatList.StorageHintText" = "Clear storage space on your iPhone";
"StorageManagement.PeerShowDetails" = "Show Details";
"StorageManagement.PeerOpenProfile" = "Open Profile";
"StorageManagement.ContextSelect" = "Select";
"StorageManagement.ContextDeselect" = "Deselect";
"StorageManagement.OpenPhoto" = "Open Photo";
"StorageManagement.OpenVideo" = "Open Video";
"StorageManagement.OpenFile" = "Open File";

View File

@ -791,6 +791,7 @@ public protocol SharedAccountContext: AnyObject {
func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
func makeStorageManagementController(context: AccountContext) -> ViewController
func navigateToChatController(_ params: NavigateToChatControllerParams)
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError>

View File

@ -185,7 +185,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
interaction.isInlineMode = isInlineMode
let items = (0 ..< 2).map { _ -> ChatListItem in

View File

@ -2004,6 +2004,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
interaction.openPeer(peer, peer, threadId, false)
self.listNode.clearHighlightAnimated(true)
})
}, openStorageManagement: {
})
chatListInteraction.isSearchMode = true
@ -3205,7 +3206,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
var isInlineMode = false
if case .topics = key {
isInlineMode = false

View File

@ -91,6 +91,7 @@ public final class ChatListNodeInteraction {
let activateChatPreview: (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
let present: (ViewController) -> Void
let openForumThread: (EnginePeer.Id, Int64) -> Void
let openStorageManagement: () -> Void
public var searchTextHighightState: String?
var highlightedChatLocation: ChatListHighlightedLocation?
@ -132,7 +133,8 @@ public final class ChatListNodeInteraction {
hidePsa: @escaping (EnginePeer.Id) -> Void,
activateChatPreview: @escaping (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void,
present: @escaping (ViewController) -> Void,
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void,
openStorageManagement: @escaping () -> Void
) {
self.activateSearch = activateSearch
self.peerSelected = peerSelected
@ -162,6 +164,7 @@ public final class ChatListNodeInteraction {
self.animationCache = animationCache
self.animationRenderer = animationRenderer
self.openForumThread = openForumThread
self.openStorageManagement = openStorageManagement
}
}
@ -567,6 +570,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .StorageInfo(presentationData, sizeFraction):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in
nodeInteraction?.openStorageManagement()
}), directionHint: entry.directionHint)
}
}
}
@ -778,6 +785,10 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .StorageInfo(presentationData, sizeFraction):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in
nodeInteraction?.openStorageManagement()
}), directionHint: entry.directionHint)
case .HeaderEntry:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
case let .AdditionalCategory(index: _, id, title, image, appearance, selected, presentationData):
@ -1258,6 +1269,12 @@ public final class ChatListNode: ListView {
}
self.peerSelected?(peer, threadId, true, true, nil)
})
}, openStorageManagement: { [weak self] in
guard let self else {
return
}
let controller = self.context.sharedContext.makeStorageManagementController(context: self.context)
self.push?(controller)
})
nodeInteraction.isInlineMode = isInlineMode
@ -1323,15 +1340,94 @@ public final class ChatListNode: ListView {
displayArchiveIntro = .single(false)
}
let storageInfo: Signal<Double?, NoError>
if "".isEmpty, case .chatList(groupId: .root) = location, chatListFilter == nil {
let storageBox = context.account.postbox.mediaBox.storageBox
storageInfo = storageBox.totalSize()
|> take(1)
|> mapToSignal { initialSize -> Signal<Double?, NoError> in
let fractionLimit: Double = 0.3
let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
let deviceFreeSpace = (systemAttributes?[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0
let initialFraction: Double
if deviceFreeSpace != 0 && initialSize != 0 {
initialFraction = Double(initialSize) / Double(deviceFreeSpace + initialSize)
} else {
initialFraction = 0.0
}
let initialReportSize: Double?
if initialFraction > fractionLimit {
initialReportSize = Double(initialSize)
} else {
initialReportSize = nil
}
final class ReportState {
var lastSize: Int64
init(lastSize: Int64) {
self.lastSize = lastSize
}
}
let state = Atomic(value: ReportState(lastSize: initialSize))
let updatedReportSize: Signal<Double?, NoError> = Signal { subscriber in
let disposable = storageBox.totalSize().start(next: { size in
let updatedSize = state.with { state -> Int64 in
if abs(initialSize - size) > 50 * 1024 * 1024 {
state.lastSize = size
return size
} else {
return -1
}
}
if updatedSize >= 0 {
let deviceFreeSpace = (systemAttributes?[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0
let updatedFraction: Double
if deviceFreeSpace != 0 && updatedSize != 0 {
updatedFraction = Double(updatedSize) / Double(deviceFreeSpace + updatedSize)
} else {
updatedFraction = 0.0
}
let updatedReportSize: Double?
if updatedFraction > fractionLimit {
updatedReportSize = Double(updatedSize)
} else {
updatedReportSize = nil
}
subscriber.putNext(updatedReportSize)
}
})
return ActionDisposable {
disposable.dispose()
}
}
return .single(initialReportSize)
|> then(
updatedReportSize
)
}
} else {
storageInfo = .single(nil)
}
let currentPeerId: EnginePeer.Id = context.account.peerId
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
let (update, filter) = updateAndFilter
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode, chatListLocation: location)
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, storageInfo: storageInfo, mode: mode, chatListLocation: location)
let entries = rawEntries.filter { entry in
switch entry {
case let .PeerEntry(peerEntry):
@ -2361,7 +2457,7 @@ public final class ChatListNode: ListView {
} else {
break loop
}
case .ArchiveIntro, .HeaderEntry, .AdditionalCategory:
case .ArchiveIntro, .StorageInfo, .HeaderEntry, .AdditionalCategory:
break
}
}

View File

@ -13,6 +13,7 @@ enum ChatListNodeEntryId: Hashable {
case ThreadId(Int64)
case GroupId(EngineChatList.Group)
case ArchiveIntro
case StorageInfo
case additionalCategory(Int)
}
@ -234,6 +235,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
case HoleEntry(EngineMessage.Index, theme: PresentationTheme)
case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool)
case ArchiveIntro(presentationData: ChatListPresentationData)
case StorageInfo(presentationData: ChatListPresentationData, sizeFraction: Double)
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
var sortIndex: ChatListNodeEntrySortIndex {
@ -248,6 +250,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .index(index)
case .ArchiveIntro:
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor))
case .StorageInfo:
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor))
case let .AdditionalCategory(index, _, _, _, _, _, _):
return .additionalCategory(index)
}
@ -270,6 +274,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .GroupId(groupId)
case .ArchiveIntro:
return .ArchiveIntro
case .StorageInfo:
return .StorageInfo
case let .AdditionalCategory(_, id, _, _, _, _, _):
return .additionalCategory(id)
}
@ -342,6 +348,18 @@ enum ChatListNodeEntry: Comparable, Identifiable {
} else {
return false
}
case let .StorageInfo(lhsPresentationData, lhsInfo):
if case let .StorageInfo(rhsPresentationData, rhsInfo) = rhs {
if lhsPresentationData !== rhsPresentationData {
return false
}
if lhsInfo != rhsInfo {
return false
}
return true
} else {
return false
}
case let .AdditionalCategory(lhsIndex, lhsId, lhsTitle, lhsImage, lhsAppearance, lhsSelected, lhsPresentationData):
if case let .AdditionalCategory(rhsIndex, rhsId, rhsTitle, rhsImage, rhsAppearance, rhsSelected, rhsPresentationData) = rhs {
if lhsIndex != rhsIndex {
@ -381,7 +399,7 @@ private func offsetPinnedIndex(_ index: EngineChatList.Item.Index, offset: UInt1
}
}
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) {
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, storageInfo: Double?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) {
var result: [ChatListNodeEntry] = []
var pinnedIndexOffset: UInt16 = 0
@ -643,6 +661,9 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
if displayArchiveIntro {
result.append(.ArchiveIntro(presentationData: state.presentationData))
}
if let storageInfo {
result.append(.StorageInfo(presentationData: state.presentationData, sizeFraction: storageInfo))
}
result.append(.HeaderEntry)
}

View File

@ -0,0 +1,164 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import Display
import SwiftSignalKit
import TelegramPresentationData
import ListSectionHeaderNode
import AppBundle
class ChatListStorageInfoItem: ListViewItem {
let theme: PresentationTheme
let strings: PresentationStrings
let sizeFraction: Double
let action: () -> Void
let selectable: Bool = true
init(theme: PresentationTheme, strings: PresentationStrings, sizeFraction: Double, action: @escaping () -> Void) {
self.theme = theme
self.strings = strings
self.sizeFraction = sizeFraction
self.action = action
}
func selected(listView: ListView) {
listView.clearHighlightAnimated(true)
self.action()
}
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async {
let node = ChatListStorageInfoItemNode()
let (nodeLayout, apply) = node.asyncLayout()(self, params, false)
node.insets = nodeLayout.insets
node.contentSize = nodeLayout.contentSize
Queue.mainQueue().async {
completion(node, {
return (nil, { _ in
apply()
})
})
}
}
}
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async {
assert(node() is ChatListStorageInfoItemNode)
if let nodeValue = node() as? ChatListStorageInfoItemNode {
let layout = nodeValue.asyncLayout()
async {
let (nodeLayout, apply) = layout(self, params, nextItem == nil)
Queue.mainQueue().async {
completion(nodeLayout, { _ in
apply()
})
}
}
}
}
}
}
private let separatorHeight = 1.0 / UIScreen.main.scale
private let titleFont = Font.semibold(15.0)
private let textFont = Font.regular(15.0)
class ChatListStorageInfoItemNode: ListViewItemNode {
private let titleNode: TextNode
private let textNode: TextNode
private let arrowNode: ASImageNode
private let separatorNode: ASDisplayNode
private var item: ChatListStorageInfoItem?
required init() {
self.titleNode = TextNode()
self.textNode = TextNode()
self.arrowNode = ASImageNode()
self.separatorNode = ASDisplayNode()
super.init(layerBacked: false, dynamicBounce: false)
self.addSubnode(self.separatorNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.arrowNode)
}
override func didLoad() {
super.didLoad()
}
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
let layout = self.asyncLayout()
let (_, apply) = layout(item as! ChatListStorageInfoItem, params, nextItem == nil)
apply()
}
func asyncLayout() -> (_ item: ChatListStorageInfoItem, _ params: ListViewItemLayoutParams, _ isLast: Bool) -> (ListViewItemNodeLayout, () -> Void) {
let previousItem = self.item
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeTextLayout = TextNode.asyncLayout(self.textNode)
return { item, params, last in
let baseWidth = params.width - params.leftInset - params.rightInset
let _ = baseWidth
let sideInset: CGFloat = params.leftInset + 16.0
let height: CGFloat = 54.0
let rightInset: CGFloat = sideInset + 24.0
let themeUpdated = item.theme !== previousItem?.theme
let sizeString = dataSizeString(Int64(item.sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: "."))
let rawTitleString = item.strings.ChatList_StorageHintTitle(sizeString)
let titleString = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
if let range = rawTitleString.ranges.first {
titleString.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range)
}
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_StorageHintText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: height), insets: UIEdgeInsets())
return (layout, { [weak self] in
if let strongSelf = self {
strongSelf.item = item
if themeUpdated {
strongSelf.backgroundColor = item.theme.chatList.pinnedItemBackgroundColor
strongSelf.separatorNode.backgroundColor = item.theme.chatList.itemSeparatorColor
strongSelf.arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.theme)
}
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))
let _ = titleLayout.1()
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 9.0), size: titleLayout.0.size)
let _ = textLayout.1()
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: strongSelf.titleNode.frame.maxY - 0.0), size: textLayout.0.size)
if let image = strongSelf.arrowNode.image {
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width + 8.0, y: floor((layout.size.height - image.size.height) / 2.0)), size: image.size)
}
strongSelf.contentSize = layout.contentSize
strongSelf.insets = layout.insets
}
})
}
}
}

View File

@ -91,6 +91,7 @@ public final class HashtagSearchController: TelegramBaseController {
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in
}, openStorageManagement: {
})
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)

View File

@ -193,24 +193,24 @@
}];
[itemViews addObject:galleryItem];
if (_hasSearchButton)
{
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SearchWeb") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
if (strongSelf != nil)
strongSelf.requestSearchController(nil);
}];
[itemViews addObject:viewItem];
}
// if (_hasSearchButton)
// {
// TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SearchWeb") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
// {
// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
// if (strongSelf == nil)
// return;
//
// __strong TGMenuSheetController *strongController = weakController;
// if (strongController == nil)
// return;
//
// [strongController dismissAnimated:true];
// if (strongSelf != nil)
// strongSelf.requestSearchController(nil);
// }];
// [itemViews addObject:viewItem];
// }
if (_hasViewButton)
{

View File

@ -158,6 +158,9 @@ public final class StorageBox {
let contentTypeStatsTable: ValueBoxTable
let metadataTable: ValueBoxTable
let totalSizeSubscribers = Bag<(Int64) -> Void>()
private var totalSize: Int64 = 0
init(queue: Queue, logger: StorageBox.Logger, basePath: String) {
self.queue = queue
self.logger = logger
@ -183,6 +186,8 @@ public final class StorageBox {
self.metadataTable = ValueBoxTable(id: 21, keyType: .binary, compactValuesOnCreation: true)
self.performUpdatesIfNeeded()
self.updateTotalSize()
}
private func performUpdatesIfNeeded() {
@ -230,6 +235,35 @@ public final class StorageBox {
})
}
func updateTotalSize() {
self.valueBox.begin()
var totalSize: Int64 = 0
self.valueBox.scan(self.contentTypeStatsTable, values: { key, value in
var size: Int64 = 0
value.read(&size, offset: 0, length: 8)
totalSize += size
return true
})
self.valueBox.commit()
if self.totalSize != totalSize {
self.totalSize = totalSize
for f in self.totalSizeSubscribers.copyItems() {
f(totalSize)
}
}
}
func incrementalUpdateTotalSize() {
for f in self.totalSizeSubscribers.copyItems() {
f(totalSize)
}
}
func reset() {
self.valueBox.begin()
@ -241,6 +275,8 @@ public final class StorageBox {
self.valueBox.removeAllFromTable(self.metadataTable)
self.valueBox.commit()
self.updateTotalSize()
}
private func internalAddSize(contentType: UInt8, delta: Int64) {
@ -260,6 +296,8 @@ public final class StorageBox {
}
self.valueBox.set(self.contentTypeStatsTable, key: key, value: MemoryBuffer(memory: &currentSize, capacity: 8, length: 8, freeWhenDone: false))
self.totalSize += delta
}
private func internalAddSize(peerId: Int64, contentType: UInt8, delta: Int64) {
@ -390,6 +428,8 @@ public final class StorageBox {
}
self.valueBox.commit()
self.incrementalUpdateTotalSize()
}
private func peerIdsReferencing(hashId: HashId) -> Set<Int64> {
@ -435,6 +475,8 @@ public final class StorageBox {
}
self.valueBox.commit()
self.incrementalUpdateTotalSize()
}
func addEmptyReferencesIfNotReferenced(ids: [(id: Data, size: Int64)], contentType: UInt8) -> Int {
@ -508,6 +550,8 @@ public final class StorageBox {
}
self.valueBox.commit()
self.incrementalUpdateTotalSize()
}
func allPeerIds() -> [PeerId] {
@ -615,6 +659,22 @@ public final class StorageBox {
return (ids, nextId)
}
func subscribeTotalSize(next: @escaping (Int64) -> Void) -> Disposable {
let index = self.totalSizeSubscribers.add(next)
next(self.totalSize)
let queue = self.queue
return ActionDisposable { [weak self] in
queue.async {
guard let self else {
return
}
self.totalSizeSubscribers.remove(index)
}
}
}
func all() -> [Entry] {
var result: [Entry] = []
@ -825,6 +885,8 @@ public final class StorageBox {
self.valueBox.commit()
self.incrementalUpdateTotalSize()
return Array(resultIds)
}
@ -854,6 +916,8 @@ public final class StorageBox {
self.valueBox.commit()
self.incrementalUpdateTotalSize()
return Array(resultIds)
}
}
@ -973,4 +1037,12 @@ public final class StorageBox {
return EmptyDisposable
}
}
public func totalSize() -> Signal<Int64, NoError> {
return self.impl.signalWith { impl, subscriber in
return impl.subscribeTotalSize(next: { value in
subscriber.putNext(value)
})
}
}
}

View File

@ -598,7 +598,6 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
return storageUsageExceptionsScreen(context: context, category: category)
}))
//pushControllerImpl?(storageUsageController(context: context, cacheUsagePromise: cacheUsagePromise))
}, openNetworkUsage: {
pushControllerImpl?(networkUsageStatsController(context: context))
}, openProxy: {

View File

@ -222,7 +222,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)

View File

@ -843,7 +843,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in })
}, openForumThread: { _, _ in },
openStorageManagement: {})
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
func makeChatListItem(

View File

@ -367,7 +367,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in })
}, openForumThread: { _, _ in }, openStorageManagement: {})
func makeChatListItem(
peer: EnginePeer,

View File

@ -20,6 +20,7 @@ enum PendingMessageReuploadInfo {
struct PendingMessageUploadedContentAndReuploadInfo {
let content: PendingMessageUploadedContent
let reuploadInfo: PendingMessageReuploadInfo?
let cacheReferenceKey: CachedSentMediaReferenceKey?
}
enum PendingMessageUploadedContentResult {
@ -81,25 +82,25 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
}
if let media = media.first as? TelegramMediaAction, media.action == .historyScreenshot {
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .messageScreenshot, reuploadInfo: nil)), .none)
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .messageScreenshot, reuploadInfo: nil, cacheReferenceKey: nil)), .none)
} else if let forwardInfo = forwardInfo {
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil)), .text)
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil)), .text)
} else if let contextResult = contextResult {
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .chatContextResult(contextResult), reuploadInfo: nil)), .text)
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .chatContextResult(contextResult), reuploadInfo: nil, cacheReferenceKey: nil)), .text)
} else if let media = media.first, let mediaResult = mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) {
return .signal(mediaResult, .media)
} else {
return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil))), .text)
return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text)
}
}
func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
if let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil, cacheReferenceKey: nil)))
}
if peerId.namespace != Namespaces.Peer.SecretChat, let reference = image.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else {
return uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, image: image, messageId: messageId, text: text, attributes: attributes, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute)
}
@ -109,7 +110,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
for attribute in file.attributes {
if case let .Sticker(_, packReferenceValue, _) = attribute {
if let _ = packReferenceValue {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: PendingMessageUploadedContent.text(text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: PendingMessageUploadedContent.text(text), reuploadInfo: nil, cacheReferenceKey: nil)))
}
}
}
@ -128,7 +129,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
}
|> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let validatedResource = validatedResource.updatedResource as? TelegramCloudMediaResourceWithFileReference, let reference = validatedResource.fileReference {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else {
return .fail(.generic)
}
@ -144,14 +145,14 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
}
}
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil, cacheReferenceKey: nil)))
}
} else {
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
}
} else if let contact = media as? TelegramMediaContact {
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName, vcard: contact.vCardData ?? "")
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else if let map = media as? TelegramMediaMap {
let input: Api.InputMedia
var flags: Int32 = 1 << 1
@ -172,7 +173,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
} else {
input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })))
}
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else if let poll = media as? TelegramMediaPoll {
if peerId.namespace == Namespaces.Peer.SecretChat {
return .fail(.generic)
@ -209,10 +210,10 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
pollMediaFlags |= 1 << 1
}
let inputPoll = Api.InputMedia.inputMediaPoll(flags: pollMediaFlags, poll: Api.Poll.poll(id: 0, flags: pollFlags, question: poll.text, answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities)
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputPoll, text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputPoll, text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else if let media = media as? TelegramMediaDice {
let input = Api.InputMedia.inputMediaDice(emoticon: media.emoji)
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else {
return nil
}
@ -220,7 +221,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
private enum PredownloadedResource {
case localReference(CachedSentMediaReferenceKey?)
case media(Media)
case media(Media, CachedSentMediaReferenceKey?)
case none
}
@ -256,7 +257,7 @@ private func maybePredownloadedImageResource(postbox: Postbox, peerId: PeerId, r
subscriber.putNext(cachedSentMediaReference(postbox: postbox, key: reference)
|> mapError { _ -> PendingMessageUploadError in } |> map { media -> PredownloadedResource in
if let media = media {
return .media(media)
return .media(media, reference)
} else {
return .localReference(reference)
}
@ -295,7 +296,7 @@ private func maybePredownloadedFileResource(postbox: Postbox, auxiliaryMethods:
}
return cachedSentMediaReference(postbox: postbox, key: reference) |> map { media -> PredownloadedResource in
if let media = media {
return .media(media)
return .media(media, reference)
} else {
return .localReference(reference)
}
@ -320,7 +321,7 @@ private func maybeCacheUploadedResource(postbox: Postbox, key: CachedSentMediaRe
private func uploadedMediaImageContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, image: TelegramMediaImage, messageId: MessageId?, text: String, attributes: [MessageAttribute], autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
guard let largestRepresentation = largestImageRepresentation(image.representations) else {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil)))
}
let predownloadedResource: Signal<PredownloadedResource, PendingMessageUploadError> = maybePredownloadedImageResource(postbox: postbox, peerId: peerId, resource: largestRepresentation.resource, forceRefresh: forceReupload)
@ -328,7 +329,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
var referenceKey: CachedSentMediaReferenceKey?
switch result {
case let .media(media):
case let .media(media, key):
if !forceReupload, let image = media as? TelegramMediaImage, let reference = image.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference {
var flags: Int32 = 0
var ttlSeconds: Int32?
@ -336,11 +337,18 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
flags |= 1 << 0
ttlSeconds = autoclearMessageAttribute.timeout
}
for attribute in attributes {
if let _ = attribute as? MediaSpoilerMessageAttribute {
flags |= 1 << 1
}
}
return .single(.progress(1.0))
|> then(
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)))
)
}
referenceKey = key
case let .localReference(key):
referenceKey = key
case .none:
@ -448,7 +456,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let inputPeer = inputPeer {
if autoclearMessageAttribute != nil {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)))
}
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds)))
@ -466,7 +474,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
if hasSpoiler {
flags |= 1 << 1
}
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil)), media: mediaImage)
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)), media: mediaImage)
}
default:
break
@ -478,7 +486,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
}
}
case let .inputSecretFile(file, size, key):
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil, cacheReferenceKey: nil)))
}
}
}
@ -606,13 +614,21 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
var referenceKey: CachedSentMediaReferenceKey?
switch result {
case let .media(media):
case let .media(media, key):
if !forceReupload, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
var flags: Int32 = 0
for attribute in attributes {
if let _ = attribute as? MediaSpoilerMessageAttribute {
flags |= 1 << 2
}
}
return .single(.progress(1.0))
|> then(
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)))
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
)
}
referenceKey = key
case let .localReference(key):
referenceKey = key
case .none:
@ -779,11 +795,13 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
}
if ttlSeconds != nil {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)))
}
if !isGrouped {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
let resultInfo = PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)
return .single(.content(resultInfo))
}
return postbox.transaction { transaction -> Api.InputPeer? in
@ -800,9 +818,9 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
var flags: Int32 = 0
if hasSpoiler {
flags |= (1 << 1)
flags |= (1 << 2)
}
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)), media: mediaFile)
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)), media: mediaFile)
}
default:
break
@ -818,7 +836,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
}
case let .inputSecretFile(file, size, key):
if case .done = fileAndThumbnailResult {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil, cacheReferenceKey: nil)))
} else {
return .complete()
}

View File

@ -41,7 +41,7 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
}
}
func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, message: Message, result: Api.Updates, accountPeerId: PeerId) -> Signal<Void, NoError> {
func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, message: Message, cacheReferenceKey: CachedSentMediaReferenceKey?, result: Api.Updates, accountPeerId: PeerId) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in
let messageId: Int32?
var apiMessage: Api.Message?
@ -284,6 +284,27 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
}
}
}
if updatedMessage.id.namespace == Namespaces.Message.Cloud, let cacheReferenceKey = cacheReferenceKey {
var storeMedia: Media?
var mediaCount = 0
for media in updatedMessage.media {
if let image = media as? TelegramMediaImage {
storeMedia = image
mediaCount += 1
} else if let file = media as? TelegramMediaFile {
storeMedia = file
mediaCount += 1
}
}
if mediaCount > 1 {
storeMedia = nil
}
if let storeMedia = storeMedia {
storeCachedSentMediaReference(transaction: transaction, key: cacheReferenceKey, media: storeMedia)
}
}
}
for file in sentStickers {
if let entry = CodableEntry(RecentMediaItem(file)) {

View File

@ -28,13 +28,27 @@ enum CachedSentMediaReferenceKey {
}
}
private struct CachedMediaReferenceEntry: Codable {
var data: Data
}
func cachedSentMediaReference(postbox: Postbox, key: CachedSentMediaReferenceKey) -> Signal<Media?, NoError> {
return .single(nil)
/*return postbox.transaction { transaction -> Media? in
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSentMediaReferences, key: key.key)) as? Media
}*/
return postbox.transaction { transaction -> Media? in
guard let entry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSentMediaReferences, key: key.key))?.get(CachedMediaReferenceEntry.self) else {
return nil
}
return PostboxDecoder(buffer: MemoryBuffer(data: entry.data)).decodeRootObject() as? Media
}
}
func storeCachedSentMediaReference(transaction: Transaction, key: CachedSentMediaReferenceKey, media: Media) {
//transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSentMediaReferences, key: key.key), entry: media)
let encoder = PostboxEncoder()
encoder.encodeRootObject(media)
let mediaData = encoder.makeData()
guard let entry = CodableEntry(CachedMediaReferenceEntry(data: mediaData)) else {
return
}
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSentMediaReferences, key: key.key), entry: entry)
}

View File

@ -450,7 +450,7 @@ public final class PendingMessageManager {
}
let sendMessage: Signal<PendingMessageResult, NoError> = strongSelf.sendGroupMessagesContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, accountPeerId: strongSelf.accountPeerId, group: messages.map { data in
let (_, message, forwardInfo) = data
return (message.id, PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil))
return (message.id, PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil))
})
|> map { next -> PendingMessageResult in
return .progress(1.0)
@ -1149,7 +1149,7 @@ public final class PendingMessageManager {
|> mapError { _ -> MTRpcError in
}
case let .result(result):
return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result)
return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, content: content, result: result)
|> mapError { _ -> MTRpcError in
}
}
@ -1232,7 +1232,7 @@ public final class PendingMessageManager {
}
}
private func applySentMessage(postbox: Postbox, stateManager: AccountStateManager, message: Message, result: Api.Updates) -> Signal<Void, NoError> {
private func applySentMessage(postbox: Postbox, stateManager: AccountStateManager, message: Message, content: PendingMessageUploadedContentAndReuploadInfo, result: Api.Updates) -> Signal<Void, NoError> {
var apiMessage: Api.Message?
for resultMessage in result.messages {
if let id = resultMessage.id(namespace: Namespaces.Message.allScheduled.contains(message.id.namespace) ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) {
@ -1249,7 +1249,7 @@ public final class PendingMessageManager {
namespace = id.namespace
}
return applyUpdateMessage(postbox: postbox, stateManager: stateManager, message: message, result: result, accountPeerId: self.accountPeerId)
return applyUpdateMessage(postbox: postbox, stateManager: stateManager, message: message, cacheReferenceKey: content.cacheReferenceKey, result: result, accountPeerId: self.accountPeerId)
|> afterDisposed { [weak self] in
if let strongSelf = self {
strongSelf.queue.async {

View File

@ -351,6 +351,7 @@ swift_library(
"//submodules/TelegramUI/Components/ChatListHeaderComponent",
"//submodules/TelegramUI/Components/ChatInputNode",
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
"//submodules/TelegramUI/Components/StorageUsageScreen",
"//submodules/MediaPasteboardUI:MediaPasteboardUI",
"//submodules/DrawingUI:DrawingUI",
"//submodules/FeaturedStickersScreen:FeaturedStickersScreen",

View File

@ -0,0 +1,20 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "AnimatedTextComponent",
module_name = "AnimatedTextComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Display",
"//submodules/ComponentFlow",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,190 @@
import Foundation
import UIKit
import Display
import ComponentFlow
public final class AnimatedTextComponent: Component {
public struct Item: Equatable {
public enum Content: Equatable {
case text(String)
case number(Int)
}
public var id: AnyHashable
public var isUnbreakable: Bool
public var content: Content
public init(id: AnyHashable, isUnbreakable: Bool = false, content: Content) {
self.id = id
self.isUnbreakable = isUnbreakable
self.content = content
}
}
public let font: UIFont
public let color: UIColor
public let items: [Item]
public init(
font: UIFont,
color: UIColor,
items: [Item]
) {
self.font = font
self.color = color
self.items = items
}
public static func ==(lhs: AnimatedTextComponent, rhs: AnimatedTextComponent) -> Bool {
if lhs.font != rhs.font {
return false
}
if lhs.color != rhs.color {
return false
}
if lhs.items != rhs.items {
return false
}
return true
}
private struct CharacterKey: Hashable {
var itemId: AnyHashable
var index: Int
var value: String
}
public final class View: UIView {
private var characters: [CharacterKey: ComponentView<Empty>] = [:]
private var component: AnimatedTextComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
preconditionFailure()
}
func update(component: AnimatedTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
self.component = component
self.state = state
var size = CGSize()
let delayNorm: CGFloat = 0.002
var validKeys: [CharacterKey] = []
for item in component.items {
var itemText: [String] = []
switch item.content {
case let .text(text):
if item.isUnbreakable {
itemText = [text]
} else {
itemText = text.map(String.init)
}
case let .number(value):
if item.isUnbreakable {
itemText = ["\(value)"]
} else {
itemText = "\(value)".map(String.init)
}
}
var index = 0
for character in itemText {
let characterKey = CharacterKey(itemId: item.id, index: index, value: character)
index += 1
validKeys.append(characterKey)
var characterTransition = transition
let characterView: ComponentView<Empty>
if let current = self.characters[characterKey] {
characterView = current
} else {
characterTransition = .immediate
characterView = ComponentView()
self.characters[characterKey] = characterView
}
let characterSize = characterView.update(
transition: characterTransition,
component: AnyComponent(Text(
text: String(character),
font: component.font,
color: component.color
)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
)
let characterFrame = CGRect(origin: CGPoint(x: size.width, y: 0.0), size: characterSize)
if let characterComponentView = characterView.view {
var animateIn = false
if characterComponentView.superview == nil {
self.addSubview(characterComponentView)
animateIn = true
}
if characterComponentView.frame != characterFrame {
if characterTransition.animation.isImmediate {
characterComponentView.frame = characterFrame
} else {
characterComponentView.bounds = CGRect(origin: CGPoint(), size: characterFrame.size)
let deltaPosition = CGPoint(x: characterFrame.midX - characterComponentView.frame.midX, y: characterFrame.midY - characterComponentView.frame.midY)
characterComponentView.center = characterFrame.center
characterComponentView.layer.animatePosition(from: CGPoint(x: -deltaPosition.x, y: -deltaPosition.y), to: CGPoint(), duration: 0.4, delay: delayNorm * size.width, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}
characterTransition.setFrame(view: characterComponentView, frame: characterFrame)
if animateIn, !transition.animation.isImmediate {
characterComponentView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, delay: delayNorm * size.width, timingFunction: kCAMediaTimingFunctionSpring)
//characterComponentView.layer.animateSpring(from: (characterSize.height * 0.5) as NSNumber, to: 0.0 as NSNumber, keyPath: "position.y", duration: 0.5, additive: true)
characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * 0.5), to: CGPoint(), duration: 0.4, delay: delayNorm * size.width, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
characterComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18, delay: delayNorm * size.width)
}
}
size.height = max(size.height, characterSize.height)
size.width += max(0.0, characterSize.width - UIScreenPixel * 2.0)
}
}
var removedKeys: [CharacterKey] = []
for (key, characterView) in self.characters {
if !validKeys.contains(key) {
removedKeys.append(key)
if let characterComponentView = characterView.view {
if !transition.animation.isImmediate {
characterComponentView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.4, delay: delayNorm * characterComponentView.frame.minX, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
characterComponentView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -characterComponentView.bounds.height * 0.4), duration: 0.4, delay: delayNorm * characterComponentView.frame.minX, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
characterComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, delay: delayNorm * characterComponentView.frame.minX, removeOnCompletion: false, completion: { [weak characterComponentView] _ in
characterComponentView?.removeFromSuperview()
})
} else {
characterComponentView.removeFromSuperview()
}
}
}
}
for removedKey in removedKeys {
self.characters.removeValue(forKey: removedKey)
}
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -6663,7 +6663,7 @@ public final class EmojiPagerContentComponent: Component {
if hasPremium {
maxTopLineCount = 2
} else {
maxTopLineCount = 5
maxTopLineCount = 6
}
for reactionItem in topReactionItems {
@ -6721,7 +6721,7 @@ public final class EmojiPagerContentComponent: Component {
if hasPremium {
maxRecentLineCount = 10
} else {
maxRecentLineCount = 5
maxRecentLineCount = 10
}
let popularTitle = hasRecent ? strings.Chat_ReactionSection_Recent : strings.Chat_ReactionSection_Popular

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/CheckNode",
"//submodules/Markdown",
"//submodules/ContextUI",

View File

@ -40,6 +40,7 @@ final class StorageCategoriesComponent: Component {
let strings: PresentationStrings
let categories: [CategoryData]
let isOtherExpanded: Bool
let displayAction: Bool
let toggleCategorySelection: (StorageUsageScreenComponent.Category) -> Void
let toggleOtherExpanded: () -> Void
let clearAction: () -> Void
@ -49,6 +50,7 @@ final class StorageCategoriesComponent: Component {
strings: PresentationStrings,
categories: [CategoryData],
isOtherExpanded: Bool,
displayAction: Bool,
toggleCategorySelection: @escaping (StorageUsageScreenComponent.Category) -> Void,
toggleOtherExpanded: @escaping () -> Void,
clearAction: @escaping () -> Void
@ -57,6 +59,7 @@ final class StorageCategoriesComponent: Component {
self.strings = strings
self.categories = categories
self.isOtherExpanded = isOtherExpanded
self.displayAction = displayAction
self.toggleCategorySelection = toggleCategorySelection
self.toggleOtherExpanded = toggleOtherExpanded
self.clearAction = clearAction
@ -75,6 +78,9 @@ final class StorageCategoriesComponent: Component {
if lhs.isOtherExpanded != rhs.isOtherExpanded {
return false
}
if lhs.displayAction != rhs.displayAction {
return false
}
return true
}
@ -195,6 +201,7 @@ final class StorageCategoriesComponent: Component {
self.itemViews.removeValue(forKey: key)
}
if component.displayAction {
let clearTitle: String
let label: String?
if totalSelectedSize == 0 {
@ -239,7 +246,7 @@ final class StorageCategoriesComponent: Component {
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 50.0)
)
let buttonFrame = CGRect(origin: CGPoint(x: 16.0, y: contentHeight), size: buttonSize)
if let buttonView = button.view {
if let buttonView = self.button.view {
if buttonView.superview == nil {
self.addSubview(buttonView)
}
@ -248,6 +255,11 @@ final class StorageCategoriesComponent: Component {
contentHeight += buttonSize.height
contentHeight += 16.0
} else {
if let buttonView = self.button.view {
buttonView.removeFromSuperview()
}
}
self.backgroundColor = component.theme.list.itemBlocksBackgroundColor

View File

@ -206,14 +206,18 @@ final class StorageCategoryItemComponent: Component {
iconView.removeFromSuperview()
}
let fractionValue: Double = floor(component.category.sizeFraction * 100.0 * 10.0) / 10.0
let fractionString: String
if component.category.sizeFraction != 0.0 {
let fractionValue: Double = floor(component.category.sizeFraction * 100.0 * 10.0) / 10.0
if fractionValue < 0.1 {
fractionString = "<0.1"
fractionString = "<0.1%"
} else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 {
fractionString = "\(Int(fractionValue))"
fractionString = "\(Int(fractionValue))%"
} else {
fractionString = "\(fractionValue)"
fractionString = "\(fractionValue)%"
}
} else {
fractionString = ""
}
let labelSize = self.label.update(
@ -225,8 +229,8 @@ final class StorageCategoryItemComponent: Component {
availableWidth = max(1.0, availableWidth - labelSize.width - 1.0)
let titleValueSize = self.titleValue.update(
transition: transition,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(fractionString)%", font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))),
transition: .immediate,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: fractionString, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))),
environment: {},
containerSize: CGSize(width: availableWidth, height: 100.0)
)
@ -266,8 +270,13 @@ final class StorageCategoryItemComponent: Component {
titleValueView.isUserInteractionEnabled = false
self.addSubview(titleValueView)
}
if titleValueView.bounds.size != titleValueFrame.size {
titleValueView.frame = titleValueFrame
} else {
transition.setFrame(view: titleValueView, frame: titleValueFrame)
}
}
if let labelView = self.label.view {
if labelView.superview == nil {
labelView.isUserInteractionEnabled = false

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Profile Photos.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Profile Photos</title>
<g id="Icon-/-Profile-Photos" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M36,10 C21.648,10 10,21.648 10,36 C10,50.352 21.648,62 36,62 C50.352,62 62,50.352 62,36 C62,21.648 50.352,10 36,10 Z M36,19.8 C40.316,19.8 43.8,23.284 43.8,27.6 C43.8,31.916 40.316,35.4 36,35.4 C31.684,35.4 28.2,31.916 28.2,27.6 C28.2,23.284 31.684,19.8 36,19.8 Z M36,54.72 C29.5,54.72 23.754,51.4742263 20.4,46.5548505 C20.478,42.4839792 29.826,39.72 36,39.72 C42.174,39.72 51.522,42.4839792 51.6,46.5548505 C48.246,51.4742263 42.5,54.72 36,54.72 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 860 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Documents.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Documents</title>
<g id="Icon-/-Documents" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M43.15125,14.3808511 C42.24875,13.4914894 41.0375,13 39.77875,13 L24.7,13 C21.1375,13 18,16.106383 18,19.6170213 L18,50.3829787 C18,53.893617 21.11375,57 24.67625,57 L49.3,57 C52.8625,57 56,53.893617 56,50.3829787 L56,28.9851064 C56,27.7446809 55.50125,26.5510638 54.59875,25.6851064 L43.15125,14.3808511 Z M39.375,27.0425532 L39.375,16.5106383 L52.4375,29.3829787 L41.75,29.3829787 C40.44375,29.3829787 39.375,28.3297872 39.375,27.0425532 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 842 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Music.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Music</title>
<g id="Icon-/-Music" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M36,10 C50.352,10 62,21.648 62,36 C62,50.352 50.352,62 36,62 C21.648,62 10,50.352 10,36 C10,21.648 21.648,10 36,10 Z M33.1111111,23.950754 C31.5156218,23.950754 30.2222222,25.2441536 30.2222222,26.8396429 L30.2222222,45.6296296 C30.2222222,46.2546991 30.4249583,46.8629074 30.8,47.362963 C31.7572936,48.6393544 33.568053,48.8980343 34.8444444,47.9407407 L46.5881862,39.1329344 C46.7600033,39.0040716 46.9169145,38.8564504 47.0560109,38.6928077 C48.089327,37.4771416 47.9415032,35.6539814 46.7258372,34.6206653 L34.9820954,24.6384848 C34.4597827,24.194519 33.7966155,23.950754 33.1111111,23.950754 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 991 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Other.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Other</title>
<g id="Icon-/-Other" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M31.2684326,15 L17.9882353,15 C15.2447059,15 13.0249412,17.25 13.0249412,20 L13,49.0697674 C13,52.75 15.2447059,55 18.9294118,55 L55.0705882,55 C58.7552941,55 61,52.75 61,49.0697674 L61,25.9302326 C61,22.25 58.7552941,20.5813953 55.0705882,20.5813953 L40.0468122,20.5813953 C38.7070557,20.5813953 37.430813,20.0103436 36.5380314,19.0113998 L34.0754572,16.2559964 C33.361232,15.4568414 32.3402378,15 31.2684326,15 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 806 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Photos.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Photos</title>
<g id="Icon-/-Photos" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M48,12 C54.627417,12 60,17.372583 60,24 L60,48 C60,54.627417 54.627417,60 48,60 L24,60 C17.372583,60 12,54.627417 12,48 L12,24 C12,17.372583 17.372583,12 24,12 L48,12 Z M44.3651844,34.6306685 C43.8183724,33.9010116 42.7247485,33.9010116 42.1779366,34.6036442 L33.7023513,45.3863509 L27.9608258,38.549196 C27.3866733,37.8735878 26.3477306,37.9006121 25.8282592,38.6032446 L19.9804504,47.2510295 C19.2695949,48.1428323 19.8984286,50.4 21.0467337,50.4 L51.9081103,50.4 C52.9904208,50.4 53.639443,48.3210078 53.0676996,47.374937 L44.3651844,34.6306685 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 924 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Stickers.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Stickers</title>
<g id="Icon-/-Stickers" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M60,35.5 L59.9953966,35.670698 C59.9067791,37.309531 58.5498341,38.6111111 56.8888889,38.6111111 L50.6666667,38.6111111 L50.3912615,38.6142092 C45.9287855,38.7147025 42.0676082,41.2512307 40.0852923,44.9462311 C39.7167534,44.8455073 39.3161253,44.8387068 38.9195771,44.9463572 C37.9769648,45.202247 36.9982344,45.3333333 36,45.3333333 C33.4531941,45.3333333 29.5907927,43.2780139 28.1265456,41.3638425 L27.9058916,41.062472 C27.1856257,40.1544843 25.8744537,39.9480152 24.9057896,40.6151336 C23.8950097,41.3112571 23.6399302,42.6949756 24.3360537,43.7057556 C26.5046744,46.8546196 31.8881746,49.7777778 36,49.7777778 C36.9171651,49.7777778 37.8247038,49.6981543 38.7149707,49.541295 C38.6826363,49.8921863 38.6666667,50.2497837 38.6666667,50.6111111 L38.6666667,56.8888889 L38.6620632,57.0595868 C38.5734457,58.6984199 37.2165008,60 35.5555556,60 L24.4137931,60 C17.5578445,60 12,54.4421555 12,47.5862069 L12,24.4137931 C12,17.5578445 17.5578445,12 24.4137931,12 L47.5862069,12 C54.4421555,12 60,17.5578445 60,24.4137931 L60,35.5 Z M42.6383189,59.9550937 L42.457984,59.9664377 C42.8777423,59.0264139 43.1111111,57.9849045 43.1111111,56.8888889 L43.1111111,50.6111111 L43.1144503,50.384302 C43.2343809,46.3164026 46.569718,43.0555556 50.6666667,43.0555556 L56.8888889,43.0555556 L57.115698,43.0522164 C58.130088,43.0223099 59.0942932,42.7924609 59.9702649,42.4007181 C59.4478271,51.6700337 52.1614105,59.1320852 42.9674765,59.929458 L42.6383189,59.9550937 Z M44.4444444,28 C42.7262252,28 41.3333333,29.790861 41.3333333,32 C41.3333333,34.209139 42.7262252,36 44.4444444,36 C46.1626637,36 47.5555556,34.209139 47.5555556,32 C47.5555556,29.790861 46.1626637,28 44.4444444,28 Z M27.5555556,28 C25.8373363,28 24.4444444,29.790861 24.4444444,32 C24.4444444,34.209139 25.8373363,36 27.5555556,36 C29.2737748,36 30.6666667,34.209139 30.6666667,32 C30.6666667,29.790861 29.2737748,28 27.5555556,28 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Videos.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Videos</title>
<g id="Icon-/-Videos" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M18.5095398,18.85 L36.9719416,18.85 C40.5670612,18.85 43.4814815,21.7644203 43.4814815,25.3595398 L43.4814815,45.6404602 C43.4814815,49.2355797 40.5670612,52.15 36.9719416,52.15 L18.5095398,52.15 C14.9144203,52.15 12,49.2355797 12,45.6404602 L12,25.3595398 C12,21.7644203 14.9144203,18.85 18.5095398,18.85 Z M49.5847133,27.1084323 L56.6902242,21.3231792 C58.0841802,20.1882308 60.1342623,20.3981979 61.2692107,21.7921539 C61.741904,22.3727209 62,23.0984963 62,23.8471598 L62,47.6096485 C62,49.4072083 60.5427899,50.8644185 58.7452301,50.8644185 C58.0575374,50.8644185 57.3875158,50.6465978 56.831289,50.2422056 L49.8668429,45.1788626 C48.1820423,43.9539665 47.1851852,41.9967567 47.1851852,39.9137485 L47.1851852,32.1563935 C47.1851852,30.1985078 48.0664292,28.3446077 49.5847133,27.1084323 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -262,6 +262,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}
}, present: { _ in
}, openForumThread: { _, _ in
}, openStorageManagement: {
})
interaction.searchTextHighightState = searchQuery
self.interaction = interaction

View File

@ -29,6 +29,7 @@ import PremiumUI
import StickerPackPreviewUI
import ChatControllerInteraction
import ChatPresentationInterfaceState
import StorageUsageScreen
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -1424,6 +1425,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return SettingsUI.makePrivacyAndSecurityController(context: context)
}
public func makeStorageManagementController(context: AccountContext) -> ViewController {
return StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { [weak context] category in
guard let context else {
return nil
}
return storageUsageExceptionsScreen(context: context, category: category)
})
}
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController {
let mappedSource: PremiumSource
switch source {

@ -1 +1 @@
Subproject commit 2f773d98bdfd60d55247b2b3adb8ec70a1fc5748
Subproject commit b0f186935c88e09be2106c80b9880ba92fb6b2d3

View File

@ -10,6 +10,7 @@ swift_library(
"-warnings-as-errors",
],
deps = [
"//submodules/Display"
],
visibility = [
"//visibility:public",

View File

@ -8,6 +8,7 @@
import Foundation
import QuartzCore
import UIKit
import Display
// MARK: - LottieBackgroundBehavior
@ -395,31 +396,15 @@ final public class AnimationView: AnimationViewBase {
}
}
private var workaroundDisplayLink: CADisplayLink?
private var workaroundDisplayLink: SharedDisplayLinkDriver.Link?
private var needsWorkaroundDisplayLink: Bool = false {
didSet {
if self.needsWorkaroundDisplayLink != oldValue {
if self.needsWorkaroundDisplayLink {
if workaroundDisplayLink == nil {
class WorkaroundDisplayLinkTarget {
private let f: () -> Void
init(_ f: @escaping () -> Void) {
self.f = f
}
@objc func update() {
self.f()
}
}
/*self.workaroundDisplayLink = CADisplayLink(target: WorkaroundDisplayLinkTarget { [weak self] in
if self.workaroundDisplayLink == nil {
self.workaroundDisplayLink = SharedDisplayLinkDriver.shared.add { [weak self] in
let _ = self?.realtimeAnimationProgress
}, selector: #selector(WorkaroundDisplayLinkTarget.update))
if #available(iOS 15.0, *) {
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
self.workaroundDisplayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
}
self.workaroundDisplayLink?.add(to: .main, forMode: .common)*/
}
} else {
if let workaroundDisplayLink = self.workaroundDisplayLink {

View File

@ -1,5 +1,5 @@
{
"app": "9.3",
"app": "9.3.1",
"bazel": "5.3.1",
"xcode": "14.1"
}