Storage management improvements

This commit is contained in:
Ali
2022-12-27 16:09:37 +04:00
parent ff1e02517e
commit 0874e5bc00
6 changed files with 836 additions and 59 deletions

View File

@@ -38,6 +38,7 @@ swift_library(
"//submodules/AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode",
"//submodules/LegacyComponents",
"//submodules/GalleryData",
],
visibility = [
"//visibility:public",

View File

@@ -151,6 +151,7 @@ private final class FileListItemComponent: Component {
let selectionState: SelectionState
let hasNext: Bool
let action: (EngineMessage.Id) -> Void
let contextAction: (EngineMessage.Id, ContextExtractedContentContainingView, ContextGesture) -> Void
init(
context: AccountContext,
@@ -163,7 +164,8 @@ private final class FileListItemComponent: Component {
sideInset: CGFloat,
selectionState: SelectionState,
hasNext: Bool,
action: @escaping (EngineMessage.Id) -> Void
action: @escaping (EngineMessage.Id) -> Void,
contextAction: @escaping (EngineMessage.Id, ContextExtractedContentContainingView, ContextGesture) -> Void
) {
self.context = context
self.theme = theme
@@ -176,6 +178,7 @@ private final class FileListItemComponent: Component {
self.selectionState = selectionState
self.hasNext = hasNext
self.action = action
self.contextAction = contextAction
}
static func ==(lhs: FileListItemComponent, rhs: FileListItemComponent) -> Bool {
@@ -212,7 +215,10 @@ private final class FileListItemComponent: Component {
return true
}
final class View: HighlightTrackingButton {
final class View: ContextControllerSourceView {
private let extractedContainerView: ContextExtractedContentContainingView
private let containerButton: HighlightTrackingButton
private let title = ComponentView<Empty>()
private let subtitle = ComponentView<Empty>()
private let label = ComponentView<Empty>()
@@ -227,19 +233,53 @@ private final class FileListItemComponent: Component {
private var checkLayer: CheckLayer?
private var isExtractedToContextMenu: Bool = false
private var highlightBackgroundFrame: CGRect?
private var highlightBackgroundLayer: SimpleLayer?
private var component: FileListItemComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
self.extractedContainerView = ContextExtractedContentContainingView()
self.containerButton = HighlightTrackingButton()
self.separatorLayer = SimpleLayer()
super.init(frame: frame)
self.layer.addSublayer(self.separatorLayer)
self.highligthedChanged = { [weak self] isHighlighted in
self.addSubview(self.extractedContainerView)
self.targetViewForActivationProgress = self.extractedContainerView.contentView
self.extractedContainerView.contentView.addSubview(self.containerButton)
self.extractedContainerView.isExtractedToContextPreviewUpdated = { [weak self] value in
guard let self, let component = self.component else {
return
}
self.containerButton.clipsToBounds = value
self.containerButton.backgroundColor = value ? component.theme.list.plainBackgroundColor : nil
self.containerButton.layer.cornerRadius = value ? 10.0 : 0.0
}
self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] value, transition in
guard let self else {
return
}
self.isExtractedToContextMenu = value
let mappedTransition: Transition
if value {
mappedTransition = Transition(transition)
} else {
mappedTransition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
}
self.state?.updated(transition: mappedTransition)
}
self.containerButton.highligthedChanged = { [weak self] isHighlighted in
guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else {
return
}
@@ -267,7 +307,15 @@ private final class FileListItemComponent: Component {
}
}
}
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.activated = { [weak self] gesture, _ in
guard let self, let component = self.component else {
gesture.cancel()
return
}
component.contextAction(component.messageId, self.extractedContainerView, gesture)
}
}
required init?(coder: NSCoder) {
@@ -301,6 +349,11 @@ private final class FileListItemComponent: Component {
}
self.component = component
self.state = state
self.isGestureEnabled = component.selectionState == .none
let contextInset: CGFloat = self.isExtractedToContextMenu ? 12.0 : 0.0
let spacing: CGFloat = 1.0
let height: CGFloat = 52.0
@@ -323,7 +376,7 @@ private final class FileListItemComponent: Component {
} else {
checkLayer = CheckLayer(theme: CheckNodeTheme(theme: component.theme, style: .plain))
self.checkLayer = checkLayer
self.layer.addSublayer(checkLayer)
self.containerButton.layer.addSublayer(checkLayer)
checkLayer.frame = CGRect(origin: CGPoint(x: -checkSize, y: floor((height - checkSize) / 2.0)), size: CGSize(width: checkSize, height: checkSize))
checkLayer.setSelected(isSelected, animated: false)
checkLayer.setNeedsDisplay()
@@ -338,7 +391,8 @@ private final class FileListItemComponent: Component {
}
}
let rightInset: CGFloat = 16.0 + component.sideInset
let rightInset: CGFloat = contextInset * 2.0 + 16.0 + component.sideInset
let verticalInset: CGFloat = 1.0
if case let .fileExtension(text) = component.icon {
let iconView: UIImageView
@@ -347,7 +401,7 @@ private final class FileListItemComponent: Component {
} else {
iconView = UIImageView()
self.iconView = iconView
self.addSubview(iconView)
self.containerButton.addSubview(iconView)
}
let iconText: ComponentView<Empty>
@@ -362,7 +416,7 @@ private final class FileListItemComponent: Component {
iconView.image = extensionImage(fileExtension: "mp3")
}
if let image = iconView.image {
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor(( leftInset - iconLeftInset - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size)
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor((leftInset - iconLeftInset - image.size.width) / 2.0), y: floor((height - verticalInset * 2.0 - image.size.height) / 2.0)), size: image.size)
transition.setFrame(view: iconView, frame: iconFrame)
let iconTextSize = iconText.update(
@@ -377,7 +431,7 @@ private final class FileListItemComponent: Component {
)
if let iconTextView = iconText.view {
if iconTextView.superview == nil {
self.addSubview(iconTextView)
self.containerButton.addSubview(iconTextView)
}
transition.setFrame(view: iconTextView, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floor((iconFrame.width - iconTextSize.width) / 2.0), y: iconFrame.maxY - iconTextSize.height - 4.0), size: iconTextSize))
}
@@ -404,7 +458,7 @@ private final class FileListItemComponent: Component {
iconImageNode = TransformImageNode()
self.iconImageNode = iconImageNode
self.addSubview(iconImageNode.view)
self.containerButton.addSubview(iconImageNode.view)
}
let iconSize = CGSize(width: 40.0, height: 40.0)
@@ -429,7 +483,7 @@ private final class FileListItemComponent: Component {
}
}
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor((leftInset - iconLeftInset - iconSize.width) / 2.0), y: floor((height - iconSize.height) / 2.0)), size: iconSize)
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor((leftInset - iconLeftInset - iconSize.width) / 2.0), y: floor((height - verticalInset * 2.0 - iconSize.height) / 2.0)), size: iconSize)
transition.setFrame(view: iconImageNode.view, frame: iconFrame)
let iconImageLayout = iconImageNode.asyncLayout()
@@ -454,11 +508,11 @@ private final class FileListItemComponent: Component {
} else {
semanticStatusNode = SemanticStatusNode(backgroundNodeColor: .clear, foregroundNodeColor: .white)
self.semanticStatusNode = semanticStatusNode
self.addSubview(semanticStatusNode.view)
self.containerButton.addSubview(semanticStatusNode.view)
}
let iconSize = CGSize(width: 40.0, height: 40.0)
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor((leftInset - iconLeftInset - iconSize.width) / 2.0), y: floor((height - iconSize.height) / 2.0)), size: iconSize)
let iconFrame = CGRect(origin: CGPoint(x: iconLeftInset + floor((leftInset - iconLeftInset - iconSize.width) / 2.0), y: floor((height - verticalInset * 2.0 - iconSize.height) / 2.0)), size: iconSize)
transition.setFrame(view: semanticStatusNode.view, frame: iconFrame)
semanticStatusNode.backgroundNodeColor = component.theme.list.itemCheckColors.fillColor
@@ -507,13 +561,13 @@ private final class FileListItemComponent: Component {
let contentHeight = titleSize.height + spacing + subtitleSize.height
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - contentHeight) / 2.0)), size: titleSize)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - verticalInset * 2.0 - contentHeight) / 2.0)), size: titleSize)
let subtitleFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + spacing), size: subtitleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.isUserInteractionEnabled = false
self.addSubview(titleView)
self.containerButton.addSubview(titleView)
}
titleView.frame = titleFrame
if let previousTitleFrame, previousTitleFrame.origin.x != titleFrame.origin.x {
@@ -522,7 +576,7 @@ private final class FileListItemComponent: Component {
if let previousTitleFrame, let previousTitleContents, previousTitleFrame.size != titleSize {
previousTitleContents.frame = CGRect(origin: previousTitleFrame.origin, size: previousTitleFrame.size)
self.addSubview(previousTitleContents)
self.containerButton.addSubview(previousTitleContents)
transition.setFrame(view: previousTitleContents, frame: CGRect(origin: titleFrame.origin, size: previousTitleFrame.size))
transition.setAlpha(view: previousTitleContents, alpha: 0.0, completion: { [weak previousTitleContents] _ in
@@ -534,14 +588,14 @@ private final class FileListItemComponent: Component {
if let subtitleView = self.subtitle.view {
if subtitleView.superview == nil {
subtitleView.isUserInteractionEnabled = false
self.addSubview(subtitleView)
self.containerButton.addSubview(subtitleView)
}
transition.setFrame(view: subtitleView, frame: subtitleFrame)
}
if let labelView = self.label.view {
if labelView.superview == nil {
labelView.isUserInteractionEnabled = false
self.addSubview(labelView)
self.containerButton.addSubview(labelView)
}
transition.setFrame(view: labelView, frame: CGRect(origin: CGPoint(x: availableSize.width - rightInset - labelSize.width, y: floor((height - labelSize.height) / 2.0)), size: labelSize))
}
@@ -554,6 +608,14 @@ private final class FileListItemComponent: Component {
self.highlightBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height + ((component.hasNext) ? UIScreenPixel : 0.0)))
let resultBounds = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height))
transition.setFrame(view: self.extractedContainerView, frame: resultBounds)
transition.setFrame(view: self.extractedContainerView.contentView, frame: resultBounds)
self.extractedContainerView.contentRect = resultBounds
let containerFrame = CGRect(origin: CGPoint(x: contextInset, y: verticalInset), size: CGSize(width: availableSize.width - contextInset * 2.0, height: height - verticalInset * 2.0))
transition.setFrame(view: self.containerButton, frame: containerFrame)
return CGSize(width: availableSize.width, height: height)
}
}
@@ -611,18 +673,21 @@ final class StorageFileListPanelComponent: Component {
let context: AccountContext
let items: Items?
let selectionState: StorageUsageScreenComponent.SelectionState?
let peerAction: (EngineMessage.Id) -> Void
let action: (EngineMessage.Id) -> Void
let contextAction: (EngineMessage.Id, ContextExtractedContentContainingView, ContextGesture) -> Void
init(
context: AccountContext,
items: Items?,
selectionState: StorageUsageScreenComponent.SelectionState?,
peerAction: @escaping (EngineMessage.Id) -> Void
action: @escaping (EngineMessage.Id) -> Void,
contextAction: @escaping (EngineMessage.Id, ContextExtractedContentContainingView, ContextGesture) -> Void
) {
self.context = context
self.items = items
self.selectionState = selectionState
self.peerAction = peerAction
self.action = action
self.contextAction = contextAction
}
static func ==(lhs: StorageFileListPanelComponent, rhs: StorageFileListPanelComponent) -> Bool {
@@ -681,8 +746,14 @@ final class StorageFileListPanelComponent: Component {
}
}
private final class ScrollViewImpl: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
return true
}
}
class View: UIView, UIScrollViewDelegate {
private let scrollView: UIScrollView
private let scrollView: ScrollViewImpl
private let measureItem = ComponentView<Empty>()
private var visibleItems: [EngineMessage.Id: ComponentView<Empty>] = [:]
@@ -694,7 +765,7 @@ final class StorageFileListPanelComponent: Component {
private var itemLayout: ItemLayout?
override init(frame: CGRect) {
self.scrollView = UIScrollView()
self.scrollView = ScrollViewImpl()
super.init(frame: frame)
@@ -726,6 +797,10 @@ final class StorageFileListPanelComponent: Component {
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
cancelContextGestures(view: scrollView)
}
private func updateScrolling(transition: Transition) {
guard let component = self.component, let environment = self.environment, let items = component.items, let itemLayout = self.itemLayout else {
return
@@ -898,7 +973,8 @@ final class StorageFileListPanelComponent: Component {
sideInset: environment.containerInsets.left,
selectionState: itemSelectionState,
hasNext: index != items.items.count - 1,
action: component.peerAction
action: component.action,
contextAction: component.contextAction
)),
environment: {},
containerSize: CGSize(width: itemLayout.containerWidth, height: itemLayout.itemHeight)
@@ -949,6 +1025,8 @@ final class StorageFileListPanelComponent: Component {
selectionState: .none,
hasNext: false,
action: { _ in
},
contextAction: { _, _, _ in
}
)),
environment: {},

View File

@@ -261,18 +261,21 @@ final class StorageMediaGridPanelComponent: Component {
let context: AccountContext
let items: Items?
let selectionState: StorageUsageScreenComponent.SelectionState?
let peerAction: (EngineMessage.Id) -> Void
let action: (EngineMessage.Id) -> Void
let contextAction: (EngineMessage.Id, UIView, CGRect, ContextGesture) -> Void
init(
context: AccountContext,
items: Items?,
selectionState: StorageUsageScreenComponent.SelectionState?,
peerAction: @escaping (EngineMessage.Id) -> Void
action: @escaping (EngineMessage.Id) -> Void,
contextAction: @escaping (EngineMessage.Id, UIView, CGRect, ContextGesture) -> Void
) {
self.context = context
self.items = items
self.selectionState = selectionState
self.peerAction = peerAction
self.action = action
self.contextAction = contextAction
}
static func ==(lhs: StorageMediaGridPanelComponent, rhs: StorageMediaGridPanelComponent) -> Bool {
@@ -360,7 +363,7 @@ final class StorageMediaGridPanelComponent: Component {
}
}
class View: UIView, UIScrollViewDelegate {
class View: ContextControllerSourceView, UIScrollViewDelegate {
private let scrollView: UIScrollView
private var visibleLayers: [EngineMessage.Id: MediaGridLayer] = [:]
@@ -372,6 +375,8 @@ final class StorageMediaGridPanelComponent: Component {
private var environment: StorageUsagePanelEnvironment?
private var itemLayout: ItemLayout?
private weak var currentGestureItemLayer: MediaGridLayer?
override init(frame: CGRect) {
self.scrollView = UIScrollView()
@@ -395,12 +400,121 @@ final class StorageMediaGridPanelComponent: Component {
self.addSubview(self.scrollView)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.shouldBegin = { [weak self] point in
guard let self else {
return false
}
var itemLayer: MediaGridLayer?
let scrollPoint = self.convert(point, to: self.scrollView)
for (_, itemLayerValue) in self.visibleLayers {
if itemLayerValue.frame.contains(scrollPoint) {
itemLayer = itemLayerValue
break
}
}
guard let itemLayer else {
return false
}
self.currentGestureItemLayer = itemLayer
return true
}
self.customActivationProgress = { [weak self] progress, update in
guard let self, let itemLayer = self.currentGestureItemLayer else {
return
}
let targetContentRect = CGRect(origin: CGPoint(), size: itemLayer.bounds.size)
let scaleSide = itemLayer.bounds.width
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
let originalCenterOffsetX: CGFloat = itemLayer.bounds.width / 2.0 - targetContentRect.midX
let scaledCenterOffsetX: CGFloat = originalCenterOffsetX * currentScale
let originalCenterOffsetY: CGFloat = itemLayer.bounds.height / 2.0 - targetContentRect.midY
let scaledCenterOffsetY: CGFloat = originalCenterOffsetY * currentScale
let scaleMidX: CGFloat = scaledCenterOffsetX - originalCenterOffsetX
let scaleMidY: CGFloat = scaledCenterOffsetY - originalCenterOffsetY
switch update {
case .update:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
itemLayer.transform = sublayerTransform
case .begin:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
itemLayer.transform = sublayerTransform
case .ended:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
let previousTransform = itemLayer.transform
itemLayer.transform = sublayerTransform
itemLayer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "transform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
}
}
self.activated = { [weak self] gesture, _ in
guard let self, let component = self.component, let itemLayer = self.currentGestureItemLayer else {
return
}
self.currentGestureItemLayer = nil
guard let message = itemLayer.message else {
return
}
let rect = self.convert(itemLayer.frame, from: self.scrollView)
component.contextAction(message.id, self, rect, gesture)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
var foundItemLayer: MediaGridLayer?
for (_, itemLayer) in self.visibleLayers {
if let message = itemLayer.message, message.id == messageId {
foundItemLayer = itemLayer
}
}
guard let itemLayer = foundItemLayer else {
return nil
}
let itemFrame = self.convert(itemLayer.frame, from: self.scrollView)
let proxyNode = ASDisplayNode()
proxyNode.frame = itemFrame
if let contents = itemLayer.contents {
if let image = contents as? UIImage {
proxyNode.contents = image.cgImage
} else {
proxyNode.contents = contents
}
}
proxyNode.isHidden = true
self.addSubnode(proxyNode)
let escapeNotification = EscapeNotification {
proxyNode.removeFromSupernode()
}
return (proxyNode, proxyNode.bounds, {
let view = UIView()
view.frame = proxyNode.frame
view.layer.contents = proxyNode.layer.contents
escapeNotification.keep()
return (view, nil)
})
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
guard let component = self.component else {
@@ -409,7 +523,7 @@ final class StorageMediaGridPanelComponent: Component {
let point = recognizer.location(in: self.scrollView)
for (id, itemLayer) in self.visibleLayers {
if itemLayer.frame.contains(point) {
component.peerAction(id)
component.action(id)
break
}
}

View File

@@ -18,6 +18,19 @@ import AvatarNode
private let avatarFont = avatarPlaceholderFont(size: 15.0)
func cancelContextGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
for gesture in gestureRecognizers {
if let gesture = gesture as? ContextGesture {
gesture.cancel()
}
}
}
for subview in view.subviews {
cancelContextGestures(view: subview)
}
}
private final class PeerListItemComponent: Component {
enum SelectionState: Equatable {
case none
@@ -33,6 +46,7 @@ private final class PeerListItemComponent: Component {
let selectionState: SelectionState
let hasNext: Bool
let action: (EnginePeer) -> Void
let contextAction: (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void
init(
context: AccountContext,
@@ -43,7 +57,8 @@ private final class PeerListItemComponent: Component {
label: String,
selectionState: SelectionState,
hasNext: Bool,
action: @escaping (EnginePeer) -> Void
action: @escaping (EnginePeer) -> Void,
contextAction: @escaping (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void
) {
self.context = context
self.theme = theme
@@ -54,6 +69,7 @@ private final class PeerListItemComponent: Component {
self.selectionState = selectionState
self.hasNext = hasNext
self.action = action
self.contextAction = contextAction
}
static func ==(lhs: PeerListItemComponent, rhs: PeerListItemComponent) -> Bool {
@@ -84,7 +100,10 @@ private final class PeerListItemComponent: Component {
return true
}
final class View: HighlightTrackingButton {
final class View: ContextControllerSourceView {
private let extractedContainerView: ContextExtractedContentContainingView
private let containerButton: HighlightTrackingButton
private let title = ComponentView<Empty>()
private let label = ComponentView<Empty>()
private let separatorLayer: SimpleLayer
@@ -92,22 +111,58 @@ private final class PeerListItemComponent: Component {
private var checkLayer: CheckLayer?
private var isExtractedToContextMenu: Bool = false
private var highlightBackgroundFrame: CGRect?
private var highlightBackgroundLayer: SimpleLayer?
private var component: PeerListItemComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
self.separatorLayer = SimpleLayer()
self.extractedContainerView = ContextExtractedContentContainingView()
self.containerButton = HighlightTrackingButton()
self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = true
super.init(frame: frame)
self.layer.addSublayer(self.separatorLayer)
self.layer.addSublayer(self.avatarNode.layer)
self.highligthedChanged = { [weak self] isHighlighted in
self.addSubview(self.extractedContainerView)
self.targetViewForActivationProgress = self.extractedContainerView.contentView
self.extractedContainerView.contentView.addSubview(self.containerButton)
self.containerButton.layer.addSublayer(self.avatarNode.layer)
self.extractedContainerView.isExtractedToContextPreviewUpdated = { [weak self] value in
guard let self, let component = self.component else {
return
}
self.containerButton.clipsToBounds = value
self.containerButton.backgroundColor = value ? component.theme.list.plainBackgroundColor : nil
self.containerButton.layer.cornerRadius = value ? 10.0 : 0.0
}
self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] value, transition in
guard let self else {
return
}
self.isExtractedToContextMenu = value
let mappedTransition: Transition
if value {
mappedTransition = Transition(transition)
} else {
mappedTransition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
}
self.state?.updated(transition: mappedTransition)
}
self.containerButton.highligthedChanged = { [weak self] isHighlighted in
guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else {
return
}
@@ -135,7 +190,15 @@ private final class PeerListItemComponent: Component {
}
}
}
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.activated = { [weak self] gesture, _ in
guard let self, let component = self.component, let peer = component.peer else {
gesture.cancel()
return
}
component.contextAction(peer, self.extractedContainerView, gesture)
}
}
required init?(coder: NSCoder) {
@@ -169,6 +232,11 @@ private final class PeerListItemComponent: Component {
}
self.component = component
self.state = state
self.isGestureEnabled = component.selectionState == .none
let contextInset: CGFloat = self.isExtractedToContextMenu ? 12.0 : 0.0
let height: CGFloat = 52.0
var leftInset: CGFloat = 62.0 + component.sideInset
@@ -190,7 +258,7 @@ private final class PeerListItemComponent: Component {
} else {
checkLayer = CheckLayer(theme: CheckNodeTheme(theme: component.theme, style: .plain))
self.checkLayer = checkLayer
self.layer.addSublayer(checkLayer)
self.containerButton.layer.addSublayer(checkLayer)
checkLayer.frame = CGRect(origin: CGPoint(x: -checkSize, y: floor((height - checkSize) / 2.0)), size: CGSize(width: checkSize, height: checkSize))
checkLayer.setSelected(isSelected, animated: false)
checkLayer.setNeedsDisplay()
@@ -205,7 +273,8 @@ private final class PeerListItemComponent: Component {
}
}
let rightInset: CGFloat = 16.0 + component.sideInset
let rightInset: CGFloat = contextInset * 2.0 + 16.0 + component.sideInset
let verticalInset: CGFloat = 1.0
let avatarSize: CGFloat = 40.0
@@ -248,11 +317,11 @@ private final class PeerListItemComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - leftInset - rightInset - labelSize.width - 4.0, height: 100.0)
)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - verticalInset * 2.0 - titleSize.height) / 2.0)), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.isUserInteractionEnabled = false
self.addSubview(titleView)
self.containerButton.addSubview(titleView)
}
titleView.frame = titleFrame
if let previousTitleFrame, previousTitleFrame.origin.x != titleFrame.origin.x {
@@ -273,9 +342,9 @@ private final class PeerListItemComponent: Component {
if let labelView = self.label.view {
if labelView.superview == nil {
labelView.isUserInteractionEnabled = false
self.addSubview(labelView)
self.containerButton.addSubview(labelView)
}
transition.setFrame(view: labelView, frame: CGRect(origin: CGPoint(x: availableSize.width - rightInset - labelSize.width, y: floor((height - labelSize.height) / 2.0)), size: labelSize))
transition.setFrame(view: labelView, frame: CGRect(origin: CGPoint(x: availableSize.width - rightInset - labelSize.width, y: floor((height - verticalInset * 2.0 - labelSize.height) / 2.0)), size: labelSize))
}
if themeUpdated {
@@ -286,6 +355,14 @@ private final class PeerListItemComponent: Component {
self.highlightBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height + ((component.hasNext) ? UIScreenPixel : 0.0)))
let resultBounds = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height))
transition.setFrame(view: self.extractedContainerView, frame: resultBounds)
transition.setFrame(view: self.extractedContainerView.contentView, frame: resultBounds)
self.extractedContainerView.contentRect = resultBounds
let containerFrame = CGRect(origin: CGPoint(x: contextInset, y: verticalInset), size: CGSize(width: availableSize.width - contextInset * 2.0, height: height - verticalInset * 2.0))
transition.setFrame(view: self.containerButton, frame: containerFrame)
return CGSize(width: availableSize.width, height: height)
}
}
@@ -344,17 +421,20 @@ final class StoragePeerListPanelComponent: Component {
let items: Items?
let selectionState: StorageUsageScreenComponent.SelectionState?
let peerAction: (EnginePeer) -> Void
let contextAction: (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void
init(
context: AccountContext,
items: Items?,
selectionState: StorageUsageScreenComponent.SelectionState?,
peerAction: @escaping (EnginePeer) -> Void
peerAction: @escaping (EnginePeer) -> Void,
contextAction: @escaping (EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void
) {
self.context = context
self.items = items
self.selectionState = selectionState
self.peerAction = peerAction
self.contextAction = contextAction
}
static func ==(lhs: StoragePeerListPanelComponent, rhs: StoragePeerListPanelComponent) -> Bool {
@@ -413,8 +493,14 @@ final class StoragePeerListPanelComponent: Component {
}
}
private final class ScrollViewImpl: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
return true
}
}
class View: UIView, UIScrollViewDelegate {
private let scrollView: UIScrollView
private let scrollView: ScrollViewImpl
private let measureItem = ComponentView<Empty>()
private var visibleItems: [EnginePeer.Id: ComponentView<Empty>] = [:]
@@ -426,7 +512,7 @@ final class StoragePeerListPanelComponent: Component {
private var itemLayout: ItemLayout?
override init(frame: CGRect) {
self.scrollView = UIScrollView()
self.scrollView = ScrollViewImpl()
super.init(frame: frame)
@@ -458,6 +544,10 @@ final class StoragePeerListPanelComponent: Component {
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
cancelContextGestures(view: scrollView)
}
private func updateScrolling(transition: Transition) {
guard let component = self.component, let environment = self.environment, let items = component.items, let itemLayout = self.itemLayout else {
return
@@ -505,7 +595,8 @@ final class StoragePeerListPanelComponent: Component {
label: dataSizeString(item.size, formatting: dataSizeFormatting),
selectionState: itemSelectionState,
hasNext: index != items.items.count - 1,
action: component.peerAction
action: component.peerAction,
contextAction: component.contextAction
)),
environment: {},
containerSize: CGSize(width: itemLayout.containerWidth, height: itemLayout.itemHeight)
@@ -554,6 +645,8 @@ final class StoragePeerListPanelComponent: Component {
selectionState: .none,
hasNext: false,
action: { _ in
},
contextAction: { _, _, _ in
}
)),
environment: {},

View File

@@ -439,6 +439,13 @@ final class StorageUsagePanelContainerComponent: Component {
fatalError("init(coder:) has not been implemented")
}
var currentPanelView: UIView? {
guard let currentId = self.currentId, let panel = self.visiblePanels[currentId] else {
return nil
}
return panel.view
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}

View File

@@ -21,6 +21,7 @@ import UndoUI
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import TelegramStringFormatting
import GalleryData
#if DEBUG
import os.signpost
@@ -1561,6 +1562,88 @@ final class StorageUsageScreenComponent: Component {
} else {
self.openPeer(peer: peer)
}
},
contextAction: { [weak self] peer, sourceView, gesture in
guard let self, let component = self.component else {
return
}
var itemList: [ContextMenuItem] = []
//TODO:localize
itemList.append(.action(ContextMenuActionItem(
text: "Show Details",
icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) },
action: { [weak self] c, _ in
c.dismiss(completion: { [weak self] in
guard let self else {
return
}
self.openPeer(peer: peer)
})
})
))
itemList.append(.action(ContextMenuActionItem(
text: "Open Profile",
icon: { theme in
if case .user = peer {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor)
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Groups"), color: theme.contextMenu.primaryColor)
}
},
action: { [weak self] c, _ in
c.dismiss(completion: { [weak self] in
guard let self, let component = self.component, let controller = self.controller?() else {
return
}
let peerInfoController = component.context.sharedContext.makePeerInfoController(
context: component.context,
updatedPresentationData: nil,
peer: peer._asPeer(),
mode: .generic,
avatarInitiallyExpanded: false,
fromChat: false,
requestsContext: nil
)
if let peerInfoController {
controller.push(peerInfoController)
}
})
})
))
itemList.append(.action(ContextMenuActionItem(
text: "Select",
icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) },
action: { [weak self] c, _ in
c.dismiss(completion: {
})
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
}
self.selectionState = self.selectionState?.togglePeer(id: peer.id)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
})
))
let items = ContextController.Items(content: .list(itemList))
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let controller = ContextController(
account: component.context.account,
presentationData: presentationData,
source: .extracted(StorageUsageListContextExtractedContentSource(contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture)
self.controller?()?.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismiss()
}
return true
})
self.controller?()?.presentInGlobalOverlay(controller)
}
))
))
@@ -1573,15 +1656,25 @@ final class StorageUsageScreenComponent: Component {
context: component.context,
items: self.imageItems,
selectionState: self.selectionState,
peerAction: { [weak self] messageId in
action: { [weak self] messageId in
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
guard let message = self.currentMessages[messageId] else {
return
}
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
if self.selectionState == nil {
self.openMessage(message: message)
} else {
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
},
contextAction: { [weak self] messageId, containerView, sourceRect, gesture in
guard let self else {
return
}
self.messageGaleryContextAction(messageId: messageId, sourceView: containerView, sourceRect: sourceRect, gesture: gesture)
}
))
))
@@ -1594,15 +1687,25 @@ final class StorageUsageScreenComponent: Component {
context: component.context,
items: self.fileItems,
selectionState: self.selectionState,
peerAction: { [weak self] messageId in
action: { [weak self] messageId in
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
guard let message = self.currentMessages[messageId] else {
return
}
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
if self.selectionState == nil {
self.openMessage(message: message)
} else {
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
},
contextAction: { [weak self] messageId, containerView, gesture in
guard let self else {
return
}
self.messageContextAction(messageId: messageId, sourceView: containerView, gesture: gesture)
}
))
))
@@ -1615,15 +1718,30 @@ final class StorageUsageScreenComponent: Component {
context: component.context,
items: self.musicItems,
selectionState: self.selectionState,
peerAction: { [weak self] messageId in
action: { [weak self] messageId in
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
guard let message = self.currentMessages[messageId] else {
return
}
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
if self.selectionState == nil {
//self.openMessage(message: message)
let _ = message
self.selectionState = SelectionState()
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
} else {
self.selectionState = self.selectionState?.toggleMessage(id: messageId)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
},
contextAction: { [weak self] messageId, containerView, gesture in
guard let self else {
return
}
self.messageContextAction(messageId: messageId, sourceView: containerView, gesture: gesture)
}
))
))
@@ -1974,6 +2092,316 @@ final class StorageUsageScreenComponent: Component {
controller.push(childController)
}
private func messageGaleryContextAction(messageId: EngineMessage.Id, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture) {
guard let component = self.component, let message = self.currentMessages[messageId] else {
gesture.cancel()
return
}
let _ = (chatMediaListPreviewControllerData(
context: component.context,
chatLocation: .peer(id: message.id.peerId),
chatLocationContextHolder: nil,
message: message,
standalone: true,
reverseMessageGalleryOrder: false,
navigationController: self.controller?()?.navigationController as? NavigationController
)
|> deliverOnMainQueue).start(next: { [weak self] previewData in
guard let self, let component = self.component, let previewData else {
gesture.cancel()
return
}
let context = component.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let strings = presentationData.strings
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
c.dismiss(completion: { [weak self] in
guard let self, let component = self.component, let controller = self.controller?(), let navigationController = controller.navigationController as? NavigationController else {
return
}
guard let peer = message.peers[message.id.peerId].flatMap(EnginePeer.init) else {
return
}
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
}
component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: component.context,
chatLocation: chatLocation,
subject: .message(id: .id(message.id), highlight: true, timecode: nil),
keepStack: .always
))
})
})))
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuSelect, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] c, _ in
c.dismiss(completion: {
})
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
}
self.selectionState = self.selectionState?.toggleMessage(id: message.id)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
})))
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(
account: component.context.account,
presentationData: presentationData,
source: .controller(StorageUsageListContextGalleryContentSourceImpl(
controller: gallery,
sourceView: sourceView,
sourceRect: sourceRect
)),
items: .single(ContextController.Items(content: .list(items))),
gesture: gesture
)
self.controller?()?.presentInGlobalOverlay(contextController)
case .instantPage:
break
}
})
}
private func messageContextAction(messageId: EngineMessage.Id, sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
guard let component = self.component else {
return
}
guard let message = self.currentMessages[messageId] else {
return
}
//TODO:localize
var openTitle: String = "Open"
var isAudio: Bool = false
for media in message.media {
if let _ = media as? TelegramMediaImage {
openTitle = "Open Photo"
} else if let file = media as? TelegramMediaFile {
if file.isVideo {
openTitle = "Open Video"
} else {
openTitle = "Open File"
}
isAudio = file.isMusic || file.isVoice
}
}
var itemList: [ContextMenuItem] = []
//TODO:localize
if !isAudio {
itemList.append(.action(ContextMenuActionItem(
text: openTitle,
icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Expand"), color: theme.contextMenu.primaryColor) },
action: { [weak self] c, _ in
c.dismiss(completion: { [weak self] in
guard let self else {
return
}
self.openMessage(message: message)
})
})
))
}
itemList.append(.action(ContextMenuActionItem(
text: "View in Chat",
icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)
},
action: { [weak self] c, _ in
c.dismiss(completion: { [weak self] in
guard let self, let component = self.component, let controller = self.controller?(), let navigationController = controller.navigationController as? NavigationController else {
return
}
guard let peer = message.peers[message.id.peerId].flatMap(EnginePeer.init) else {
return
}
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
}
component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: component.context,
chatLocation: chatLocation,
subject: .message(id: .id(message.id), highlight: true, timecode: nil),
keepStack: .always
))
})
})
))
itemList.append(.action(ContextMenuActionItem(
text: "Select",
icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) },
action: { [weak self] c, _ in
c.dismiss(completion: {
})
guard let self else {
return
}
if self.selectionState == nil {
self.selectionState = SelectionState()
}
self.selectionState = self.selectionState?.toggleMessage(id: message.id)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
})
))
let items = ContextController.Items(content: .list(itemList))
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let controller = ContextController(
account: component.context.account,
presentationData: presentationData,
source: .extracted(StorageUsageListContextExtractedContentSource(contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture)
self.controller?()?.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismiss()
}
return true
})
self.controller?()?.presentInGlobalOverlay(controller)
}
private func openMessage(message: Message) {
guard let component = self.component else {
return
}
guard let controller = self.controller?(), let navigationController = controller.navigationController as? NavigationController else {
return
}
let foundGalleryMessage: Message? = message
guard let galleryMessage = foundGalleryMessage else {
return
}
self.endEditing(true)
let _ = component.context.sharedContext.openChatMessage(OpenChatMessageParams(
context: component.context,
chatLocation: .peer(id: message.id.peerId),
chatLocationContextHolder: nil,
message: galleryMessage,
standalone: true,
reverseMessageGalleryOrder: true,
navigationController: navigationController,
dismissInput: { [weak self] in
self?.endEditing(true)
}, present: { [weak self] c, a in
guard let self else {
return
}
self.controller?()?.present(c, in: .window(.root), with: a, blockInteraction: true)
},
transitionNode: { [weak self] messageId, media in
guard let self else {
return nil
}
if let panelContainerView = self.panelContainer.view as? StorageUsagePanelContainerComponent.View {
if let currentPanelView = panelContainerView.currentPanelView as? StorageMediaGridPanelComponent.View {
return currentPanelView.transitionNodeForGallery(messageId: messageId, media: media)
}
}
return nil
}, addToTransitionSurface: { [weak self] view in
guard let self else {
return
}
if let panelContainerView = self.panelContainer.view as? StorageUsagePanelContainerComponent.View {
panelContainerView.currentPanelView?.addSubview(view)
}
}, openUrl: { [weak self] url in
guard let self else {
return
}
let _ = self
}, openPeer: { [weak self] peer, navigation in
guard let self else {
return
}
let _ = self
},
callPeer: { _, _ in
//self?.controllerInteraction?.callPeer(peerId)
},
enqueueMessage: { _ in
},
sendSticker: nil,
sendEmoji: nil,
setupTemporaryHiddenMedia: { _, _, _ in },
chatAvatarHiddenMedia: { _, _ in },
actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
guard let self else {
return
}
let _ = self
//strongSelf.openUrl(url: url, concealed: false, external: false)
}, openUrlIn: { [weak self] url in
guard let self else {
return
}
let _ = self
}, openPeerMention: { [weak self] mention in
guard let self else {
return
}
let _ = self
}, openPeer: { [weak self] peer in
guard let self else {
return
}
let _ = self
}, openHashtag: { [weak self] peerName, hashtag in
guard let self else {
return
}
let _ = self
}, openBotCommand: { _ in
}, addContact: { _ in
}, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
guard let self else {
return
}
let _ = self
}, editMedia: { [weak self] messageId, snapshots, transitionCompletion in
guard let self else {
return
}
let _ = self
}),
centralItemUpdated: { [weak self] messageId in
//let _ = self?.paneContainerNode.requestExpandTabs?()
//self?.paneContainerNode.currentPane?.node.ensureMessageIsVisible(id: messageId)
guard let self else {
return
}
let _ = self
}
))
}
private func requestClear(categories: Set<Category>, peers: Set<PeerId>, messages: Set<EngineMessage.Id>) {
guard let component = self.component else {
return
@@ -2730,3 +3158,59 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
self.descriptionTextNode.frame = descriptionTextFrame
}
}
private final class StorageUsageListContextGalleryContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceView: UIView?
let sourceRect: CGRect
let navigationController: NavigationController? = nil
let passthroughTouches: Bool
init(controller: ViewController, sourceView: UIView?, sourceRect: CGRect = CGRect(origin: CGPoint(), size: CGSize()), passthroughTouches: Bool = false) {
self.controller = controller
self.sourceView = sourceView
self.sourceRect = sourceRect
self.passthroughTouches = passthroughTouches
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceView = self.sourceView
let sourceRect = self.sourceRect
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceView] in
if let sourceView = sourceView {
let rect = sourceRect.isEmpty ? sourceView.bounds : sourceRect
return (sourceView, rect)
} else {
return nil
}
})
}
func animatedIn() {
self.controller.didAppearInContextPreview()
}
}
private final class StorageUsageListContextExtractedContentSource: ContextExtractedContentSource {
let keepInPlace: Bool = false
let ignoreContentTouches: Bool = false
let blurBackground: Bool = true
//let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
private let contentView: ContextExtractedContentContainingView
init(contentView: ContextExtractedContentContainingView) {
self.contentView = contentView
}
func takeView() -> ContextControllerTakeViewInfo? {
return ContextControllerTakeViewInfo(containingItem: .view(self.contentView), contentAreaInScreenSpace: UIScreen.main.bounds)
}
func putBack() -> ContextControllerPutBackViewInfo? {
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
}
}