mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Shared media improvements
This commit is contained in:
parent
e7c626004c
commit
1a5876f90e
@ -268,6 +268,8 @@ private final class DayComponent: Component {
|
|||||||
private var action: (() -> Void)?
|
private var action: (() -> Void)?
|
||||||
private var currentMedia: DayMedia?
|
private var currentMedia: DayMedia?
|
||||||
|
|
||||||
|
private(set) var index: MessageIndex?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.button = HighlightableButton()
|
self.button = HighlightableButton()
|
||||||
self.highlightView = UIImageView()
|
self.highlightView = UIImageView()
|
||||||
@ -295,6 +297,7 @@ private final class DayComponent: Component {
|
|||||||
|
|
||||||
func update(component: DayComponent, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
|
func update(component: DayComponent, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
|
||||||
self.action = component.action
|
self.action = component.action
|
||||||
|
self.index = component.media?.message.index
|
||||||
|
|
||||||
let diameter = min(availableSize.width, availableSize.height)
|
let diameter = min(availableSize.width, availableSize.height)
|
||||||
let contentFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - diameter) / 2.0), y: floor((availableSize.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter))
|
let contentFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - diameter) / 2.0), y: floor((availableSize.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter))
|
||||||
@ -601,16 +604,18 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
private final class Node: ViewControllerTracingNode, UIScrollViewDelegate {
|
private final class Node: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
|
private let initialTimestamp: Int32
|
||||||
private let navigateToDay: (Int32) -> Void
|
private let navigateToDay: (Int32) -> Void
|
||||||
|
private let previewDay: (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var scrollView: Scroller
|
private var scrollView: Scroller
|
||||||
|
|
||||||
private let calendarSource: SparseMessageCalendar
|
private let calendarSource: SparseMessageCalendar
|
||||||
|
|
||||||
private var initialMonthIndex: Int = 0
|
|
||||||
private var months: [MonthModel] = []
|
private var months: [MonthModel] = []
|
||||||
private var monthViews: [Int: ComponentHostView<ImageCache>] = [:]
|
private var monthViews: [Int: ComponentHostView<ImageCache>] = [:]
|
||||||
|
private let contextGestureContainerNode: ContextControllerSourceNode
|
||||||
|
|
||||||
private let imageCache = ImageCache()
|
private let imageCache = ImageCache()
|
||||||
|
|
||||||
@ -622,14 +627,20 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
private var isLoadingMoreDisposable: Disposable?
|
private var isLoadingMoreDisposable: Disposable?
|
||||||
private var stateDisposable: Disposable?
|
private var stateDisposable: Disposable?
|
||||||
|
|
||||||
init(context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToDay: @escaping (Int32) -> Void) {
|
private weak var currentGestureDayView: DayComponent.View?
|
||||||
|
|
||||||
|
init(context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToDay: @escaping (Int32) -> Void, previewDay: @escaping (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
|
self.initialTimestamp = initialTimestamp
|
||||||
self.calendarSource = calendarSource
|
self.calendarSource = calendarSource
|
||||||
self.navigateToDay = navigateToDay
|
self.navigateToDay = navigateToDay
|
||||||
|
self.previewDay = previewDay
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
self.contextGestureContainerNode = ContextControllerSourceNode()
|
||||||
|
|
||||||
self.scrollView = Scroller()
|
self.scrollView = Scroller()
|
||||||
self.scrollView.showsVerticalScrollIndicator = true
|
self.scrollView.showsVerticalScrollIndicator = true
|
||||||
self.scrollView.showsHorizontalScrollIndicator = false
|
self.scrollView.showsHorizontalScrollIndicator = false
|
||||||
@ -644,6 +655,75 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.contextGestureContainerNode.shouldBegin = { [weak self] point in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let result = strongSelf.contextGestureContainerNode.view.hitTest(point, with: nil) as? HighlightableButton else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let dayView = result.superview as? DayComponent.View else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.currentGestureDayView = dayView
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contextGestureContainerNode.customActivationProgress = { [weak self] progress, update in
|
||||||
|
guard let strongSelf = self, let currentGestureDayView = strongSelf.currentGestureDayView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let itemLayer = currentGestureDayView.layer
|
||||||
|
|
||||||
|
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.sublayerTransform = sublayerTransform
|
||||||
|
case .begin:
|
||||||
|
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
|
||||||
|
itemLayer.sublayerTransform = sublayerTransform
|
||||||
|
case .ended:
|
||||||
|
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
|
||||||
|
let previousTransform = itemLayer.sublayerTransform
|
||||||
|
itemLayer.sublayerTransform = sublayerTransform
|
||||||
|
|
||||||
|
itemLayer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contextGestureContainerNode.activated = { [weak self] gesture, _ in
|
||||||
|
guard let strongSelf = self, let currentGestureDayView = strongSelf.currentGestureDayView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.currentGestureDayView = nil
|
||||||
|
|
||||||
|
currentGestureDayView.isUserInteractionEnabled = false
|
||||||
|
currentGestureDayView.isUserInteractionEnabled = true
|
||||||
|
|
||||||
|
if let index = currentGestureDayView.index {
|
||||||
|
strongSelf.previewDay(index, strongSelf, currentGestureDayView.convert(currentGestureDayView.bounds, to: strongSelf.view), gesture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let calendar = Calendar(identifier: .gregorian)
|
let calendar = Calendar(identifier: .gregorian)
|
||||||
|
|
||||||
let baseDate = Date()
|
let baseDate = Date()
|
||||||
@ -651,8 +731,6 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
let currentMonth = calendar.component(.month, from: baseDate)
|
let currentMonth = calendar.component(.month, from: baseDate)
|
||||||
let currentDayOfMonth = calendar.component(.day, from: baseDate)
|
let currentDayOfMonth = calendar.component(.day, from: baseDate)
|
||||||
|
|
||||||
let initialDate = Date(timeIntervalSince1970: TimeInterval(initialTimestamp))
|
|
||||||
|
|
||||||
for i in 0 ..< 12 * 20 {
|
for i in 0 ..< 12 * 20 {
|
||||||
guard let firstDayOfMonth = calendar.date(from: calendar.dateComponents([.year, .month], from: baseDate)) else {
|
guard let firstDayOfMonth = calendar.date(from: calendar.dateComponents([.year, .month], from: baseDate)) else {
|
||||||
break
|
break
|
||||||
@ -660,10 +738,18 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
guard let monthBaseDate = calendar.date(byAdding: .month, value: -i, to: firstDayOfMonth) else {
|
guard let monthBaseDate = calendar.date(byAdding: .month, value: -i, to: firstDayOfMonth) else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let monthModel = monthMetadata(calendar: calendar, for: monthBaseDate, currentYear: currentYear, currentMonth: currentMonth, currentDayOfMonth: currentDayOfMonth) else {
|
guard let monthModel = monthMetadata(calendar: calendar, for: monthBaseDate, currentYear: currentYear, currentMonth: currentMonth, currentDayOfMonth: currentDayOfMonth) else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let firstDayTimestamp = Int32(monthModel.firstDay.timeIntervalSince1970)
|
||||||
|
let lastDayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(monthModel.numberOfDays)
|
||||||
|
|
||||||
|
if let minTimestamp = calendarSource.minTimestamp, minTimestamp > lastDayTimestamp {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if monthModel.year < 2013 {
|
if monthModel.year < 2013 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -676,19 +762,11 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
self.months.append(monthModel)
|
self.months.append(monthModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.months.count > 1 {
|
|
||||||
for i in 0 ..< self.months.count - 1 {
|
|
||||||
if initialDate >= self.months[i].firstDay {
|
|
||||||
self.initialMonthIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||||
|
|
||||||
self.scrollView.delegate = self
|
self.scrollView.delegate = self
|
||||||
self.view.addSubview(self.scrollView)
|
self.addSubnode(self.contextGestureContainerNode)
|
||||||
|
self.contextGestureContainerNode.view.addSubview(self.scrollView)
|
||||||
|
|
||||||
self.isLoadingMoreDisposable = (self.calendarSource.isLoadingMore
|
self.isLoadingMoreDisposable = (self.calendarSource.isLoadingMore
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
@ -722,20 +800,38 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
if self.updateScrollLayoutIfNeeded() {
|
if self.updateScrollLayoutIfNeeded() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFirstLayout, let frame = self.scrollLayout?.frames[self.initialMonthIndex] {
|
if isFirstLayout {
|
||||||
var contentOffset = floor(frame.midY - self.scrollView.bounds.height / 2.0)
|
let initialDate = Date(timeIntervalSince1970: TimeInterval(self.initialTimestamp))
|
||||||
if contentOffset < 0 {
|
var initialMonthIndex: Int?
|
||||||
contentOffset = 0
|
|
||||||
|
if self.months.count > 1 {
|
||||||
|
for i in 0 ..< self.months.count - 1 {
|
||||||
|
if initialDate >= self.months[i].firstDay {
|
||||||
|
initialMonthIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if contentOffset > self.scrollView.contentSize.height - self.scrollView.bounds.height {
|
|
||||||
contentOffset = self.scrollView.contentSize.height - self.scrollView.bounds.height
|
if isFirstLayout, let initialMonthIndex = initialMonthIndex, let frame = self.scrollLayout?.frames[initialMonthIndex] {
|
||||||
|
var contentOffset = floor(frame.midY - self.scrollView.bounds.height / 2.0)
|
||||||
|
if contentOffset < 0 {
|
||||||
|
contentOffset = 0
|
||||||
|
}
|
||||||
|
if contentOffset > self.scrollView.contentSize.height - self.scrollView.bounds.height {
|
||||||
|
contentOffset = self.scrollView.contentSize.height - self.scrollView.bounds.height
|
||||||
|
}
|
||||||
|
self.scrollView.setContentOffset(CGPoint(x: 0.0, y: contentOffset), animated: false)
|
||||||
}
|
}
|
||||||
self.scrollView.setContentOffset(CGPoint(x: 0.0, y: contentOffset), animated: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMonthViews()
|
updateMonthViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
self.contextGestureContainerNode.cancelGesture()
|
||||||
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
if let indicator = scrollView.value(forKey: "_verticalScrollIndicator") as? UIView {
|
if let indicator = scrollView.value(forKey: "_verticalScrollIndicator") as? UIView {
|
||||||
indicator.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
|
indicator.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
|
||||||
@ -784,7 +880,8 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
|
|
||||||
self.scrollLayout = (layout.size.width, contentHeight, frames)
|
self.scrollLayout = (layout.size.width, contentHeight, frames)
|
||||||
|
|
||||||
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight))
|
self.contextGestureContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight))
|
||||||
|
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight))
|
||||||
self.scrollView.contentSize = CGSize(width: layout.size.width, height: contentHeight)
|
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: layout.size.width - 3.0 - 6.0)
|
self.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: layout.intrinsicInsets.bottom, left: 0.0, bottom: 0.0, right: layout.size.width - 3.0 - 6.0)
|
||||||
|
|
||||||
@ -861,19 +958,11 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
messageMap.append(message)
|
messageMap.append(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = messageMap
|
|
||||||
|
|
||||||
var updatedMedia: [Int: [Int: DayMedia]] = [:]
|
var updatedMedia: [Int: [Int: DayMedia]] = [:]
|
||||||
var removeMonths: [Int] = []
|
|
||||||
for i in 0 ..< self.months.count {
|
for i in 0 ..< self.months.count {
|
||||||
let firstDayTimestamp = Int32(self.months[i].firstDay.timeIntervalSince1970)
|
|
||||||
let lastDayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(self.months[i].numberOfDays)
|
|
||||||
|
|
||||||
if let minTimestamp = calendarState.minTimestamp, minTimestamp > lastDayTimestamp {
|
|
||||||
removeMonths.append(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for day in 0 ..< self.months[i].numberOfDays {
|
for day in 0 ..< self.months[i].numberOfDays {
|
||||||
|
let firstDayTimestamp = Int32(self.months[i].firstDay.timeIntervalSince1970)
|
||||||
|
|
||||||
let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day)
|
let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day)
|
||||||
let nextDayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day - 1)
|
let nextDayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day - 1)
|
||||||
|
|
||||||
@ -901,57 +990,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
self.months[monthIndex].mediaByDay = mediaByDay
|
self.months[monthIndex].mediaByDay = mediaByDay
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in removeMonths.reversed() {
|
|
||||||
self.months.remove(at: i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !removeMonths.isEmpty {
|
|
||||||
self.scrollLayout = nil
|
|
||||||
let _ = self.updateScrollLayoutIfNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.updateMonthViews()
|
self.updateMonthViews()
|
||||||
|
|
||||||
/*let peerId = self.peerId
|
|
||||||
let months = self.months
|
|
||||||
let _ = (self.context.account.postbox.transaction { transaction -> [Int: [Int: DayMedia]] in
|
|
||||||
var updatedMedia: [Int: [Int: DayMedia]] = [:]
|
|
||||||
|
|
||||||
for i in 0 ..< months.count {
|
|
||||||
for day in 0 ..< months[i].numberOfDays {
|
|
||||||
let dayTimestamp = Int32(months[i].firstDay.timeIntervalSince1970) + 24 * 60 * 60 * Int32(day)
|
|
||||||
let nextDayTimestamp = Int32(months[i].firstDay.timeIntervalSince1970) + 24 * 60 * 60 * Int32(day - 1)
|
|
||||||
if let message = transaction.firstMessageInRange(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .photoOrVideo, timestampMax: dayTimestamp, timestampMin: nextDayTimestamp - 1) {
|
|
||||||
/*if message.timestamp < nextDayTimestamp {
|
|
||||||
continue
|
|
||||||
}*/
|
|
||||||
if updatedMedia[i] == nil {
|
|
||||||
updatedMedia[i] = [:]
|
|
||||||
}
|
|
||||||
mediaLoop: for media in message.media {
|
|
||||||
switch media {
|
|
||||||
case _ as TelegramMediaImage, _ as TelegramMediaFile:
|
|
||||||
updatedMedia[i]![day] = DayMedia(message: EngineMessage(message), media: EngineMedia(media))
|
|
||||||
break mediaLoop
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedMedia
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] updatedMedia in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (monthIndex, mediaByDay) in updatedMedia {
|
|
||||||
strongSelf.months[monthIndex].mediaByDay = mediaByDay
|
|
||||||
}
|
|
||||||
strongSelf.updateMonthViews()
|
|
||||||
})*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,13 +1003,15 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
private let calendarSource: SparseMessageCalendar
|
private let calendarSource: SparseMessageCalendar
|
||||||
private let initialTimestamp: Int32
|
private let initialTimestamp: Int32
|
||||||
private let navigateToDay: (CalendarMessageScreen, Int32) -> Void
|
private let navigateToDay: (CalendarMessageScreen, Int32) -> Void
|
||||||
|
private let previewDay: (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void
|
||||||
|
|
||||||
public init(context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToDay: @escaping (CalendarMessageScreen, Int32) -> Void) {
|
public init(context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToDay: @escaping (CalendarMessageScreen, Int32) -> Void, previewDay: @escaping (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.calendarSource = calendarSource
|
self.calendarSource = calendarSource
|
||||||
self.initialTimestamp = initialTimestamp
|
self.initialTimestamp = initialTimestamp
|
||||||
self.navigateToDay = navigateToDay
|
self.navigateToDay = navigateToDay
|
||||||
|
self.previewDay = previewDay
|
||||||
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -997,7 +1038,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.navigateToDay(strongSelf, timestamp)
|
strongSelf.navigateToDay(strongSelf, timestamp)
|
||||||
})
|
}, previewDay: self.previewDay)
|
||||||
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
47
submodules/Postbox/Sources/DeletedMessagesView.swift
Normal file
47
submodules/Postbox/Sources/DeletedMessagesView.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
final class MutableDeletedMessagesView: MutablePostboxView {
|
||||||
|
let peerId: PeerId
|
||||||
|
var currentDeletedMessages: [MessageId] = []
|
||||||
|
|
||||||
|
init(peerId: PeerId) {
|
||||||
|
self.peerId = peerId
|
||||||
|
}
|
||||||
|
|
||||||
|
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
||||||
|
var updated = false
|
||||||
|
if let operations = transaction.currentOperationsByPeerId[self.peerId] {
|
||||||
|
var testMessageIds: [MessageId] = []
|
||||||
|
for operation in operations {
|
||||||
|
switch operation {
|
||||||
|
case let .Remove(indices):
|
||||||
|
for (index, _) in indices {
|
||||||
|
testMessageIds.append(index.id)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.currentDeletedMessages.removeAll()
|
||||||
|
for id in testMessageIds {
|
||||||
|
if !postbox.messageHistoryIndexTable.exists(id) {
|
||||||
|
self.currentDeletedMessages.append(id)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
func immutableView() -> PostboxView {
|
||||||
|
return DeletedMessagesView(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DeletedMessagesView: PostboxView {
|
||||||
|
public let currentDeletedMessages: [MessageId]
|
||||||
|
|
||||||
|
init(_ view: MutableDeletedMessagesView) {
|
||||||
|
self.currentDeletedMessages = view.currentDeletedMessages
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ public enum PostboxViewKey: Hashable {
|
|||||||
case historyTagInfo(peerId: PeerId, tag: MessageTags)
|
case historyTagInfo(peerId: PeerId, tag: MessageTags)
|
||||||
case topChatMessage(peerIds: [PeerId])
|
case topChatMessage(peerIds: [PeerId])
|
||||||
case contacts(accountPeerId: PeerId?, includePresences: Bool)
|
case contacts(accountPeerId: PeerId?, includePresences: Bool)
|
||||||
|
case deletedMessages(peerId: PeerId)
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -106,6 +107,8 @@ public enum PostboxViewKey: Hashable {
|
|||||||
hasher.combine(peerIds)
|
hasher.combine(peerIds)
|
||||||
case .contacts:
|
case .contacts:
|
||||||
hasher.combine(16)
|
hasher.combine(16)
|
||||||
|
case let .deletedMessages(peerId):
|
||||||
|
hasher.combine(peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +300,12 @@ public enum PostboxViewKey: Hashable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .deletedMessages(peerId):
|
||||||
|
if case .deletedMessages(peerId) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,5 +374,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
|
|||||||
return MutableTopChatMessageView(postbox: postbox, peerIds: Set(peerIds))
|
return MutableTopChatMessageView(postbox: postbox, peerIds: Set(peerIds))
|
||||||
case let .contacts(accountPeerId, includePresences):
|
case let .contacts(accountPeerId, includePresences):
|
||||||
return MutableContactPeersView(postbox: postbox, accountPeerId: accountPeerId, includePresences: includePresences)
|
return MutableContactPeersView(postbox: postbox, accountPeerId: accountPeerId, includePresences: includePresences)
|
||||||
|
case let .deletedMessages(peerId):
|
||||||
|
return MutableDeletedMessagesView(peerId: peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,6 +596,23 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func visualItem(at index: Int) -> SparseItemGridDisplayItem? {
|
||||||
|
guard let items = self.items, !items.items.isEmpty else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let item = items.item(at: index) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for (id, visibleItem) in self.visibleItems {
|
||||||
|
if id == item.id {
|
||||||
|
return visibleItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func item(at point: CGPoint) -> Item? {
|
func item(at point: CGPoint) -> Item? {
|
||||||
guard let items = self.items, !items.items.isEmpty else {
|
guard let items = self.items, !items.items.isEmpty else {
|
||||||
return nil
|
return nil
|
||||||
@ -1468,6 +1485,13 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
return currentViewport.visualItem(at: point)
|
return currentViewport.visualItem(at: point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func item(at index: Int) -> SparseItemGridDisplayItem? {
|
||||||
|
guard let currentViewport = self.currentViewport else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return currentViewport.visualItem(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
public func scrollToItem(at index: Int) {
|
public func scrollToItem(at index: Int) {
|
||||||
guard let currentViewport = self.currentViewport else {
|
guard let currentViewport = self.currentViewport else {
|
||||||
return
|
return
|
||||||
|
@ -74,6 +74,8 @@ public final class SparseMessageList {
|
|||||||
private var topSection: TopSection?
|
private var topSection: TopSection?
|
||||||
private var topItemsDisposable = MetaDisposable()
|
private var topItemsDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
private var deletedMessagesDisposable: Disposable?
|
||||||
|
|
||||||
private var sparseItems: SparseItems?
|
private var sparseItems: SparseItems?
|
||||||
private var sparseItemsDisposable: Disposable?
|
private var sparseItemsDisposable: Disposable?
|
||||||
|
|
||||||
@ -159,12 +161,24 @@ public final class SparseMessageList {
|
|||||||
strongSelf.updateState()
|
strongSelf.updateState()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.deletedMessagesDisposable = (account.postbox.combinedView(keys: [.deletedMessages(peerId: peerId)])
|
||||||
|
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let view = views.views[.deletedMessages(peerId: peerId)] as? DeletedMessagesView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.processDeletedMessages(ids: view.currentDeletedMessages)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.topItemsDisposable.dispose()
|
self.topItemsDisposable.dispose()
|
||||||
self.sparseItemsDisposable?.dispose()
|
self.sparseItemsDisposable?.dispose()
|
||||||
self.loadHoleDisposable.dispose()
|
self.loadHoleDisposable.dispose()
|
||||||
|
self.deletedMessagesDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetTopSection() {
|
private func resetTopSection() {
|
||||||
@ -188,162 +202,37 @@ public final class SparseMessageList {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func processDeletedMessages(ids: [MessageId]) {
|
||||||
|
if let sparseItems = self.sparseItems {
|
||||||
|
let idsSet = Set(ids)
|
||||||
|
|
||||||
|
var removeIndices: [Int] = []
|
||||||
|
for i in 0 ..< sparseItems.items.count {
|
||||||
|
switch sparseItems.items[i] {
|
||||||
|
case let .anchor(id, _, message):
|
||||||
|
if message != nil, idsSet.contains(id) {
|
||||||
|
removeIndices.append(i)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !removeIndices.isEmpty {
|
||||||
|
for index in removeIndices.reversed() {
|
||||||
|
self.sparseItems?.items.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadMoreFromTopSection() {
|
func loadMoreFromTopSection() {
|
||||||
self.topSectionItemRequestCount += 100
|
self.topSectionItemRequestCount += 100
|
||||||
self.resetTopSection()
|
self.resetTopSection()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPlaceholders(ids: [MessageId]) {
|
|
||||||
var loadGlobalIds: [MessageId] = []
|
|
||||||
var loadChannelIds: [PeerId: [MessageId]] = [:]
|
|
||||||
for id in ids {
|
|
||||||
if self.loadingPlaceholders[id] != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
self.loadingPlaceholders[id] = MetaDisposable()
|
|
||||||
if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup {
|
|
||||||
loadGlobalIds.append(id)
|
|
||||||
} else if id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
|
||||||
if loadChannelIds[id.peerId] == nil {
|
|
||||||
loadChannelIds[id.peerId] = []
|
|
||||||
}
|
|
||||||
loadChannelIds[id.peerId]!.append(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var loadSignals: [Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError>] = []
|
|
||||||
let account = self.account
|
|
||||||
|
|
||||||
if !loadGlobalIds.isEmpty {
|
|
||||||
loadSignals.append(self.account.postbox.transaction { transaction -> [Api.InputMessage] in
|
|
||||||
var result: [Api.InputMessage] = []
|
|
||||||
for id in loadGlobalIds {
|
|
||||||
let inputMessage: Api.InputMessage = .inputMessageID(id: id.id)
|
|
||||||
result.append(inputMessage)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|> mapToSignal { inputMessages -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
|
|
||||||
return account.network.request(Api.functions.messages.getMessages(id: inputMessages))
|
|
||||||
|> map { result -> (messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) in
|
|
||||||
switch result {
|
|
||||||
case let .messages(messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case let .messagesSlice(_, _, _, _, messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case let .channelMessages(_, _, _, _, messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case .messagesNotModified:
|
|
||||||
return ([], [], [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> `catch` { _ -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
|
|
||||||
return .single(([], [], []))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if !loadChannelIds.isEmpty {
|
|
||||||
for (channelId, ids) in loadChannelIds {
|
|
||||||
loadSignals.append(self.account.postbox.transaction { transaction -> Api.InputChannel? in
|
|
||||||
return transaction.getPeer(channelId).flatMap(apiInputChannel)
|
|
||||||
}
|
|
||||||
|> mapToSignal { inputChannel -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
|
|
||||||
guard let inputChannel = inputChannel else {
|
|
||||||
return .single(([], [], []))
|
|
||||||
}
|
|
||||||
|
|
||||||
return account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: ids.map { Api.InputMessage.inputMessageID(id: $0.id) }))
|
|
||||||
|> map { result -> (messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) in
|
|
||||||
switch result {
|
|
||||||
case let .messages(messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case let .messagesSlice(_, _, _, _, messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case let .channelMessages(_, _, _, _, messages, chats, users):
|
|
||||||
return (messages, chats, users)
|
|
||||||
case .messagesNotModified:
|
|
||||||
return ([], [], [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> `catch` { _ -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
|
|
||||||
return .single(([], [], []))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = (combineLatest(queue: self.queue, loadSignals)
|
|
||||||
|> mapToSignal { messageLists -> Signal<[Message], NoError> in
|
|
||||||
return account.postbox.transaction { transaction -> [Message] in
|
|
||||||
var parsedMessages: [StoreMessage] = []
|
|
||||||
var peers: [Peer] = []
|
|
||||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
|
||||||
|
|
||||||
for (messages, chats, users) in messageLists {
|
|
||||||
for chat in chats {
|
|
||||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
|
||||||
peers.append(groupOrChannel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for user in users {
|
|
||||||
let telegramUser = TelegramUser(user: user)
|
|
||||||
peers.append(telegramUser)
|
|
||||||
if let presence = TelegramUserPresence(apiUser: user) {
|
|
||||||
peerPresences[telegramUser.id] = presence
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for message in messages {
|
|
||||||
if let parsedMessage = StoreMessage(apiMessage: message) {
|
|
||||||
parsedMessages.append(parsedMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated })
|
|
||||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
|
||||||
let _ = transaction.addMessages(parsedMessages, location: .Random)
|
|
||||||
|
|
||||||
var result: [Message] = []
|
|
||||||
for parsedMessage in parsedMessages {
|
|
||||||
switch parsedMessage.id {
|
|
||||||
case let .Id(id):
|
|
||||||
if let message = transaction.getMessage(id) {
|
|
||||||
result.append(message)
|
|
||||||
}
|
|
||||||
case .Partial:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] messages in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for message in messages {
|
|
||||||
strongSelf.loadedPlaceholders[message.id] = message
|
|
||||||
}
|
|
||||||
if strongSelf.sparseItems != nil {
|
|
||||||
for i in 0 ..< strongSelf.sparseItems!.items.count {
|
|
||||||
switch strongSelf.sparseItems!.items[i] {
|
|
||||||
case let .anchor(id, timestamp, _):
|
|
||||||
if let message = strongSelf.loadedPlaceholders[id] {
|
|
||||||
strongSelf.sparseItems!.items[i] = .anchor(id: id, timestamp: timestamp, message: message)
|
|
||||||
}
|
|
||||||
case .range:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.updateState()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadHole(anchor: MessageId, direction: LoadHoleDirection, completion: @escaping () -> Void) {
|
func loadHole(anchor: MessageId, direction: LoadHoleDirection, completion: @escaping () -> Void) {
|
||||||
let loadingHole = LoadingHole(anchor: anchor, direction: direction)
|
let loadingHole = LoadingHole(anchor: anchor, direction: direction)
|
||||||
if self.loadingHole == loadingHole {
|
if self.loadingHole == loadingHole {
|
||||||
@ -811,12 +700,23 @@ public final class SparseMessageCalendar {
|
|||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let impl: QueueLocalObject<Impl>
|
private let impl: QueueLocalObject<Impl>
|
||||||
|
|
||||||
|
public var minTimestamp: Int32?
|
||||||
|
private var disposable: Disposable?
|
||||||
|
|
||||||
init(account: Account, peerId: PeerId, messageTag: MessageTags) {
|
init(account: Account, peerId: PeerId, messageTag: MessageTags) {
|
||||||
let queue = Queue()
|
let queue = Queue()
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return Impl(queue: queue, account: account, peerId: peerId, messageTag: messageTag)
|
return Impl(queue: queue, account: account, peerId: peerId, messageTag: messageTag)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.disposable = self.state.start(next: { [weak self] state in
|
||||||
|
self?.minTimestamp = state.minTimestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var state: Signal<State, NoError> {
|
public var state: Signal<State, NoError> {
|
||||||
|
@ -2201,6 +2201,24 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
if let index = previousIndex {
|
if let index = previousIndex {
|
||||||
self.itemGrid.scrollToItem(at: index)
|
self.itemGrid.scrollToItem(at: index)
|
||||||
|
|
||||||
|
if let item = self.itemGrid.item(at: index) {
|
||||||
|
if let layer = item.layer as? ItemLayer {
|
||||||
|
Queue.mainQueue().after(0.1, { [weak layer] in
|
||||||
|
guard let layer = layer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let overlayLayer = ListShimmerLayer.OverlayLayer()
|
||||||
|
overlayLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.6).cgColor
|
||||||
|
overlayLayer.frame = layer.bounds
|
||||||
|
layer.addSublayer(overlayLayer)
|
||||||
|
overlayLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.8, delay: 0.3, removeOnCompletion: false, completion: { [weak overlayLayer] _ in
|
||||||
|
overlayLayer?.removeFromSuperlayer()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6027,6 +6027,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
let _ = (context.account.postbox.combinedView(keys: summaryTags.map { tag in
|
let _ = (context.account.postbox.combinedView(keys: summaryTags.map { tag in
|
||||||
return PostboxViewKey.historyTagSummaryView(tag: tag, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
return PostboxViewKey.historyTagSummaryView(tag: tag, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||||
})
|
})
|
||||||
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -6194,7 +6195,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
initialTimestamp = timestamp
|
initialTimestamp = timestamp
|
||||||
|
|
||||||
self.controller?.push(CalendarMessageScreen(context: self.context, peerId: self.peerId, calendarSource: calendarSource, initialTimestamp: initialTimestamp, navigateToDay: { [weak self] c, timestamp in
|
var dismissCalendarScreen: (() -> Void)?
|
||||||
|
|
||||||
|
let calendarScreen = CalendarMessageScreen(context: self.context, peerId: self.peerId, calendarSource: calendarSource, initialTimestamp: initialTimestamp, navigateToDay: { [weak self] c, timestamp in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
c.dismiss()
|
c.dismiss()
|
||||||
return
|
return
|
||||||
@ -6207,7 +6210,60 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
pane.scrollToTimestamp(timestamp: timestamp)
|
pane.scrollToTimestamp(timestamp: timestamp)
|
||||||
|
|
||||||
c.dismiss()
|
c.dismiss()
|
||||||
}))
|
}, previewDay: { [weak self] index, sourceNode, sourceRect, gesture in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
f(.dismissWithoutContent)
|
||||||
|
dismissCalendarScreen?()
|
||||||
|
|
||||||
|
guard let strongSelf = self, let controller = strongSelf.controller, let navigationController = controller.navigationController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
||||||
|
navigationController: navigationController,
|
||||||
|
chatController: nil,
|
||||||
|
context: strongSelf.context,
|
||||||
|
chatLocation: .peer(strongSelf.peerId),
|
||||||
|
subject: .message(id: index.id, highlight: false, timecode: nil),
|
||||||
|
botStart: nil,
|
||||||
|
updateTextInputState: nil,
|
||||||
|
activateInput: false,
|
||||||
|
keepStack: .never,
|
||||||
|
useExisting: true,
|
||||||
|
purposefulAction: nil,
|
||||||
|
scrollToEndIfExists: false,
|
||||||
|
activateMessageSearch: nil,
|
||||||
|
peekData: nil,
|
||||||
|
peerNearbyData: nil,
|
||||||
|
reportReason: nil,
|
||||||
|
animated: true,
|
||||||
|
options: [],
|
||||||
|
parentGroupId: nil,
|
||||||
|
chatListFilter: nil,
|
||||||
|
changeColors: false,
|
||||||
|
completion: { _ in
|
||||||
|
}
|
||||||
|
))
|
||||||
|
})))
|
||||||
|
|
||||||
|
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(id: index.id, highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true))
|
||||||
|
chatController.canReadHistory.set(false)
|
||||||
|
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(items: items)), gesture: gesture)
|
||||||
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.controller?.push(calendarScreen)
|
||||||
|
dismissCalendarScreen = { [weak calendarScreen] in
|
||||||
|
calendarScreen?.dismiss(completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePresentationData(_ presentationData: PresentationData) {
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
@ -7427,21 +7483,26 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
|
|||||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
let controller: ViewController
|
let controller: ViewController
|
||||||
weak var sourceNode: ASDisplayNode?
|
weak var sourceNode: ASDisplayNode?
|
||||||
|
let sourceRect: CGRect
|
||||||
|
|
||||||
let navigationController: NavigationController? = nil
|
let navigationController: NavigationController? = nil
|
||||||
|
|
||||||
let passthroughTouches: Bool = false
|
let passthroughTouches: Bool
|
||||||
|
|
||||||
init(controller: ViewController, sourceNode: ASDisplayNode?) {
|
init(controller: ViewController, sourceNode: ASDisplayNode?, sourceRect: CGRect = CGRect(origin: CGPoint(), size: CGSize()), passthroughTouches: Bool = false) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.sourceNode = sourceNode
|
self.sourceNode = sourceNode
|
||||||
|
self.sourceRect = sourceRect
|
||||||
|
self.passthroughTouches = passthroughTouches
|
||||||
}
|
}
|
||||||
|
|
||||||
func transitionInfo() -> ContextControllerTakeControllerInfo? {
|
func transitionInfo() -> ContextControllerTakeControllerInfo? {
|
||||||
let sourceNode = self.sourceNode
|
let sourceNode = self.sourceNode
|
||||||
|
let sourceRect = self.sourceRect
|
||||||
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
|
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
|
||||||
if let sourceNode = sourceNode {
|
if let sourceNode = sourceNode {
|
||||||
return (sourceNode, sourceNode.bounds)
|
let rect = sourceRect.isEmpty ? sourceNode.bounds : sourceRect
|
||||||
|
return (sourceNode, rect)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user