mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Additional gallery options
This commit is contained in:
parent
90650e71c7
commit
a7140ac206
@ -142,7 +142,38 @@ private class Scroller: UIScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
private final class ImageCache: Equatable {
|
||||
static func ==(lhs: ImageCache, rhs: ImageCache) -> Bool {
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
private struct FilledCircle: Hashable {
|
||||
var diameter: CGFloat
|
||||
var color: UInt32
|
||||
}
|
||||
|
||||
private var items: [AnyHashable: UIImage] = [:]
|
||||
|
||||
func filledCircle(diameter: CGFloat, color: UIColor) -> UIImage {
|
||||
let key = AnyHashable(FilledCircle(diameter: diameter, color: color.argb))
|
||||
if let image = self.items[key] {
|
||||
return image
|
||||
}
|
||||
let image = generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
})!.stretchableImage(withLeftCapWidth: Int(diameter) / 2, topCapHeight: Int(diameter) / 2)
|
||||
self.items[key] = image
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
private final class DayComponent: Component {
|
||||
typealias EnvironmentType = ImageCache
|
||||
|
||||
let title: String
|
||||
let isCurrent: Bool
|
||||
let isEnabled: Bool
|
||||
@ -198,9 +229,6 @@ private final class DayComponent: Component {
|
||||
private let titleNode: ImmediateTextNode
|
||||
private var mediaPreviewNode: MediaPreviewNode?
|
||||
|
||||
private var currentTheme: PresentationTheme?
|
||||
private var currentDiameter: CGFloat?
|
||||
private var currentIsCurrent: Bool?
|
||||
private var action: (() -> Void)?
|
||||
private var currentMedia: DayMedia?
|
||||
|
||||
@ -227,40 +255,24 @@ private final class DayComponent: Component {
|
||||
self.action?()
|
||||
}
|
||||
|
||||
func update(component: DayComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(component: DayComponent, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
|
||||
self.action = component.action
|
||||
|
||||
let shadowInset: CGFloat = 0.0
|
||||
let diameter = min(availableSize.width, availableSize.height)
|
||||
|
||||
var updated = false
|
||||
if self.currentTheme !== component.theme || self.currentIsCurrent != component.isCurrent {
|
||||
updated = true
|
||||
}
|
||||
|
||||
if self.currentDiameter != diameter || updated {
|
||||
self.currentDiameter = diameter
|
||||
self.currentTheme = component.theme
|
||||
self.currentIsCurrent = component.isCurrent
|
||||
|
||||
if component.isCurrent || component.media != nil {
|
||||
self.highlightNode.image = generateImage(CGSize(width: diameter + shadowInset * 2.0, height: diameter + shadowInset * 2.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
if component.media != nil {
|
||||
context.setFillColor(UIColor(white: 0.0, alpha: 0.2).cgColor)
|
||||
} else {
|
||||
context.setFillColor(component.theme.list.itemAccentColor.cgColor)
|
||||
}
|
||||
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: CGSize(width: size.width - shadowInset * 2.0, height: size.height - shadowInset * 2.0)))
|
||||
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowInset * 2.0) / 2, topCapHeight: Int(diameter + shadowInset * 2.0) / 2)
|
||||
} else {
|
||||
self.highlightNode.image = nil
|
||||
}
|
||||
let imageCache = environment[ImageCache.self]
|
||||
if component.media != nil {
|
||||
self.highlightNode.image = imageCache.value.filledCircle(diameter: diameter, color: UIColor(white: 0.0, alpha: 0.2))
|
||||
} else if component.isCurrent {
|
||||
self.highlightNode.image = imageCache.value.filledCircle(diameter: diameter, color: component.theme.list.itemAccentColor)
|
||||
} else {
|
||||
self.highlightNode.image = nil
|
||||
}
|
||||
|
||||
if self.currentMedia != component.media {
|
||||
self.currentMedia = component.media
|
||||
|
||||
if let mediaPreviewNode = self.mediaPreviewNode {
|
||||
self.mediaPreviewNode = nil
|
||||
mediaPreviewNode.removeFromSupernode()
|
||||
@ -308,12 +320,14 @@ private final class DayComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MonthComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ImageCache
|
||||
|
||||
let context: AccountContext
|
||||
let model: MonthModel
|
||||
let foregroundColor: UIColor
|
||||
@ -359,7 +373,7 @@ private final class MonthComponent: CombinedComponent {
|
||||
static var body: Body {
|
||||
let title = Child(Text.self)
|
||||
let weekdayTitles = ChildMap(environment: Empty.self, keyedBy: Int.self)
|
||||
let days = ChildMap(environment: Empty.self, keyedBy: Int.self)
|
||||
let days = ChildMap(environment: ImageCache.self, keyedBy: Int.self)
|
||||
|
||||
return { context in
|
||||
let sideInset: CGFloat = 14.0
|
||||
@ -423,6 +437,9 @@ private final class MonthComponent: CombinedComponent {
|
||||
navigateToDay(dayTimestamp)
|
||||
}
|
||||
)),
|
||||
environment: {
|
||||
context.environment[ImageCache.self]
|
||||
},
|
||||
availableSize: CGSize(width: weekdaySize, height: weekdaySize),
|
||||
transition: .immediate
|
||||
)
|
||||
@ -545,7 +562,9 @@ public final class CalendarMessageScreen: ViewController {
|
||||
|
||||
private var initialMonthIndex: Int = 0
|
||||
private var months: [MonthModel] = []
|
||||
private var monthViews: [Int: ComponentHostView<Empty>] = [:]
|
||||
private var monthViews: [Int: ComponentHostView<ImageCache>] = [:]
|
||||
|
||||
private let imageCache = ImageCache()
|
||||
|
||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||
private var scrollLayout: (width: CGFloat, contentHeight: CGFloat, frames: [Int: CGRect])?
|
||||
@ -642,6 +661,10 @@ public final class CalendarMessageScreen: ViewController {
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let indicator = scrollView.value(forKey: "_verticalScrollIndicator") as? UIView {
|
||||
indicator.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
|
||||
}
|
||||
|
||||
self.updateMonthViews()
|
||||
}
|
||||
|
||||
@ -656,7 +679,8 @@ public final class CalendarMessageScreen: ViewController {
|
||||
var contentHeight: CGFloat = layout.intrinsicInsets.bottom
|
||||
var frames: [Int: CGRect] = [:]
|
||||
|
||||
let measureView = ComponentHostView<Empty>()
|
||||
let measureView = ComponentHostView<ImageCache>()
|
||||
let imageCache = ImageCache()
|
||||
for i in 0 ..< self.months.count {
|
||||
let monthSize = measureView.update(
|
||||
transition: .immediate,
|
||||
@ -669,7 +693,9 @@ public final class CalendarMessageScreen: ViewController {
|
||||
navigateToDay: { _ in
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
environment: {
|
||||
imageCache
|
||||
},
|
||||
containerSize: CGSize(width: layout.size.width, height: 10000.0
|
||||
))
|
||||
let monthFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: monthSize)
|
||||
@ -684,7 +710,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
|
||||
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight))
|
||||
self.scrollView.contentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||
self.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: layout.intrinsicInsets.bottom, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: layout.intrinsicInsets.bottom, left: 0.0, bottom: 0.0, right: layout.size.width - 3.0 - 6.0)
|
||||
|
||||
return true
|
||||
}
|
||||
@ -706,7 +732,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
}
|
||||
validMonths.insert(i)
|
||||
|
||||
let monthView: ComponentHostView<Empty>
|
||||
let monthView: ComponentHostView<ImageCache>
|
||||
if let current = self.monthViews[i] {
|
||||
monthView = current
|
||||
} else {
|
||||
@ -730,7 +756,9 @@ public final class CalendarMessageScreen: ViewController {
|
||||
strongSelf.navigateToDay(timestamp)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
environment: {
|
||||
self.imageCache
|
||||
},
|
||||
containerSize: CGSize(width: width, height: 10000.0
|
||||
))
|
||||
monthView.frame = monthFrame
|
||||
|
@ -54,6 +54,7 @@ private func updateChildAnyComponent<EnvironmentType>(
|
||||
let size = component._update(
|
||||
view: view,
|
||||
availableSize: availableSize,
|
||||
environment: context.environment,
|
||||
transition: transition
|
||||
)
|
||||
context.layoutResult.size = size
|
||||
@ -609,7 +610,7 @@ public extension CombinedComponent {
|
||||
return UIView()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
let context = view.getCombinedComponentContext(Self.self)
|
||||
|
||||
let storedBody: Body
|
||||
|
@ -100,7 +100,7 @@ public final class EmptyComponentState: ComponentState {
|
||||
public protocol _TypeErasedComponent {
|
||||
func _makeView() -> UIView
|
||||
func _makeContext() -> _TypeErasedComponentContext
|
||||
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize
|
||||
func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize
|
||||
func _isEqual(to other: _TypeErasedComponent) -> Bool
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ public protocol Component: _TypeErasedComponent, Equatable {
|
||||
|
||||
func makeView() -> View
|
||||
func makeState() -> State
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize
|
||||
}
|
||||
|
||||
public extension Component {
|
||||
@ -123,8 +123,8 @@ public extension Component {
|
||||
return ComponentContext<Self>(component: self, environment: Environment<EnvironmentType>(), state: self.makeState())
|
||||
}
|
||||
|
||||
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return self.update(view: view as! Self.View, availableSize: availableSize, transition: transition)
|
||||
func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize {
|
||||
return self.update(view: view as! Self.View, availableSize: availableSize, environment: environment as! Environment<EnvironmentType>, transition: transition)
|
||||
}
|
||||
|
||||
func _isEqual(to other: _TypeErasedComponent) -> Bool {
|
||||
@ -173,8 +173,8 @@ public class AnyComponent<EnvironmentType>: _TypeErasedComponent, Equatable {
|
||||
return self.wrapped._makeContext()
|
||||
}
|
||||
|
||||
public func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return self.wrapped._update(view: view, availableSize: availableSize, transition: transition)
|
||||
public func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize {
|
||||
return self.wrapped._update(view: view, availableSize: availableSize, environment: environment as! Environment<EnvironmentType>, transition: transition)
|
||||
}
|
||||
|
||||
public func _isEqual(to other: _TypeErasedComponent) -> Bool {
|
||||
|
@ -55,7 +55,7 @@ public class _EnvironmentValue {
|
||||
public final class EnvironmentValue<T: Equatable>: _EnvironmentValue, Equatable {
|
||||
private var storage: EnvironmentValueStorage<T>
|
||||
|
||||
fileprivate var value: T {
|
||||
public var value: T {
|
||||
switch self.storage {
|
||||
case let .direct(value):
|
||||
return value
|
||||
|
@ -25,7 +25,7 @@ public final class Rectangle: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
public func update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
public func update(view: UIView, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
var size = availableSize
|
||||
if let width = self.width {
|
||||
size.width = min(size.width, width)
|
||||
|
@ -95,7 +95,7 @@ public final class Text: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
public func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize)
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public final class ComponentHostView<EnvironmentType>: UIView {
|
||||
} as () -> Environment<EnvironmentType>, updateEnvironment: false, containerSize: containerSize)
|
||||
}
|
||||
|
||||
let updatedSize = component._update(view: componentView, availableSize: containerSize, transition: transition)
|
||||
let updatedSize = component._update(view: componentView, availableSize: containerSize, environment: context.erasedEnvironment, transition: transition)
|
||||
transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(), size: updatedSize))
|
||||
|
||||
self.isUpdating = false
|
||||
|
@ -37,7 +37,7 @@ private final class RoundedRectangle: Component {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(component: RoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(component: RoundedRectangle, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
let shadowInset: CGFloat = 0.0
|
||||
let diameter = min(availableSize.width, availableSize.height)
|
||||
|
||||
@ -76,8 +76,8 @@ private final class RoundedRectangle: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ private final class ShadowRoundedRectangle: Component {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(component: ShadowRoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(component: ShadowRoundedRectangle, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
let shadowInset: CGFloat = 10.0
|
||||
let diameter = min(availableSize.width, availableSize.height)
|
||||
|
||||
@ -150,8 +150,8 @@ private final class ShadowRoundedRectangle: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,25 +108,20 @@ public final class SparseMessageList {
|
||||
|> map { result -> SparseItems in
|
||||
switch result {
|
||||
case let .searchResultsPositions(totalCount, positions):
|
||||
struct Position {
|
||||
struct Position: Equatable {
|
||||
var id: Int32
|
||||
var date: Int32
|
||||
var offset: Int
|
||||
}
|
||||
let positions: [Position] = positions.sorted(by: { lhs, rhs in
|
||||
switch lhs {
|
||||
case let .searchResultPosition(lhsId, _, _):
|
||||
switch rhs {
|
||||
case let .searchResultPosition(rhsId, _, _):
|
||||
return lhsId > rhsId
|
||||
}
|
||||
}
|
||||
}).map { position -> Position in
|
||||
var positions: [Position] = positions.map { position -> Position in
|
||||
switch position {
|
||||
case let .searchResultPosition(id, date, offset):
|
||||
return Position(id: id, date: date, offset: Int(offset))
|
||||
}
|
||||
}
|
||||
positions.sort(by: { lhs, rhs in
|
||||
return lhs.id > rhs.id
|
||||
})
|
||||
|
||||
var result = SparseItems(items: [])
|
||||
for i in 0 ..< positions.count {
|
||||
|
@ -22,6 +22,7 @@ public enum PresentationResourceKey: Int32 {
|
||||
case navigationCompactSearchIcon
|
||||
case navigationCalendarIcon
|
||||
case navigationMoreIcon
|
||||
case navigationMoreCircledIcon
|
||||
case navigationAddIcon
|
||||
case navigationPlayerCloseButton
|
||||
|
||||
|
@ -92,6 +92,12 @@ public struct PresentationResourcesRootController {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public static func navigationMoreCircledIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationMoreCircledIcon.rawValue, { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
})
|
||||
}
|
||||
|
||||
public static func navigationAddIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationAddIcon.rawValue, { theme in
|
||||
|
@ -52,7 +52,7 @@ final class BlurredRoundedRectangle: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ final class RadialProgressComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
@ -308,7 +308,7 @@ final class CheckComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
@ -558,7 +558,7 @@ final class AvatarComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
@ -656,7 +656,7 @@ private final class WallpaperBlurComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
@ -979,7 +979,7 @@ final class OverscrollContentsComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
@ -743,6 +743,10 @@ private func tagMaskForType(_ type: PeerInfoVisualMediaPaneNode.ContentType) ->
|
||||
switch type {
|
||||
case .photoOrVideo:
|
||||
return .photoOrVideo
|
||||
case .photo:
|
||||
return .photo
|
||||
case .video:
|
||||
return .video
|
||||
case .gifs:
|
||||
return .gif
|
||||
}
|
||||
@ -758,11 +762,27 @@ private enum ItemsLayout {
|
||||
let rowCount: Int
|
||||
let contentHeight: CGFloat
|
||||
|
||||
init(containerWidth: CGFloat, itemCount: Int, bottomInset: CGFloat) {
|
||||
init(containerWidth: CGFloat, zoomLevel: PeerInfoVisualMediaPaneNode.ZoomLevel, itemCount: Int, bottomInset: CGFloat) {
|
||||
self.containerWidth = containerWidth
|
||||
self.itemCount = itemCount
|
||||
self.itemSpacing = 1.0
|
||||
self.itemsInRow = max(3, min(6, Int(containerWidth / 140.0)))
|
||||
let minItemsInRow: Int
|
||||
let maxItemsInRow: Int
|
||||
switch zoomLevel {
|
||||
case .level2:
|
||||
minItemsInRow = 2
|
||||
maxItemsInRow = 4
|
||||
case .level3:
|
||||
minItemsInRow = 3
|
||||
maxItemsInRow = 6
|
||||
case .level4:
|
||||
minItemsInRow = 4
|
||||
maxItemsInRow = 8
|
||||
case .level5:
|
||||
minItemsInRow = 5
|
||||
maxItemsInRow = 10
|
||||
}
|
||||
self.itemsInRow = max(minItemsInRow, min(maxItemsInRow, Int(containerWidth / 140.0)))
|
||||
self.itemSize = floor(containerWidth / CGFloat(itemsInRow))
|
||||
|
||||
self.rowCount = itemCount / self.itemsInRow + (itemCount % self.itemsInRow == 0 ? 0 : 1)
|
||||
@ -817,13 +837,48 @@ private enum ItemsLayout {
|
||||
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
||||
enum ContentType {
|
||||
case photoOrVideo
|
||||
case photo
|
||||
case video
|
||||
case gifs
|
||||
}
|
||||
|
||||
enum ZoomLevel {
|
||||
case level2
|
||||
case level3
|
||||
case level4
|
||||
case level5
|
||||
|
||||
func incremented() -> ZoomLevel {
|
||||
switch self {
|
||||
case .level2:
|
||||
return .level3
|
||||
case .level3:
|
||||
return .level4
|
||||
case .level4:
|
||||
return .level5
|
||||
case .level5:
|
||||
return .level5
|
||||
}
|
||||
}
|
||||
|
||||
func decremented() -> ZoomLevel {
|
||||
switch self {
|
||||
case .level2:
|
||||
return .level2
|
||||
case .level3:
|
||||
return .level2
|
||||
case .level4:
|
||||
return .level3
|
||||
case .level5:
|
||||
return .level4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let chatControllerInteraction: ChatControllerInteraction
|
||||
private let contentType: ContentType
|
||||
private(set) var contentType: ContentType
|
||||
|
||||
weak var parentController: ViewController?
|
||||
|
||||
@ -860,8 +915,10 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
private var animationTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private let listSource: SparseMessageList
|
||||
private var listSource: SparseMessageList
|
||||
private var requestedPlaceholderIds = Set<MessageId>()
|
||||
|
||||
private(set) var zoomLevel: ZoomLevel = .level3
|
||||
|
||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
||||
self.context = context
|
||||
@ -908,7 +965,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.addSubnode(self.scrollingArea)
|
||||
|
||||
self.requestHistoryAroundVisiblePosition()
|
||||
self.requestHistoryAroundVisiblePosition(synchronous: false, reloadAtTop: false)
|
||||
|
||||
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||
guard let strongSelf = self else {
|
||||
@ -943,6 +1000,37 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
self.hiddenMediaDisposable?.dispose()
|
||||
self.animationTimer?.invalidate()
|
||||
}
|
||||
|
||||
func updateContentType(contentType: ContentType) {
|
||||
if self.contentType == contentType {
|
||||
return
|
||||
}
|
||||
self.contentType = contentType
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||
self.isRequestingView = false
|
||||
self.requestHistoryAroundVisiblePosition(synchronous: true, reloadAtTop: true)
|
||||
}
|
||||
|
||||
func updateZoomLevel(level: ZoomLevel) {
|
||||
if self.zoomLevel == level {
|
||||
return
|
||||
}
|
||||
self.zoomLevel = level
|
||||
|
||||
self.itemsLayout = nil
|
||||
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
|
||||
if let copyView = self.scrollNode.view.snapshotView(afterScreenUpdates: false) {
|
||||
copyView.backgroundColor = self.context.sharedContext.currentPresentationData.with({ $0 }).theme.list.plainBackgroundColor
|
||||
self.view.addSubview(copyView)
|
||||
copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyView] _ in
|
||||
copyView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: true, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureMessageIsVisible(id: MessageId) {
|
||||
let activeRect = self.scrollNode.bounds
|
||||
@ -959,22 +1047,26 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
}
|
||||
}
|
||||
|
||||
private func requestHistoryAroundVisiblePosition() {
|
||||
private func requestHistoryAroundVisiblePosition(synchronous: Bool, reloadAtTop: Bool) {
|
||||
if self.isRequestingView {
|
||||
return
|
||||
}
|
||||
self.isRequestingView = true
|
||||
var firstTime = true
|
||||
self.listDisposable.set((self.listSource.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] list in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateHistory(list: list)
|
||||
let currentSynchronous = synchronous && firstTime
|
||||
let currentReloadAtTop = reloadAtTop && firstTime
|
||||
firstTime = false
|
||||
strongSelf.updateHistory(list: list, synchronous: currentSynchronous, reloadAtTop: currentReloadAtTop)
|
||||
strongSelf.isRequestingView = false
|
||||
}))
|
||||
}
|
||||
|
||||
private func updateHistory(list: SparseMessageList.State) {
|
||||
private func updateHistory(list: SparseMessageList.State, synchronous: Bool, reloadAtTop: Bool) {
|
||||
self.mediaItems = VisualMediaItemCollection(items: [], totalCount: list.totalCount)
|
||||
for item in list.items {
|
||||
switch item.content {
|
||||
@ -990,7 +1082,21 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
self.isFirstHistoryView = false
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
|
||||
if synchronous {
|
||||
if let copyView = self.scrollNode.view.snapshotView(afterScreenUpdates: false) {
|
||||
copyView.backgroundColor = self.context.sharedContext.currentPresentationData.with({ $0 }).theme.list.plainBackgroundColor
|
||||
self.view.addSubview(copyView)
|
||||
copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyView] _ in
|
||||
copyView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.ignoreScrolling = true
|
||||
if reloadAtTop {
|
||||
self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
|
||||
}
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView || synchronous, transition: .immediate)
|
||||
self.ignoreScrolling = false
|
||||
if !self.didSetReady {
|
||||
self.didSetReady = true
|
||||
self.ready.set(.single(true))
|
||||
@ -1108,8 +1214,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
itemsLayout = current
|
||||
} else {
|
||||
switch self.contentType {
|
||||
case .photoOrVideo, .gifs:
|
||||
itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.totalCount, bottomInset: bottomInset))
|
||||
case .photoOrVideo, .photo, .video, .gifs:
|
||||
itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, zoomLevel: self.zoomLevel, itemCount: self.mediaItems.totalCount, bottomInset: bottomInset))
|
||||
}
|
||||
self.itemsLayout = itemsLayout
|
||||
}
|
||||
@ -1140,8 +1246,12 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
}
|
||||
|
||||
private var previousDidScrollTimestamp: Double = 0.0
|
||||
private var ignoreScrolling: Bool = false
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if self.ignoreScrolling {
|
||||
return
|
||||
}
|
||||
if let (size, sideInset, bottomInset, visibleHeight, _, _, presentationData) = self.currentParams {
|
||||
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false)
|
||||
|
||||
|
@ -916,6 +916,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
let contextSourceNode: ContextReferenceContentNode
|
||||
private let regularTextNode: ImmediateTextNode
|
||||
private let whiteTextNode: ImmediateTextNode
|
||||
private let iconNode: ASImageNode
|
||||
@ -935,6 +936,8 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
var action: (() -> Void)?
|
||||
|
||||
init() {
|
||||
self.contextSourceNode = ContextReferenceContentNode()
|
||||
|
||||
self.regularTextNode = ImmediateTextNode()
|
||||
self.whiteTextNode = ImmediateTextNode()
|
||||
self.whiteTextNode.isHidden = true
|
||||
@ -948,9 +951,11 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityTraits = .button
|
||||
|
||||
self.addSubnode(self.regularTextNode)
|
||||
self.addSubnode(self.whiteTextNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.contextSourceNode.addSubnode(self.regularTextNode)
|
||||
self.contextSourceNode.addSubnode(self.whiteTextNode)
|
||||
self.contextSourceNode.addSubnode(self.iconNode)
|
||||
|
||||
self.addSubnode(self.contextSourceNode)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
@ -983,9 +988,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
text = presentationData.strings.Settings_EditPhoto
|
||||
case .editVideo:
|
||||
text = presentationData.strings.Settings_EditVideo
|
||||
case .calendar:
|
||||
text = ""
|
||||
icon = PresentationResourcesRootController.navigationCalendarIcon(presentationData.theme)
|
||||
case .more:
|
||||
text = ""
|
||||
icon = PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme)
|
||||
}
|
||||
self.accessibilityLabel = text
|
||||
|
||||
@ -1010,9 +1015,13 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
if let image = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
|
||||
return CGSize(width: image.size.width + inset * 2.0, height: height)
|
||||
let size = CGSize(width: image.size.width + inset * 2.0, height: height)
|
||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
return size
|
||||
} else {
|
||||
return CGSize(width: textSize.width + inset * 2.0, height: height)
|
||||
let size = CGSize(width: textSize.width + inset * 2.0, height: height)
|
||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
return size
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1026,7 +1035,7 @@ enum PeerInfoHeaderNavigationButtonKey {
|
||||
case search
|
||||
case editPhoto
|
||||
case editVideo
|
||||
case calendar
|
||||
case more
|
||||
}
|
||||
|
||||
struct PeerInfoHeaderNavigationButtonSpec: Equatable {
|
||||
@ -1049,7 +1058,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var performAction: ((PeerInfoHeaderNavigationButtonKey) -> Void)?
|
||||
var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?) -> Void)?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
@ -1075,7 +1084,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode {
|
||||
self.addSubnode(buttonNode)
|
||||
buttonNode.isWhite = self.isWhite
|
||||
buttonNode.action = { [weak self] in
|
||||
self?.performAction?(spec.key)
|
||||
guard let strongSelf = self, let buttonNode = strongSelf.buttonNodes[spec.key] else {
|
||||
return
|
||||
}
|
||||
strongSelf.performAction?(spec.key, buttonNode.contextSourceNode)
|
||||
}
|
||||
}
|
||||
let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height)
|
||||
|
@ -1834,7 +1834,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start()
|
||||
}
|
||||
})
|
||||
@ -1853,7 +1853,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).start()
|
||||
}
|
||||
})
|
||||
@ -1968,7 +1968,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start()
|
||||
}
|
||||
})
|
||||
@ -1987,7 +1987,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).start()
|
||||
}
|
||||
})
|
||||
@ -2441,7 +2441,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
}
|
||||
|
||||
self.headerNode.navigationButtonContainer.performAction = { [weak self] key in
|
||||
self.headerNode.navigationButtonContainer.performAction = { [weak self] key, source in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -2482,7 +2482,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
strongSelf.view.endEditing(true)
|
||||
if case .done = key {
|
||||
guard let data = strongSelf.data else {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
return
|
||||
}
|
||||
if let peer = data.peer as? TelegramUser {
|
||||
@ -2525,10 +2525,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}))
|
||||
} else {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}
|
||||
} else if data.isContact {
|
||||
let firstName = strongSelf.headerNode.editingContentNode.editingTextForKey(.firstName) ?? ""
|
||||
@ -2559,7 +2559,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}, completed: {
|
||||
dismissStatus?()
|
||||
|
||||
@ -2588,14 +2588,14 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
}
|
||||
}).start()
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}
|
||||
} else {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}
|
||||
} else if let group = data.peer as? TelegramGroup, canEditPeerInfo(context: strongSelf.context, peer: group) {
|
||||
let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? ""
|
||||
@ -2646,14 +2646,14 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}, completed: {
|
||||
dismissStatus?()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}))
|
||||
}
|
||||
} else if let channel = data.peer as? TelegramChannel, canEditPeerInfo(context: strongSelf.context, peer: channel) {
|
||||
@ -2705,21 +2705,21 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}, completed: {
|
||||
dismissStatus?()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
proceed()
|
||||
} else {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}
|
||||
} else {
|
||||
strongSelf.state = strongSelf.state.withIsEditing(false)
|
||||
@ -2748,8 +2748,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
case .search:
|
||||
strongSelf.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
strongSelf.activateSearch()
|
||||
case .calendar:
|
||||
strongSelf.openCalendarSearch()
|
||||
case .more:
|
||||
if let source = source {
|
||||
strongSelf.displayMediaGalleryContextMenu(source: source)
|
||||
}
|
||||
case .editPhoto, .editVideo:
|
||||
break
|
||||
}
|
||||
@ -3001,7 +3003,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
|
||||
@objc private func editingCancelPressed() {
|
||||
self.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
self.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
}
|
||||
|
||||
private func openMessage(id: MessageId) -> Bool {
|
||||
@ -4063,7 +4065,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil)
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
@ -5403,7 +5405,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
case .avatar:
|
||||
self.openAvatarForEditing()
|
||||
case .edit:
|
||||
self.headerNode.navigationButtonContainer.performAction?(.edit)
|
||||
self.headerNode.navigationButtonContainer.performAction?(.edit, nil)
|
||||
case .proxy:
|
||||
self.controller?.push(proxySettingsController(context: self.context))
|
||||
case .savedMessages:
|
||||
@ -5662,7 +5664,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).start()
|
||||
}
|
||||
}))
|
||||
@ -5679,7 +5681,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).start()
|
||||
}
|
||||
}))
|
||||
@ -5795,7 +5797,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root))
|
||||
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
|
||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in
|
||||
return .forward(source: id, grouping: .auto, attributes: [], correlationId: nil)
|
||||
@ -5829,7 +5831,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
})
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
|
||||
if let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
||||
let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId))
|
||||
@ -5946,7 +5948,113 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
}
|
||||
|
||||
private func openCalendarSearch() {
|
||||
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else {
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Zoom In", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let pane = pane else {
|
||||
return
|
||||
}
|
||||
pane.updateZoomLevel(level: pane.zoomLevel.decremented())
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "Zoom Out", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let pane = pane else {
|
||||
return
|
||||
}
|
||||
pane.updateZoomLevel(level: pane.zoomLevel.incremented())
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Search/Calendar"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
self?.openMediaCalendar()
|
||||
})))
|
||||
items.append(.separator)
|
||||
|
||||
let showPhotos: Bool
|
||||
switch pane.contentType {
|
||||
case .photo, .photoOrVideo:
|
||||
showPhotos = true
|
||||
default:
|
||||
showPhotos = false
|
||||
}
|
||||
let showVideos: Bool
|
||||
switch pane.contentType {
|
||||
case .video, .photoOrVideo:
|
||||
showVideos = true
|
||||
default:
|
||||
showVideos = false
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Show Photos", icon: { theme in
|
||||
if !showPhotos {
|
||||
return nil
|
||||
}
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let pane = pane else {
|
||||
return
|
||||
}
|
||||
let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType
|
||||
switch pane.contentType {
|
||||
case .photoOrVideo:
|
||||
updatedContentType = .video
|
||||
case .photo:
|
||||
updatedContentType = .photo
|
||||
case .video:
|
||||
updatedContentType = .photoOrVideo
|
||||
case .gifs:
|
||||
updatedContentType = .gifs
|
||||
}
|
||||
pane.updateContentType(contentType: updatedContentType)
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "Show Videos", icon: { theme in
|
||||
if !showVideos {
|
||||
return nil
|
||||
}
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let pane = pane else {
|
||||
return
|
||||
}
|
||||
let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType
|
||||
switch pane.contentType {
|
||||
case .photoOrVideo:
|
||||
updatedContentType = .photo
|
||||
case .photo:
|
||||
updatedContentType = .photoOrVideo
|
||||
case .video:
|
||||
updatedContentType = .video
|
||||
case .gifs:
|
||||
updatedContentType = .gifs
|
||||
}
|
||||
pane.updateContentType(contentType: updatedContentType)
|
||||
})))
|
||||
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(items: items)), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
private func openMediaCalendar() {
|
||||
var initialTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
|
||||
if let pane = self.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode, let timestamp = pane.currentTopTimestamp() {
|
||||
@ -6156,7 +6264,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { messages in
|
||||
if let strongSelf = self, !messages.isEmpty {
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
|
||||
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil)
|
||||
|
||||
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages.sorted(by: { lhs, rhs in
|
||||
return lhs.index < rhs.index
|
||||
@ -6303,7 +6411,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
case .files, .music, .links, .members:
|
||||
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||
case .media:
|
||||
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .calendar, isForExpandedView: true))
|
||||
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user