diff --git a/Telegram/BUILD b/Telegram/BUILD index 839607d6b8..664c77eee4 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -377,6 +377,7 @@ official_apple_pay_merchants = [ "merchant.privatbank.test.telergramios", "merchant.privatbank.prod.telergram", "merchant.paymaster.test.telegramios", + "merchant.paymaster.prod.telegramios", "merchant.smartglocal.prod.telegramios", "merchant.smartglocal.test.telegramios", "merchant.yoomoney.test.telegramios", diff --git a/Telegram/SiriIntents/IntentMessages.swift b/Telegram/SiriIntents/IntentMessages.swift index f08d7e076d..f8b551899d 100644 --- a/Telegram/SiriIntents/IntentMessages.swift +++ b/Telegram/SiriIntents/IntentMessages.swift @@ -49,7 +49,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> { } if !isMuted && hasUnread { - signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(index.messageIndex.id.peerId), anchor: .upperBound, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation) + signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(index.messageIndex.id.peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation) |> take(1) |> map { view -> [INMessage] in var messages: [INMessage] = [] diff --git a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift index e9a6dd5ae7..1b9df36546 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift @@ -14,7 +14,7 @@ public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, st let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: primaryColor) let bold = MarkdownAttributeSet(font: Font.semibold(16.0), textColor: primaryColor) return parseMarkdownIntoAttributedString(strings.Login_CodeSentInternal, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center) - case .call, .flashCall: + case .call, .flashCall, .missedCall: return NSAttributedString(string: strings.ChangePhoneNumberCode_Called, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) } } @@ -31,7 +31,7 @@ public func authorizationNextOptionText(currentType: SentAuthorizationCodeType, let timeString = NSString(format: "%d:%.02d", Int(minutes), Int(seconds)) return (NSAttributedString(string: strings.Login_WillSendSms(timeString as String).string, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center), false) } - case .call, .flashCall: + case .call, .flashCall, .missedCall: if timeout <= 0 { return (NSAttributedString(string: strings.ChangePhoneNumberCode_Called, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center), false) } else { diff --git a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift index 970746cb5b..94c3b9431e 100644 --- a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift +++ b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift @@ -28,6 +28,12 @@ private class SimpleLayer: CALayer { } } +private enum SelectionTransition { + case begin + case change + case end +} + private final class MediaPreviewView: SimpleLayer { private let context: AccountContext private let message: EngineMessage @@ -282,6 +288,7 @@ private final class ImageCache: Equatable { private final class DayEnvironment: Equatable { let imageCache: ImageCache let directImageCache: DirectMediaImageCache + var selectionDelayCoordination: Int = 0 init(imageCache: ImageCache, directImageCache: DirectMediaImageCache) { self.imageCache = imageCache @@ -417,6 +424,7 @@ private final class DayComponent: Component { private var action: (() -> Void)? private var currentMedia: DayMedia? + private var currentSelection: DaySelection? private(set) var timestamp: Int32? private(set) var index: MessageIndex? @@ -460,6 +468,11 @@ private final class DayComponent: Component { self.timestamp = component.timestamp self.index = component.media?.message.index self.isHighlightingEnabled = component.isEnabled && component.media != nil && !component.isSelecting + + let previousSelection = self.currentSelection ?? component.selection + let previousSelected = previousSelection != .none + let isSelected = component.selection != .none + self.currentSelection = component.selection 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)) @@ -471,6 +484,7 @@ private final class DayComponent: Component { self.highlightView.contents = nil } + var animateTitle = false var animateMediaIn = false if self.currentMedia != component.media { self.currentMedia = component.media @@ -545,35 +559,32 @@ private final class DayComponent: Component { case .middle, .none: if let selectionView = self.selectionView { self.selectionView = nil - selectionView.removeFromSuperlayer() + if let _ = transition.userData(SelectionTransition.self), previousSelected != isSelected { + selectionView.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak selectionView] _ in + selectionView?.removeFromSuperlayer() + }) + } else { + selectionView.removeFromSuperlayer() + } } } + let minimizedContentScale: CGFloat = (contentFrame.width - 8.0) / contentFrame.width let contentScale: CGFloat switch component.selection { case .edge, .middle: - contentScale = (contentFrame.width - 8.0) / contentFrame.width + contentScale = minimizedContentScale case .none: contentScale = 1.0 } let titleImage = dayEnvironment.imageCache.text(fontSize: titleFontSize, isSemibold: titleFontIsSemibold, color: titleColor, string: component.title) if animateMediaIn { - let previousTitleView = SimpleLayer() - previousTitleView.contents = self.titleView.contents - previousTitleView.frame = self.titleView.frame - self.titleView.superlayer?.insertSublayer(previousTitleView, above: self.titleView) - previousTitleView.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousTitleView] _ in - previousTitleView?.removeFromSuperlayer() - }) - self.titleView.animateAlpha(from: 0.0, to: 1.0, duration: 0.16) + animateTitle = true } - self.titleView.contents = titleImage.cgImage - let titleSize = titleImage.size - - self.highlightView.frame = CGRect(origin: CGPoint(x: contentFrame.midX - contentFrame.width * contentScale / 2.0, y: contentFrame.midY - contentFrame.width * contentScale / 2.0), size: CGSize(width: contentFrame.width * contentScale, height: contentFrame.height * contentScale)) - - self.titleView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize) + + self.highlightView.bounds = CGRect(origin: CGPoint(), size: contentFrame.size) + self.highlightView.position = CGPoint(x: contentFrame.midX, y: contentFrame.midY) if let mediaPreviewView = self.mediaPreviewView { mediaPreviewView.bounds = CGRect(origin: CGPoint(), size: contentFrame.size) @@ -587,6 +598,69 @@ private final class DayComponent: Component { self.highlightView.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } + + self.highlightView.transform = CATransform3DMakeScale(contentScale, contentScale, 1.0) + + if let _ = transition.userData(SelectionTransition.self), previousSelected != isSelected { + if self.mediaPreviewView == nil { + animateTitle = true + } + if isSelected { + if component.selection == .edge { + let scaleIn = self.layer.makeAnimation(from: 1.0 as NSNumber, to: 0.75 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.1) + let scaleOut = self.layer.springAnimation(from: 0.75 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5) + self.layer.animateGroup([scaleIn, scaleOut], key: "selection") + if let selectionView = self.selectionView { + if self.mediaPreviewView != nil { + let shapeLayer = CAShapeLayer() + let lineWidth: CGFloat = 2.0 + shapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: diameter / 2.0, y: diameter / 2.0), radius: diameter / 2.0 - lineWidth / 2.0, startAngle: -CGFloat.pi / 2.0, endAngle: 2 * CGFloat.pi - CGFloat.pi / 2.0, clockwise: true).cgPath + shapeLayer.frame = selectionView.frame + shapeLayer.strokeColor = component.theme.list.itemCheckColors.fillColor.cgColor + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.lineWidth = lineWidth + shapeLayer.lineCap = .round + selectionView.isHidden = true + self.layer.insertSublayer(shapeLayer, above: selectionView) + shapeLayer.animate(from: 0.0 as NSNumber, to: 1.0 as NSNumber, keyPath: "strokeEnd", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.25, delay: 0.1, completion: { [weak selectionView, weak shapeLayer] _ in + shapeLayer?.removeFromSuperlayer() + selectionView?.isHidden = false + }) + } else { + selectionView.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + } else { + if let mediaPreviewView = self.mediaPreviewView { + mediaPreviewView.animateScale(from: 1.0, to: contentScale, duration: 0.2) + } + self.highlightView.animateScale(from: 1.0, to: contentScale, duration: 0.2) + } + } else { + if let mediaPreviewView = self.mediaPreviewView { + mediaPreviewView.animateScale(from: minimizedContentScale, to: contentScale, duration: 0.2) + } + self.highlightView.animateScale(from: minimizedContentScale, to: contentScale, duration: 0.2) + } + } + + if animateTitle { + let previousTitleView = SimpleLayer() + previousTitleView.contents = self.titleView.contents + previousTitleView.frame = self.titleView.frame + self.titleView.superlayer?.insertSublayer(previousTitleView, above: self.titleView) + previousTitleView.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousTitleView] _ in + previousTitleView?.removeFromSuperlayer() + }) + self.titleView.animateAlpha(from: 0.0, to: 1.0, duration: 0.16) + } + + self.titleView.contents = titleImage.cgImage + let titleSize = titleImage.size + + self.highlightView.frame = CGRect(origin: CGPoint(x: contentFrame.midX - contentFrame.width * contentScale / 2.0, y: contentFrame.midY - contentFrame.width * contentScale / 2.0), size: CGSize(width: contentFrame.width * contentScale, height: contentFrame.height * contentScale)) + + self.titleView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize) return availableSize } @@ -601,219 +675,6 @@ private final class DayComponent: Component { } } -private final class ManualMonthComponent: Component { - typealias EnvironmentType = DayEnvironment - - let context: AccountContext - let model: MonthModel - let foregroundColor: UIColor - let strings: PresentationStrings - let theme: PresentationTheme - let dayAction: (Int32) -> Void - let selectedDays: ClosedRange? - - init( - context: AccountContext, - model: MonthModel, - foregroundColor: UIColor, - strings: PresentationStrings, - theme: PresentationTheme, - dayAction: @escaping (Int32) -> Void, - selectedDays: ClosedRange? - ) { - self.context = context - self.model = model - self.foregroundColor = foregroundColor - self.strings = strings - self.theme = theme - self.dayAction = dayAction - self.selectedDays = selectedDays - } - - static func ==(lhs: ManualMonthComponent, rhs: ManualMonthComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.model != rhs.model { - return false - } - if lhs.foregroundColor != rhs.foregroundColor { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.selectedDays != rhs.selectedDays { - return false - } - return true - } - - final class View: UIView { - private let title: Text.View - private var weekdayTitles: [UIImageView] = [] - private var days: [Int: DayComponent.View] = [:] - - init() { - self.title = Text.View() - - super.init(frame: CGRect()) - - self.addSubview(self.title) - } - - required init?(coder aDecoder: NSCoder) { - preconditionFailure() - } - - func update(component: ManualMonthComponent, availableSize: CGSize, environment: Environment, transition: Transition) -> CGSize { - let sideInset: CGFloat = 14.0 - let titleWeekdaysSpacing: CGFloat = 18.0 - let weekdayDaySpacing: CGFloat = 14.0 - let weekdaySize: CGFloat = 46.0 - let weekdaySpacing: CGFloat = 6.0 - - let dayEnvironment = environment[DayEnvironment.self].value - - let usableWeekdayWidth = floor((availableSize.width - sideInset * 2.0 - weekdaySpacing * 6.0) / 7.0) - let weekdayWidth = floor((availableSize.width - sideInset * 2.0) / 7.0) - - let monthName = stringForMonth(strings: component.strings, month: Int32(component.model.index - 1), ofYear: Int32(component.model.year - 1900)) - - let titleSize = self.title.update( - component: Text( - text: monthName, - font: Font.semibold(17.0), - color: component.foregroundColor - ), - availableSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) - ) - - let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: 0.0), size: titleSize) - self.title.frame = titleFrame - - for i in 0 ..< 7 { - let weekdayTitle: UIImageView - if self.weekdayTitles.count > i { - weekdayTitle = self.weekdayTitles[i] - } else { - weekdayTitle = UIImageView() - self.addSubview(weekdayTitle) - self.weekdayTitles.append(weekdayTitle) - } - let image = dayEnvironment.imageCache.text(fontSize: 10.0, isSemibold: false, color: component.foregroundColor, string: gridDayName(index: i, firstDayOfWeek: component.model.firstWeekday, strings: component.strings)) - weekdayTitle.image = image - } - - let baseWeekdayTitleY = titleFrame.maxY + titleWeekdaysSpacing - var maxWeekdayY = baseWeekdayTitleY - - for i in 0 ..< self.weekdayTitles.count { - guard let image = self.weekdayTitles[i].image else { - continue - } - let weekdaySize = image.size - let weekdayFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i) * weekdayWidth + floor((weekdayWidth - weekdaySize.width) / 2.0), y: baseWeekdayTitleY), size: weekdaySize) - maxWeekdayY = max(maxWeekdayY, weekdayFrame.maxY) - self.weekdayTitles[i].frame = weekdayFrame - } - - var daySizes: [Int: CGSize] = [:] - for index in 0 ..< component.model.numberOfDays { - let dayOfMonth = index + 1 - let isCurrent = component.model.currentYear == component.model.year && component.model.currentMonth == component.model.index && component.model.currentDayOfMonth == dayOfMonth - var isEnabled = true - if component.model.currentYear == component.model.year { - if component.model.currentMonth == component.model.index { - if dayOfMonth > component.model.currentDayOfMonth { - isEnabled = false - } - } else if component.model.index > component.model.currentMonth { - isEnabled = false - } - } else if component.model.year > component.model.currentYear { - isEnabled = false - } - - let dayTimestamp = Int32(component.model.firstDay.timeIntervalSince1970) + 24 * 60 * 60 * Int32(index) - let dayAction = component.dayAction - - let daySelection: DayComponent.DaySelection - if let selectedDays = component.selectedDays, selectedDays.contains(dayTimestamp) { - if selectedDays.lowerBound == dayTimestamp || selectedDays.upperBound == dayTimestamp { - daySelection = .edge - } else { - daySelection = .middle - } - } else { - daySelection = .none - } - - let day: DayComponent.View - if let current = self.days[index] { - day = current - } else { - day = DayComponent.View() - self.addSubview(day) - self.days[index] = day - } - - let daySize = day.update( - component: DayComponent( - title: "\(dayOfMonth)", - isCurrent: isCurrent, - isEnabled: isEnabled, - theme: component.theme, - context: component.context, - timestamp: dayTimestamp, - media: component.model.mediaByDay[index], - selection: daySelection, - isSelecting: component.selectedDays != nil, - action: { - dayAction(dayTimestamp) - } - ), - availableSize: CGSize(width: usableWeekdayWidth, height: weekdaySize), - environment: environment, - transition: .immediate - ) - daySizes[index] = daySize - } - - let baseDayY = maxWeekdayY + weekdayDaySpacing - var maxDayY = baseDayY - - for i in 0 ..< component.model.numberOfDays { - guard let dayView = self.days[i], let dayItemSize = daySizes[i] else { - continue - } - let gridIndex = gridDayOffset(firstDayOfWeek: component.model.firstWeekday, firstWeekdayOfMonth: component.model.firstDayWeekday) + i - let rowIndex = gridIndex % 7 - let lineIndex = gridIndex / 7 - - let gridX = sideInset + CGFloat(rowIndex) * weekdayWidth - let gridY = baseDayY + CGFloat(lineIndex) * (weekdaySize + weekdaySpacing) - let dayFrame = CGRect(origin: CGPoint(x: gridX + floor((weekdayWidth - dayItemSize.width) / 2.0), y: gridY + floor((weekdaySize - dayItemSize.height) / 2.0)), size: dayItemSize) - maxDayY = max(maxDayY, gridY + weekdaySize) - dayView.frame = dayFrame - } - - return CGSize(width: availableSize.width, height: maxDayY) - } - } - - func makeView() -> View { - return View() - } - - func update(view: View, availableSize: CGSize, environment: Environment, transition: Transition) -> CGSize { - return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition) - } -} - private final class MonthComponent: CombinedComponent { typealias EnvironmentType = DayEnvironment @@ -954,7 +815,7 @@ private final class MonthComponent: CombinedComponent { context.environment[DayEnvironment.self] }, availableSize: CGSize(width: usableWeekdayWidth, height: weekdaySize), - transition: .immediate + transition: context.transition ) } @@ -1016,7 +877,10 @@ private final class MonthComponent: CombinedComponent { } if let selectedDays = context.component.selectedDays { - for (lineIndex, selection) in selectionsByLine { + for (lineIndex, selection) in selectionsByLine.sorted(by: { $0.key < $1.key }) { + if selection.leftTimestamp == selection.rightTimestamp && selection.leftTimestamp == selectedDays.lowerBound && selection.rightTimestamp == selectedDays.upperBound { + continue + } let dayEnvironment = context.environment[DayEnvironment.self].value let dayItemSize = updatedDays[0].size @@ -1027,18 +891,8 @@ private final class MonthComponent: CombinedComponent { let minY = baseDayY + CGFloat(lineIndex) * (weekdaySize + weekdaySpacing) + deltaHeight let maxY = minY + dayItemSize.width - let leftRadius: CGFloat - if selectedDays.lowerBound == selection.leftTimestamp { - leftRadius = dayItemSize.width - } else { - leftRadius = 10.0 - } - let rightRadius: CGFloat - if selectedDays.upperBound == selection.rightTimestamp { - rightRadius = dayItemSize.width - } else { - rightRadius = 10.0 - } + let leftRadius: CGFloat = dayItemSize.width + let rightRadius: CGFloat = dayItemSize.width let monthSelectionColor = context.component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.1) @@ -1048,9 +902,28 @@ private final class MonthComponent: CombinedComponent { availableSize: selectionRect.size, transition: .immediate ) + let delayIndex = dayEnvironment.selectionDelayCoordination context.add(selection .position(CGPoint(x: selectionRect.midX, y: selectionRect.midY)) + .appear(Transition.Appear { _, view, transition in + if case .none = transition.animation { + return + } + let delay = Double(delayIndex) * 0.1 + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.05, delay: delay) + view.layer.animateFrame(from: CGRect(origin: view.frame.origin, size: CGSize(width: leftRadius, height: view.frame.height)), to: view.frame, duration: 0.25, delay: delay, timingFunction: kCAMediaTimingFunctionSpring) + }) + .disappear(Transition.Disappear { view, transition, completion in + if case .none = transition.animation { + completion() + return + } + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { _ in + completion() + }) + }) ) + dayEnvironment.selectionDelayCoordination += 1 } } @@ -1376,26 +1249,31 @@ public final class CalendarMessageScreen: ViewController { } func toggleSelectionMode() { + var transition: Transition = .immediate if self.selectionState == nil { self.selectionState = SelectionState(dayRange: nil) } else { self.selectionState = nil + transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut)) + transition = transition.withUserData(SelectionTransition.end) } self.contextGestureContainerNode.isGestureEnabled = self.selectionState == nil - - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring)) - } + + self.updateSelectionState(transition: transition) } func selectDay(timestamp: Int32) { - self.selectionState = SelectionState(dayRange: timestamp ... timestamp) + if let selectionState = self.selectionState, selectionState.dayRange == timestamp ... timestamp { + self.selectionState = SelectionState(dayRange: nil) + } else { + self.selectionState = SelectionState(dayRange: timestamp ... timestamp) + } self.contextGestureContainerNode.isGestureEnabled = self.selectionState == nil if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring)) + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring), componentsTransition: .immediate) } } @@ -1404,7 +1282,7 @@ public final class CalendarMessageScreen: ViewController { self.selectionToolbarActionSelected() } - func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { + func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition, componentsTransition: Transition) { let isFirstLayout = self.validLayout == nil self.validLayout = (layout, navigationHeight) @@ -1422,13 +1300,39 @@ public final class CalendarMessageScreen: ViewController { let tabBarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight)) - if let _ = self.selectionState { + if let selectionState = self.selectionState { let selectionToolbarNode: ToolbarNode if let currrent = self.selectionToolbarNode { selectionToolbarNode = currrent + var selectedCount = 0 + if let dayRange = selectionState.dayRange { + for i in 0 ..< self.months.count { + let firstDayTimestamp = Int32(self.months[i].firstDay.timeIntervalSince1970) + + for day in 0 ..< self.months[i].numberOfDays { + let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day) + + if dayRange.contains(dayTimestamp) { + selectedCount += 1 + } + } + } + } + + let toolbarText: String + if selectedCount == 0 { + toolbarText = self.presentationData.strings.DialogList_ClearHistoryConfirmation + } else if selectedCount == 1 { + //TODO:localize + toolbarText = "Clear History For This Day" + } else { + //TODO:localize + toolbarText = "Clear History For These Days" + } + transition.updateFrame(node: selectionToolbarNode, frame: tabBarFrame) - selectionToolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: self.presentationData.strings.DialogList_ClearHistoryConfirmation, isEnabled: self.selectionState?.dayRange != nil, color: .custom(self.presentationData.theme.list.itemDestructiveColor))), transition: transition) + selectionToolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: toolbarText, isEnabled: self.selectionState?.dayRange != nil, color: .custom(self.presentationData.theme.list.itemDestructiveColor))), transition: transition) } else { selectionToolbarNode = ToolbarNode( theme: TabBarControllerTheme( @@ -1505,7 +1409,7 @@ public final class CalendarMessageScreen: ViewController { } - updateMonthViews() + updateMonthViews(transition: componentsTransition) } private func selectionToolbarActionSelected() { @@ -1630,15 +1534,9 @@ public final class CalendarMessageScreen: ViewController { guard let strongSelf = self else { return } - let _ = strongSelf.calendarSource.removeMessagesInRange(minTimestamp: minTimestampValue, maxTimestamp: maxTimestampValue, type: type, completion: { - Queue.mainQueue().async { - guard let strongSelf = self else { - return - } - - strongSelf.controller?.dismiss(completion: nil) - } - }) + + strongSelf.controller?.completedWithRemoveMessagesInRange?(minTimestampValue ... maxTimestampValue, type, selectedCount, strongSelf.calendarSource) + strongSelf.controller?.dismiss(completion: nil) } if let _ = info.canClearForMyself ?? info.canClearForEveryone { @@ -1685,8 +1583,6 @@ public final class CalendarMessageScreen: ViewController { strongSelf.controller?.present(actionSheet, in: .window(.root)) }) - - self.controller?.toggleSelectPressed() } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { @@ -1699,7 +1595,7 @@ public final class CalendarMessageScreen: ViewController { indicator.transform = CGAffineTransform(scaleX: -1.0, y: 1.0) } - self.updateMonthViews() + self.updateMonthViews(transition: .immediate) } } @@ -1751,15 +1647,17 @@ public final class CalendarMessageScreen: ViewController { return true } - func updateMonthViews() { + func updateMonthViews(transition: Transition) { guard let (width, _, frames) = self.scrollLayout else { return } + + self.dayEnvironment.selectionDelayCoordination = 0 let visibleRect = self.scrollView.bounds.insetBy(dx: 0.0, dy: -200.0) var validMonths = Set() - for i in 0 ..< self.months.count { + for i in (0 ..< self.months.count).reversed() { guard let monthFrame = frames[i] else { continue } @@ -1768,17 +1666,19 @@ public final class CalendarMessageScreen: ViewController { } validMonths.insert(i) + var monthTransition = transition let monthView: ComponentHostView if let current = self.monthViews[i] { monthView = current } else { + monthTransition = .immediate monthView = ComponentHostView() monthView.layer.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) self.monthViews[i] = monthView self.scrollView.addSubview(monthView) } let _ = monthView.update( - transition: .immediate, + transition: monthTransition, component: AnyComponent(MonthComponent( context: self.context, model: self.months[i], @@ -1790,22 +1690,29 @@ public final class CalendarMessageScreen: ViewController { return } if var selectionState = strongSelf.selectionState { + var transition = Transition(animation: .curve(duration: 0.2, curve: .spring)) if let dayRange = selectionState.dayRange { - if dayRange.lowerBound == dayRange.upperBound { + if dayRange.lowerBound == timestamp || dayRange.upperBound == timestamp { + selectionState.dayRange = nil + transition = transition.withUserData(SelectionTransition.end) + } else if dayRange.lowerBound == dayRange.upperBound { if timestamp < dayRange.lowerBound { selectionState.dayRange = timestamp ... dayRange.upperBound } else { selectionState.dayRange = dayRange.lowerBound ... timestamp } + transition = transition.withUserData(SelectionTransition.change) } else { selectionState.dayRange = timestamp ... timestamp + transition = transition.withUserData(SelectionTransition.change) } } else { selectionState.dayRange = timestamp ... timestamp + transition = transition.withUserData(SelectionTransition.begin) } strongSelf.selectionState = selectionState - strongSelf.updateSelectionState() + strongSelf.updateSelectionState(transition: transition) } else if let calendarState = strongSelf.calendarState { outer: for month in strongSelf.months { let firstDayTimestamp = Int32(month.firstDay.timeIntervalSince1970) @@ -1853,7 +1760,7 @@ public final class CalendarMessageScreen: ViewController { } } - private func updateSelectionState() { + private func updateSelectionState(transition: Transition) { var title = self.presentationData.strings.MessageCalendar_Title if let selectionState = self.selectionState, let dayRange = selectionState.dayRange { var selectedCount = 0 @@ -1876,7 +1783,7 @@ public final class CalendarMessageScreen: ViewController { self.controller?.navigationItem.title = title if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring)) + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring), componentsTransition: transition) } } @@ -1922,7 +1829,7 @@ public final class CalendarMessageScreen: ViewController { self.months[monthIndex].mediaByDay = mediaByDay } - self.updateMonthViews() + self.updateMonthViews(transition: .immediate) } } @@ -1940,6 +1847,8 @@ public final class CalendarMessageScreen: ViewController { private let previewDay: (Int32, MessageIndex?, ASDisplayNode, CGRect, ContextGesture) -> Void private var presentationData: PresentationData + + public var completedWithRemoveMessagesInRange: ((ClosedRange, InteractiveHistoryClearingType, Int, SparseMessageCalendar) -> Void)? public init( context: AccountContext, @@ -2034,6 +1943,6 @@ public final class CalendarMessageScreen: ViewController { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) - self.node.containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + self.node.containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, componentsTransition: .immediate) } } diff --git a/submodules/Display/Source/CAAnimationUtils.swift b/submodules/Display/Source/CAAnimationUtils.swift index 5fc052945d..99091d9b16 100644 --- a/submodules/Display/Source/CAAnimationUtils.swift +++ b/submodules/Display/Source/CAAnimationUtils.swift @@ -147,7 +147,7 @@ public extension CALayer { self.add(animation, forKey: additive ? nil : keyPath) } - func animateGroup(_ animations: [CAAnimation], key: String) { + func animateGroup(_ animations: [CAAnimation], key: String, completion: ((Bool) -> Void)? = nil) { let animationGroup = CAAnimationGroup() var timeOffset = 0.0 for animation in animations { @@ -156,6 +156,10 @@ public extension CALayer { } animationGroup.animations = animations animationGroup.duration = timeOffset + if let completion = completion { + animationGroup.delegate = CALayerAnimationDelegate(animation: animationGroup, completion: completion) + } + self.add(animationGroup, forKey: key) } @@ -194,6 +198,35 @@ public extension CALayer { self.add(animation, forKey: keyPath) } + + func springAnimation(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false) -> CABasicAnimation { + let animation: CABasicAnimation + if #available(iOS 9.0, *) { + animation = makeSpringBounceAnimation(keyPath, initialVelocity, damping) + } else { + animation = makeSpringAnimation(keyPath) + } + animation.fromValue = from + animation.toValue = to + animation.isRemovedOnCompletion = removeOnCompletion + animation.fillMode = .forwards + + let k = Float(UIView.animationDurationFactor()) + var speed: Float = 1.0 + if k != 0 && k != 1 { + speed = Float(1.0) / k + } + + if !delay.isZero { + animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor() + animation.fillMode = .both + } + + animation.speed = speed * Float(animation.duration / duration) + animation.isAdditive = additive + + return animation + } func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { let animation: CABasicAnimation diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 2c8fe8d548..a2492e9890 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -448,7 +448,7 @@ public class GalleryController: ViewController, StandalonePresentableController } else { namespaces = .not(Namespaces.Message.allScheduled) } - return context.account.postbox.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(message!.index), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) + return context.account.postbox.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(message!.index), ignoreMessagesInTimestampRange: nil, count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view) return .single(mapped) @@ -1139,7 +1139,7 @@ public class GalleryController: ViewController, StandalonePresentableController } else { namespaces = .not(Namespaces.Message.allScheduled) } - let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(strongSelf.context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(reloadAroundIndex), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) + let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(strongSelf.context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(reloadAroundIndex), ignoreMessagesInTimestampRange: nil, count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view) return .single(mapped) diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 43706be814..58af91bd5b 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -2904,7 +2904,7 @@ final class MessageHistoryTable: Table { return (result, mediaRefs, count == 0 ? nil : lastIndex) } - func fetch(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags?, threadId: Int64?, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, limit: Int) -> [IntermediateMessage] { + func fetch(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags?, threadId: Int64?, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, limit: Int) -> [IntermediateMessage] { precondition(fromIndex.id.peerId == toIndex.id.peerId) precondition(fromIndex.id.namespace == toIndex.id.namespace) var result: [IntermediateMessage] = [] @@ -2926,6 +2926,11 @@ final class MessageHistoryTable: Table { localIncludeFrom = false for index in sliceIndices { + if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange { + if ignoreMessagesInTimestampRange.contains(index.timestamp) { + continue + } + } if let tag = tag { if self.tagsTable.entryExists(tag: tag, index: index) { indices.append(index) @@ -2961,6 +2966,72 @@ final class MessageHistoryTable: Table { } else { indices = self.tagsTable.earlierIndices(tag: tag, peerId: peerId, namespace: namespace, index: fromIndex, includeFrom: includeFrom, count: limit) } + for index in indices { + if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange { + if ignoreMessagesInTimestampRange.contains(index.timestamp) { + continue + } + } + if fromIndex < toIndex { + if index < fromIndex || index > toIndex { + continue + } + } else { + if index < toIndex || index > fromIndex { + continue + } + } + if let message = self.getMessage(index) { + result.append(message) + } else { + assertionFailure() + } + } + } else if ignoreMessagesInTimestampRange != nil { + var indices: [MessageIndex] = [] + var startIndex = fromIndex + var localIncludeFrom = includeFrom + while true { + let startKey: ValueBoxKey + if localIncludeFrom && startIndex != MessageIndex.upperBound(peerId: peerId, namespace: namespace) { + if startIndex < toIndex { + startKey = self.key(startIndex).predecessor + } else { + startKey = self.key(startIndex).successor + } + } else { + startKey = self.key(startIndex) + } + + var sliceIndices: [MessageIndex] = [] + + self.valueBox.range(self.table, start: startKey, end: self.key(toIndex), values: { key, value in + sliceIndices.append(extractKey(key)) + return true + }, limit: limit) + + if sliceIndices.isEmpty { + break + } + startIndex = sliceIndices[sliceIndices.count - 1] + localIncludeFrom = false + + for index in sliceIndices { + if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange { + if ignoreMessagesInTimestampRange.contains(index.timestamp) { + continue + } + } + indices.append(index) + if indices.count >= limit { + break + } + } + if indices.count >= limit { + break + } + } + assert(Set(indices).count == indices.count) for index in indices { if fromIndex < toIndex { if index < fromIndex || index > toIndex { diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 046926c74a..bdecca3d5d 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -311,6 +311,7 @@ public enum HistoryViewInputAnchor: Equatable { final class MutableMessageHistoryView { private(set) var peerIds: MessageHistoryViewInput + private let ignoreMessagesInTimestampRange: ClosedRange? let tag: MessageTags? private let appendMessagesFromTheSameGroup: Bool let namespaces: MessageIdNamespaces @@ -331,12 +332,29 @@ final class MutableMessageHistoryView { fileprivate var isAddedToChatList: Bool - init(postbox: PostboxImpl, orderStatistics: MessageHistoryViewOrderStatistics, clipHoles: Bool, peerIds: MessageHistoryViewInput, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { + init( + postbox: PostboxImpl, + orderStatistics: MessageHistoryViewOrderStatistics, + clipHoles: Bool, + peerIds: MessageHistoryViewInput, + ignoreMessagesInTimestampRange: ClosedRange?, + anchor inputAnchor: HistoryViewInputAnchor, + combinedReadStates: MessageHistoryViewReadState?, + transientReadStates: MessageHistoryViewReadState?, + tag: MessageTags?, + appendMessagesFromTheSameGroup: Bool, + namespaces: MessageIdNamespaces, + count: Int, + topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], + additionalDatas: [AdditionalMessageHistoryViewDataEntry], + getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32 + ) { self.anchor = inputAnchor self.orderStatistics = orderStatistics self.clipHoles = clipHoles self.peerIds = peerIds + self.ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange self.combinedReadStates = combinedReadStates self.transientReadStates = transientReadStates self.tag = tag @@ -355,12 +373,12 @@ final class MutableMessageHistoryView { self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: input.peerId) != nil } - self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds) + self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: count + 1, locations: peerIds) if case let .loading(loadingState) = self.state { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes)) self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) case .loadHole: break @@ -372,12 +390,12 @@ final class MutableMessageHistoryView { } private func reset(postbox: PostboxImpl) { - self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds) + self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds) if case let .loading(loadingState) = self.state { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } @@ -386,7 +404,7 @@ final class MutableMessageHistoryView { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } @@ -629,7 +647,7 @@ final class MutableMessageHistoryView { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index b1bd48ca4f..9ab908095a 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -25,10 +25,10 @@ public enum MessageHistoryInput: Equatable, Hashable { } private extension MessageHistoryInput { - func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, limit: Int) -> [IntermediateMessage] { + func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, limit: Int) -> [IntermediateMessage] { switch self { case let .automatic(automatic): - var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: automatic?.tag, threadId: nil, from: fromIndex, includeFrom: includeFrom, to: toIndex, limit: limit) + var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: automatic?.tag, threadId: nil, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) if let automatic = automatic, automatic.appendMessagesFromTheSameGroup { enum Direction { case lowToHigh @@ -70,7 +70,7 @@ private extension MessageHistoryInput { } return items case let .external(input, tag): - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: input.threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, limit: limit) + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: input.threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) } } @@ -878,17 +878,19 @@ final class HistoryViewLoadedState { let namespaces: MessageIdNamespaces let input: MessageHistoryInput let statistics: MessageHistoryViewOrderStatistics + let ignoreMessagesInTimestampRange: ClosedRange? let halfLimit: Int let seedConfiguration: SeedConfiguration var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries] var holes: HistoryViewHoles var spacesWithRemovals = Set() - init(anchor: HistoryViewAnchor, tag: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewInput, postbox: PostboxImpl, holes: HistoryViewHoles) { + init(anchor: HistoryViewAnchor, tag: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, halfLimit: Int, locations: MessageHistoryViewInput, postbox: PostboxImpl, holes: HistoryViewHoles) { precondition(halfLimit >= 3) self.anchor = anchor self.namespaces = namespaces self.statistics = statistics + self.ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange self.halfLimit = halfLimit self.seedConfiguration = postbox.seedConfiguration self.orderedEntriesBySpace = [:] @@ -962,7 +964,7 @@ final class HistoryViewLoadedState { } else { nextLowerIndex = (anchorIndex, true) } - lowerOrAtAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry)) + lowerOrAtAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry)) } if higherThanAnchorMessages.count < self.halfLimit { let nextHigherIndex: MessageIndex @@ -971,7 +973,7 @@ final class HistoryViewLoadedState { } else { nextHigherIndex = anchorIndex } - higherThanAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextHigherIndex, includeFrom: false, to: upperBound, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry)) + higherThanAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextHigherIndex, includeFrom: false, to: upperBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry)) } lowerOrAtAnchorMessages.reverse() @@ -1131,6 +1133,12 @@ final class HistoryViewLoadedState { } func add(entry: MutableMessageHistoryEntry) -> Bool { + if let ignoreMessagesInTimestampRange = self.ignoreMessagesInTimestampRange { + if ignoreMessagesInTimestampRange.contains(entry.index.timestamp) { + return false + } + } + let space = PeerIdAndNamespace(peerId: entry.index.id.peerId, namespace: entry.index.id.namespace) if self.orderedEntriesBySpace[space] == nil { @@ -1447,14 +1455,14 @@ enum HistoryViewState { case loaded(HistoryViewLoadedState) case loading(HistoryViewLoadingState) - init(postbox: PostboxImpl, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewInput) { + init(postbox: PostboxImpl, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, halfLimit: Int, locations: MessageHistoryViewInput) { switch inputAnchor { case let .index(index): - self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) case .lowerBound: - self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) case .upperBound: - self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) case .unread: let anchorPeerId: PeerId switch locations { @@ -1463,7 +1471,7 @@ enum HistoryViewState { case let .associated(peerId, _): anchorPeerId = peerId case .external: - self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) return } if postbox.chatListIndexTable.get(peerId: anchorPeerId).includedIndex(peerId: anchorPeerId) != nil, let combinedState = postbox.readStateTable.getCombinedState(anchorPeerId) { @@ -1494,12 +1502,12 @@ enum HistoryViewState { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) + self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) case .loadHole: self = .loading(loadingState) } } else { - self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) } } else { preconditionFailure() @@ -1516,7 +1524,7 @@ enum HistoryViewState { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) + self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) case .loadHole: self = .loading(loadingState) } diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index 033d9fec90..18d41cf9ba 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -60,7 +60,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { } } self.anchor = anchor - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) let _ = self.updateFromView() } @@ -132,7 +132,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) return self.updateFromView() } else if self.wrappedView.replay(postbox: postbox, transaction: transaction) { var reloadView = false @@ -163,7 +163,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) } return self.updateFromView() diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index e3df35b01a..1b1c5a0d54 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1013,7 +1013,7 @@ public final class Transaction { guard let postbox = self.postbox else { return [] } - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: threadId, from: from, includeFrom: includeFrom, to: to, limit: limit).map(postbox.renderIntermediateMessage(_:)) + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: threadId, from: from, includeFrom: includeFrom, to: to, ignoreMessagesInTimestampRange: nil, limit: limit).map(postbox.renderIntermediateMessage(_:)) } public func scanMessages(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) { @@ -1030,7 +1030,7 @@ public final class Transaction { self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f) } - public func getMessagesHistoryViewState(input: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView { + public func getMessagesHistoryViewState(input: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView { precondition(!self.disposed) guard let postbox = self.postbox else { preconditionFailure() @@ -1044,7 +1044,7 @@ public final class Transaction { view = next.0 }, error: { _ in }, completed: {}) - let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: []) + let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: []) disposable.dispose() return view! @@ -1871,7 +1871,7 @@ final class PostboxImpl { if let states = initialCombinedStates?.states { for (namespace, state) in states { if namespace != messageIndex.id.namespace && state.count != 0 { - if let item = self.messageHistoryTable.fetch(peerId: messageIndex.id.peerId, namespace: namespace, tag: nil, threadId: nil, from: MessageIndex(id: MessageId(peerId: messageIndex.id.peerId, namespace: namespace, id: 1), timestamp: messageIndex.timestamp), includeFrom: true, to: MessageIndex.lowerBound(peerId: messageIndex.id.peerId, namespace: namespace), limit: 1).first { + if let item = self.messageHistoryTable.fetch(peerId: messageIndex.id.peerId, namespace: namespace, tag: nil, threadId: nil, from: MessageIndex(id: MessageId(peerId: messageIndex.id.peerId, namespace: namespace, id: 1), timestamp: messageIndex.timestamp), includeFrom: true, to: MessageIndex.lowerBound(peerId: messageIndex.id.peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, limit: 1).first { resultIds.append(contentsOf: self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: item.index, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)) } } @@ -2559,7 +2559,7 @@ final class PostboxImpl { return peerIds } - public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData in let (chatLocation, isHoleFill) = chatLocationData @@ -2615,7 +2615,7 @@ final class PostboxImpl { anchor = .upperBound } } - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) }) return signal @@ -2629,13 +2629,13 @@ final class PostboxImpl { } } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData in let (chatLocation, isHoleFill) = chatLocationData let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } return signal @@ -2649,14 +2649,14 @@ final class PostboxImpl { } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in let (chatLocation, isHoleFill) = chatLocationData let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } return signal @@ -2671,7 +2671,21 @@ final class PostboxImpl { } } - fileprivate func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { + fileprivate func syncAroundMessageHistoryViewForPeerId( + subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, + peerIds: MessageHistoryViewInput, + ignoreMessagesInTimestampRange: ClosedRange?, + count: Int, + clipHoles: Bool, + anchor: HistoryViewInputAnchor, + fixedCombinedReadStates: MessageHistoryViewReadState?, + topTaggedMessageIdNamespaces: Set, + tagMask: MessageTags?, + appendMessagesFromTheSameGroup: Bool, + namespaces: MessageIdNamespaces, + orderStatistics: MessageHistoryViewOrderStatistics, + additionalData: [AdditionalMessageHistoryViewData] + ) -> Disposable { var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] var mainPeerIdForTopTaggedMessages: PeerId? switch peerIds { @@ -2767,7 +2781,7 @@ final class PostboxImpl { readStates = transientReadStates } - let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, clipHoles: clipHoles, peerIds: peerIds, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries, getMessageCountInRange: { lowerBound, upperBound in + let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, clipHoles: clipHoles, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries, getMessageCountInRange: { lowerBound, upperBound in if let tagMask = tagMask { return Int32(self.messageHistoryTable.getMessageCountInRange(peerId: lowerBound.id.peerId, namespace: lowerBound.id.namespace, tag: tagMask, lowerBound: lowerBound, upperBound: upperBound)) } else { @@ -3410,7 +3424,7 @@ final class PostboxImpl { var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace) var remainingLimit = limit while remainingLimit > 0 { - let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: nil, from: index, includeFrom: false, to: lowerBound, limit: 10) + let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: nil, from: index, includeFrom: false, to: lowerBound, ignoreMessagesInTimestampRange: nil, limit: 10) remainingLimit -= 10 for message in messages { if !f(self.renderIntermediateMessage(message)) { @@ -3429,7 +3443,7 @@ final class PostboxImpl { var remainingLimit = limit var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace) while remainingLimit > 0 { - let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), limit: 32) + let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, limit: 32) for message in messages { let attributes = MessageHistoryTable.renderMessageAttributes(message) if !f(message.id, attributes) { @@ -3684,6 +3698,7 @@ public class Postbox { public func aroundMessageOfInterestHistoryViewForChatLocation( _ chatLocation: ChatLocationInput, + ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, @@ -3699,6 +3714,7 @@ public class Postbox { self.impl.with { impl in disposable.set(impl.aroundMessageOfInterestHistoryViewForChatLocation( chatLocation, + ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, @@ -3716,6 +3732,7 @@ public class Postbox { public func aroundIdMessageHistoryViewForLocation( _ chatLocation: ChatLocationInput, + ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, @@ -3733,6 +3750,7 @@ public class Postbox { self.impl.with { impl in disposable.set(impl.aroundIdMessageHistoryViewForLocation( chatLocation, + ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, @@ -3753,6 +3771,7 @@ public class Postbox { public func aroundMessageHistoryViewForLocation( _ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, + ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, @@ -3770,6 +3789,7 @@ public class Postbox { self.impl.with { impl in disposable.set(impl.aroundMessageHistoryViewForLocation( chatLocation, + ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, anchor: anchor, count: count, clipHoles: clipHoles, diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index c4f04b0a2c..8df71aae5d 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -342,7 +342,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-842824308] = { return Api.account.WallPapers.parse_wallPapers($0) } dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) } dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) } - dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) } + dict[142306870] = { return Api.MessageReactions.parse_messageReactions($0) } dict[-2032041631] = { return Api.Poll.parse_poll($0) } dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) } dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) } @@ -521,7 +521,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) } dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) } dict[1248893260] = { return Api.EncryptedFile.parse_encryptedFile($0) } - dict[-557924733] = { return Api.CodeSettings.parse_codeSettings($0) } + dict[-1973130814] = { return Api.CodeSettings.parse_codeSettings($0) } dict[-1343921601] = { return Api.phone.JoinAsPeers.parse_joinAsPeers($0) } dict[-391902247] = { return Api.SecureValueError.parse_secureValueErrorData($0) } dict[12467706] = { return Api.SecureValueError.parse_secureValueErrorFrontSide($0) } @@ -661,6 +661,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) } dict[1948046307] = { return Api.auth.CodeType.parse_codeTypeCall($0) } dict[577556219] = { return Api.auth.CodeType.parse_codeTypeFlashCall($0) } + dict[-702884114] = { return Api.auth.CodeType.parse_codeTypeMissedCall($0) } dict[1815593308] = { return Api.DocumentAttribute.parse_documentAttributeImageSize($0) } dict[297109817] = { return Api.DocumentAttribute.parse_documentAttributeAnimated($0) } dict[1662637586] = { return Api.DocumentAttribute.parse_documentAttributeSticker($0) } @@ -790,7 +791,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) } dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) } dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) } - dict[-764945220] = { return Api.MessageUserReaction.parse_messageUserReaction($0) } + dict[-1826077446] = { return Api.MessageUserReaction.parse_messageUserReaction($0) } dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) } dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) } dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) } @@ -931,6 +932,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1073693790] = { return Api.auth.SentCodeType.parse_sentCodeTypeSms($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } dict[-1425815847] = { return Api.auth.SentCodeType.parse_sentCodeTypeFlashCall($0) } + dict[-2113903484] = { return Api.auth.SentCodeType.parse_sentCodeTypeMissedCall($0) } dict[1577484359] = { return Api.PageListOrderedItem.parse_pageListOrderedItemText($0) } dict[-1730311882] = { return Api.PageListOrderedItem.parse_pageListOrderedItemBlocks($0) } dict[-1417756512] = { return Api.EncryptedChat.parse_encryptedChatEmpty($0) } @@ -940,6 +942,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[505183301] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) } dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) } dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) } + dict[-1957096922] = { return Api.auth.LoggedOut.parse_loggedOut($0) } dict[922273905] = { return Api.Document.parse_documentEmpty($0) } dict[512177195] = { return Api.Document.parse_document($0) } dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) } @@ -1657,6 +1660,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.PeerLocated: _1.serialize(buffer, boxed) + case let _1 as Api.auth.LoggedOut: + _1.serialize(buffer, boxed) case let _1 as Api.Document: _1.serialize(buffer, boxed) case let _1 as Api.messages.HighScores: diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 52e1e2035f..21800c6d3d 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -8902,13 +8902,13 @@ public extension Api { } public enum MessageReactions: TypeConstructorDescription { - case messageReactions(flags: Int32, results: [Api.ReactionCount]) + case messageReactions(flags: Int32, results: [Api.ReactionCount], recentReactons: [Api.MessageUserReaction]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageReactions(let flags, let results): + case .messageReactions(let flags, let results, let recentReactons): if boxed { - buffer.appendInt32(-1199954735) + buffer.appendInt32(142306870) } serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) @@ -8916,14 +8916,19 @@ public extension Api { for item in results { item.serialize(buffer, true) } + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentReactons!.count)) + for item in recentReactons! { + item.serialize(buffer, true) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageReactions(let flags, let results): - return ("messageReactions", [("flags", flags), ("results", results)]) + case .messageReactions(let flags, let results, let recentReactons): + return ("messageReactions", [("flags", flags), ("results", results), ("recentReactons", recentReactons)]) } } @@ -8934,10 +8939,15 @@ public extension Api { if let _ = reader.readInt32() { _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self) } + var _3: [Api.MessageUserReaction]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserReaction.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageReactions.messageReactions(flags: _1!, results: _2!) + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactons: _3) } else { return nil @@ -13262,32 +13272,42 @@ public extension Api { } public enum CodeSettings: TypeConstructorDescription { - case codeSettings(flags: Int32) + case codeSettings(flags: Int32, logoutTokens: [Buffer]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .codeSettings(let flags): + case .codeSettings(let flags, let logoutTokens): if boxed { - buffer.appendInt32(-557924733) + buffer.appendInt32(-1973130814) } serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(logoutTokens!.count)) + for item in logoutTokens! { + serializeBytes(item, buffer: buffer, boxed: false) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .codeSettings(let flags): - return ("codeSettings", [("flags", flags)]) + case .codeSettings(let flags, let logoutTokens): + return ("codeSettings", [("flags", flags), ("logoutTokens", logoutTokens)]) } } public static func parse_codeSettings(_ reader: BufferReader) -> CodeSettings? { var _1: Int32? _1 = reader.readInt32() + var _2: [Buffer]? + if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } } let _c1 = _1 != nil - if _c1 { - return Api.CodeSettings.codeSettings(flags: _1!) + let _c2 = (Int(_1!) & Int(1 << 6) == 0) || _2 != nil + if _c1 && _c2 { + return Api.CodeSettings.codeSettings(flags: _1!, logoutTokens: _2) } else { return nil @@ -20162,15 +20182,15 @@ public extension Api { } public enum MessageUserReaction: TypeConstructorDescription { - case messageUserReaction(userId: Int32, reaction: String) + case messageUserReaction(userId: Int64, reaction: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { case .messageUserReaction(let userId, let reaction): if boxed { - buffer.appendInt32(-764945220) + buffer.appendInt32(-1826077446) } - serializeInt32(userId, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) serializeString(reaction, buffer: buffer, boxed: false) break } @@ -20184,8 +20204,8 @@ public extension Api { } public static func parse_messageUserReaction(_ reader: BufferReader) -> MessageUserReaction? { - var _1: Int32? - _1 = reader.readInt32() + var _1: Int64? + _1 = reader.readInt64() var _2: String? _2 = parseString(reader) let _c1 = _1 != nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 5168528c9f..1adbf6e282 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -1236,6 +1236,7 @@ public struct auth { case codeTypeSms case codeTypeCall case codeTypeFlashCall + case codeTypeMissedCall public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -1256,6 +1257,12 @@ public struct auth { buffer.appendInt32(577556219) } + break + case .codeTypeMissedCall: + if boxed { + buffer.appendInt32(-702884114) + } + break } } @@ -1268,6 +1275,8 @@ public struct auth { return ("codeTypeCall", []) case .codeTypeFlashCall: return ("codeTypeFlashCall", []) + case .codeTypeMissedCall: + return ("codeTypeMissedCall", []) } } @@ -1280,6 +1289,9 @@ public struct auth { public static func parse_codeTypeFlashCall(_ reader: BufferReader) -> CodeType? { return Api.auth.CodeType.codeTypeFlashCall } + public static func parse_codeTypeMissedCall(_ reader: BufferReader) -> CodeType? { + return Api.auth.CodeType.codeTypeMissedCall + } } public enum SentCodeType: TypeConstructorDescription { @@ -1287,6 +1299,7 @@ public struct auth { case sentCodeTypeSms(length: Int32) case sentCodeTypeCall(length: Int32) case sentCodeTypeFlashCall(pattern: String) + case sentCodeTypeMissedCall(prefix: String, length: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -1314,6 +1327,13 @@ public struct auth { } serializeString(pattern, buffer: buffer, boxed: false) break + case .sentCodeTypeMissedCall(let prefix, let length): + if boxed { + buffer.appendInt32(-2113903484) + } + serializeString(prefix, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break } } @@ -1327,6 +1347,8 @@ public struct auth { return ("sentCodeTypeCall", [("length", length)]) case .sentCodeTypeFlashCall(let pattern): return ("sentCodeTypeFlashCall", [("pattern", pattern)]) + case .sentCodeTypeMissedCall(let prefix, let length): + return ("sentCodeTypeMissedCall", [("prefix", prefix), ("length", length)]) } } @@ -1374,6 +1396,62 @@ public struct auth { return nil } } + public static func parse_sentCodeTypeMissedCall(_ reader: BufferReader) -> SentCodeType? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.auth.SentCodeType.sentCodeTypeMissedCall(prefix: _1!, length: _2!) + } + else { + return nil + } + } + + } + public enum LoggedOut: TypeConstructorDescription { + case loggedOut(flags: Int32, futureAuthToken: Buffer?, futureAuthExpires: Int32?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .loggedOut(let flags, let futureAuthToken, let futureAuthExpires): + if boxed { + buffer.appendInt32(-1957096922) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(futureAuthToken!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(futureAuthExpires!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .loggedOut(let flags, let futureAuthToken, let futureAuthExpires): + return ("loggedOut", [("flags", flags), ("futureAuthToken", futureAuthToken), ("futureAuthExpires", futureAuthExpires)]) + } + } + + public static func parse_loggedOut(_ reader: BufferReader) -> LoggedOut? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } + var _3: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.auth.LoggedOut.loggedOut(flags: _1!, futureAuthToken: _2, futureAuthExpires: _3) + } + else { + return nil + } + } } } diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 3b973c5282..9ff5b07d59 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -5426,15 +5426,15 @@ public extension Api { }) } - public static func logOut() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func logOut() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1461180992) + buffer.appendInt32(1047706137) - return (FunctionDescription(name: "auth.logOut", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + return (FunctionDescription(name: "auth.logOut", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoggedOut? in let reader = BufferReader(buffer) - var result: Api.Bool? + var result: Api.auth.LoggedOut? if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool + result = Api.parse(reader, signature: signature) as? Api.auth.LoggedOut } return result }) diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index 7505bdee4f..1db3b79e63 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -426,8 +426,8 @@ private func cleanupAccount(networkArguments: NetworkInitializationArguments, ac account.shouldBeServiceTaskMaster.set(.single(.always)) return account.network.request(Api.functions.auth.logOut()) |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(.boolFalse) + |> `catch` { _ -> Signal in + return .single(nil) } |> mapToSignal { _ -> Signal in account.shouldBeServiceTaskMaster.set(.single(.never)) diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift index 86e2fad870..2a4b008e46 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift @@ -5,7 +5,7 @@ import TelegramApi extension ReactionsMessageAttribute { func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute { switch reactions { - case let .messageReactions(flags, results): + case let .messageReactions(flags, results, _): let min = (flags & (1 << 0)) != 0 var reactions = results.map { result -> MessageReaction in switch result { @@ -87,7 +87,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM extension ReactionsMessageAttribute { convenience init(apiReactions: Api.MessageReactions) { switch apiReactions { - case let .messageReactions(_, results): + case let .messageReactions(_, results, _): self.init(reactions: results.map { result in switch result { case let .reactionCount(flags, reaction, count): diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index bccf7ce12d..eef1d711d1 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -72,7 +72,9 @@ private func ~=(pattern: Regex, matchable: T) -> } public func sendAuthorizationCode(accountManager: AccountManager, account: UnauthorizedAccount, phoneNumber: String, apiId: Int32, apiHash: String, syncContacts: Bool) -> Signal { - let sendCode = Api.functions.auth.sendCode(phoneNumber: phoneNumber, apiId: apiId, apiHash: apiHash, settings: .codeSettings(flags: 0)) + var flags: Int32 = 0 + flags |= 1 << 5 //allowMissedCall + let sendCode = Api.functions.auth.sendCode(phoneNumber: phoneNumber, apiId: apiId, apiHash: apiHash, settings: .codeSettings(flags: flags, logoutTokens: nil)) let codeAndAccount = account.network.request(sendCode, automaticFloodWait: false) |> map { result in @@ -111,7 +113,7 @@ public func sendAuthorizationCode(accountManager: AccountManager timeout(20.0, queue: Queue.concurrentDefaultQueue(), alternate: .fail(.timeout)) return codeAndAccount - |> mapToSignal { (sentCode, account) -> Signal in + |> mapToSignal { sentCode, account -> Signal in return account.postbox.transaction { transaction -> UnauthorizedAccount in switch sentCode { case let .sentCode(_, type, phoneCodeHash, nextType, timeout): diff --git a/submodules/TelegramCore/Sources/State/AccountState.swift b/submodules/TelegramCore/Sources/State/AccountState.swift index 5660db71be..2d9dfe0b78 100644 --- a/submodules/TelegramCore/Sources/State/AccountState.swift +++ b/submodules/TelegramCore/Sources/State/AccountState.swift @@ -28,6 +28,8 @@ extension SentAuthorizationCodeType { self = .call(length: length) case let .sentCodeTypeFlashCall(pattern): self = .flashCall(pattern: pattern) + case let .sentCodeTypeMissedCall(prefix, length): + self = .missedCall(numberPrefix: prefix, length: length) } } } @@ -41,6 +43,8 @@ extension AuthorizationCodeNextType { self = .call case .codeTypeFlashCall: self = .flashCall + case .codeTypeMissedCall: + self = .missedCall } } } diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 6b05bf6441..4fb823fb76 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1479,7 +1479,7 @@ public final class AccountViewTracker { public func scheduledMessagesViewForLocation(_ chatLocation: ChatLocationInput, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: additionalData) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: additionalData) return withState(signal, { [weak self] () -> Int32 in if let strongSelf = self { return OSAtomicIncrement32(&strongSelf.nextViewId) @@ -1507,25 +1507,25 @@ public final class AccountViewTracker { } } - public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, count: Int, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) + let signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: true) } else { return .never() } } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, ignoreRelatedChats: Bool, messageId: MessageId, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, count: Int, ignoreRelatedChats: Bool, messageId: MessageId, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) + let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: false) } else { return .never() } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { let inputAnchor: HistoryViewInputAnchor switch index { @@ -1536,7 +1536,7 @@ public final class AccountViewTracker { case let .message(index): inputAnchor = .index(index) } - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: false) } else { return .never() diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift index 677f9bb838..910eca6e48 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift @@ -5,6 +5,7 @@ private enum SentAuthorizationCodeTypeValue: Int32 { case sms = 1 case call = 2 case flashCall = 3 + case missedCall = 4 } public enum SentAuthorizationCodeType: PostboxCoding, Equatable { @@ -12,6 +13,7 @@ public enum SentAuthorizationCodeType: PostboxCoding, Equatable { case sms(length: Int32) case call(length: Int32) case flashCall(pattern: String) + case missedCall(numberPrefix: String, length: Int32) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { @@ -23,6 +25,8 @@ public enum SentAuthorizationCodeType: PostboxCoding, Equatable { self = .call(length: decoder.decodeInt32ForKey("l", orElse: 0)) case SentAuthorizationCodeTypeValue.flashCall.rawValue: self = .flashCall(pattern: decoder.decodeStringForKey("p", orElse: "")) + case SentAuthorizationCodeTypeValue.missedCall.rawValue: + self = .missedCall(numberPrefix: decoder.decodeStringForKey("n", orElse: ""), length: decoder.decodeInt32ForKey("l", orElse: 0)) default: preconditionFailure() } @@ -42,35 +46,10 @@ public enum SentAuthorizationCodeType: PostboxCoding, Equatable { case let .flashCall(pattern): encoder.encodeInt32(SentAuthorizationCodeTypeValue.flashCall.rawValue, forKey: "v") encoder.encodeString(pattern, forKey: "p") - } - } - - public static func ==(lhs: SentAuthorizationCodeType, rhs: SentAuthorizationCodeType) -> Bool { - switch lhs { - case let .otherSession(length): - if case .otherSession(length) = rhs { - return true - } else { - return false - } - case let .sms(length): - if case .sms(length) = rhs { - return true - } else { - return false - } - case let .call(length): - if case .call(length) = rhs { - return true - } else { - return false - } - case let .flashCall(pattern): - if case .flashCall(pattern) = rhs { - return true - } else { - return false - } + case let .missedCall(numberPrefix, length): + encoder.encodeInt32(SentAuthorizationCodeTypeValue.missedCall.rawValue, forKey: "v") + encoder.encodeString(numberPrefix, forKey: "n") + encoder.encodeInt32(length, forKey: "l") } } } @@ -79,6 +58,7 @@ public enum AuthorizationCodeNextType: Int32 { case sms = 0 case call = 1 case flashCall = 2 + case missedCall = 3 } private enum UnauthorizedAccountStateContentsValue: Int32 { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift index 6364b4514a..f8e0f5ce7e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift @@ -37,7 +37,7 @@ public enum RequestChangeAccountPhoneNumberVerificationError { } func _internal_requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String) -> Signal { - return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: 0)), automaticFloodWait: false) + return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: 0, logoutTokens: nil)), automaticFloodWait: false) |> mapError { error -> RequestChangeAccountPhoneNumberVerificationError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift b/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift index 25a18b27dd..757644691d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Auth/CancelAccountReset.swift @@ -18,7 +18,7 @@ public enum RequestCancelAccountResetDataError { } func _internal_requestCancelAccountResetData(network: Network, hash: String) -> Signal { - return network.request(Api.functions.account.sendConfirmPhoneCode(hash: hash, settings: .codeSettings(flags: 0)), automaticFloodWait: false) + return network.request(Api.functions.account.sendConfirmPhoneCode(hash: hash, settings: .codeSettings(flags: 0, logoutTokens: nil)), automaticFloodWait: false) |> mapError { error -> RequestCancelAccountResetDataError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 6fc4f4e1fb..5c00200927 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -812,6 +812,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa Namespaces.Message.Cloud: holes ] )), + ignoreMessagesInTimestampRange: nil, count: 40, clipHoles: true, anchor: inputAnchor, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index bc99ee142a..d4c1c068b0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -188,7 +188,7 @@ public final class SparseMessageList { #else*/ count = 200 //#endif - self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: []) + self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: []) |> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in guard let strongSelf = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift index 4921d35659..43dfe11dff 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift @@ -19,7 +19,7 @@ public struct SecureIdPreparePhoneVerificationPayload { } public func secureIdPreparePhoneVerification(network: Network, value: SecureIdPhoneValue) -> Signal { - return network.request(Api.functions.account.sendVerifyPhoneCode(phoneNumber: value.phone, settings: .codeSettings(flags: 0)), automaticFloodWait: false) + return network.request(Api.functions.account.sendVerifyPhoneCode(phoneNumber: value.phone, settings: .codeSettings(flags: 0, logoutTokens: nil)), automaticFloodWait: false) |> mapError { error -> SecureIdPreparePhoneVerificationError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .flood diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift index 939a015821..cc3979a465 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -137,6 +137,8 @@ final class AuthorizationSequenceCodeEntryController: ViewController { minimalCodeLength = Int(length) case .flashCall: break + case let .missedCall(_, length): + minimalCodeLength = Int(length) } if self.controllerNode.currentCode.count < minimalCodeLength { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2ed4e48336..ad63769d24 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4511,7 +4511,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G location = .Initial(count: count) } - return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), context: context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) + return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -11815,6 +11815,47 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.presentInGlobalOverlay(contextController) } ) + + calendarScreen.completedWithRemoveMessagesInRange = { [weak self] range, type, dayCount, calendarSource in + guard let strongSelf = self else { + return + } + + //TODO:localize + var statusText: String + statusText = "Messages for \(dayCount) \(dayCount == 1 ? "day" : "days") deleted" + switch type { + case .forEveryone: + statusText += " for both sides" + default: + break + } + statusText += "." + + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = range + + strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, action: { value in + guard let strongSelf = self else { + return false + } + + if value == .commit { + let _ = calendarSource.removeMessagesInRange(minTimestamp: range.lowerBound, maxTimestamp: range.upperBound, type: type, completion: { + Queue.mainQueue().after(1.0, { + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil + }) + }) + return true + } else if value == .undo { + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil + return true + } + return false + }), in: .current) + } self.push(calendarScreen) dismissCalendarScreen = { [weak calendarScreen] in diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 91a0de2bba..f7864e8b80 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -82,6 +82,7 @@ struct ChatHistoryView { let lastHeaderId: Int64 let id: Int32 let locationInput: ChatHistoryLocationInput? + let ignoreMessagesInTimestampRange: ClosedRange? } enum ChatHistoryViewTransitionReason { @@ -467,6 +468,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { return id } + private let ignoreMessagesInTimestampRangePromise = ValuePromise?>(nil) + var ignoreMessagesInTimestampRange: ClosedRange? = nil { + didSet { + if self.ignoreMessagesInTimestampRange != oldValue { + self.ignoreMessagesInTimestampRangePromise.set(self.ignoreMessagesInTimestampRange) + } + } + } + private let galleryHiddenMesageAndMediaDisposable = MetaDisposable() private let messageProcessingManager = ChatMessageThrottledProcessingManager() @@ -763,7 +773,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let currentViewVersion = Atomic(value: nil) - let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?), NoError> + let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?), NoError> var isFirstTime = true var updateAllOnEachVersion = false if case let .custom(messages, at, _) = source { @@ -786,13 +796,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { scrollPosition = nil } - return (ChatHistoryViewUpdate.HistoryView(view: MessageHistoryView(tagMask: nil, namespaces: .all, entries: messages.reversed().map { MessageHistoryEntry(message: $0, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)) }, holeEarlier: hasMore, holeLater: false, isLoading: false), type: .Generic(type: version > 0 ? ViewUpdateType.Generic : ViewUpdateType.Initial), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: nil, buttonKeyboardMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil), id: 0), version, nil) + return (ChatHistoryViewUpdate.HistoryView(view: MessageHistoryView(tagMask: nil, namespaces: .all, entries: messages.reversed().map { MessageHistoryEntry(message: $0, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)) }, holeEarlier: hasMore, holeLater: false, isLoading: false), type: .Generic(type: version > 0 ? ViewUpdateType.Generic : ViewUpdateType.Initial), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: nil, buttonKeyboardMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil), id: 0), version, nil, nil) } } else { - historyViewUpdate = self.chatHistoryLocationPromise.get() - |> distinctUntilChanged - |> mapToSignal { location in - return chatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduledMessages, fixedCombinedReadStates: fixedCombinedReadStates.with { $0 }, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, additionalData: additionalData, orderStatistics: [.combinedLocation]) + historyViewUpdate = combineLatest(queue: .mainQueue(), + self.chatHistoryLocationPromise.get(), + self.ignoreMessagesInTimestampRangePromise.get() + ) + |> distinctUntilChanged(isEqual: { lhs, rhs in + if lhs.0 != rhs.0 { + return false + } + if lhs.1 != rhs.1 { + return false + } + return true + }) + |> mapToSignal { location, ignoreMessagesInTimestampRange in + return chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduledMessages, fixedCombinedReadStates: fixedCombinedReadStates.with { $0 }, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, additionalData: additionalData, orderStatistics: [.combinedLocation]) |> beforeNext { viewUpdate in switch viewUpdate { case let .HistoryView(view, _, _, _, _, _, _): @@ -801,7 +822,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { break } } - |> map { view -> (ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?) in + |> map { view -> (ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?) in let version = currentViewVersion.modify({ value in if let value = value { return value + 1 @@ -809,7 +830,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { return 0 } })! - return (view, version, location) + return (view, version, location, ignoreMessagesInTimestampRange) } } } @@ -1117,7 +1138,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { adMessages: adMessages ) let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0 - let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2) + let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages)) let previous = previousValueAndVersion?.0 let previousSelectedMessages = previousValueAndVersion?.2 diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index 91ee4348bd..3072fadeec 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -18,7 +18,7 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, c tagMask = .pinned } - return (chatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduled, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: false, additionalData: additionalData, orderStatistics: orderStatistics) + return (chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: nil, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduled, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: false, additionalData: additionalData, orderStatistics: orderStatistics) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -36,7 +36,7 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, c |> restartIfError } -func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { +func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { let account = context.account if scheduled { var first = true @@ -83,9 +83,9 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A var fadeIn = false let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> if let tagMask = tagMask { - signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), index: .upperBound, anchorIndex: .upperBound, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics) + signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: .upperBound, anchorIndex: .upperBound, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics) } else { - signal = account.viewTracker.aroundMessageOfInterestHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), count: count, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) + signal = account.viewTracker.aroundMessageOfInterestHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) } return signal |> map { view, updateType, initialData -> ChatHistoryViewUpdate in @@ -175,9 +175,9 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> switch searchLocation { case let .index(index): - signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), index: .message(index), anchorIndex: .message(index), count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) + signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: .message(index), anchorIndex: .message(index), count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) case let .id(id): - signal = account.viewTracker.aroundIdMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: id, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) + signal = account.viewTracker.aroundIdMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: id, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) } return signal |> map { view, updateType, initialData -> ChatHistoryViewUpdate in @@ -225,7 +225,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A } case let .Navigation(index, anchorIndex, count, _): var first = true - return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), index: index, anchorIndex: anchorIndex, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in + return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: index, anchorIndex: anchorIndex, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) let genericType: ViewUpdateType @@ -241,7 +241,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up let chatScrollPosition = ChatHistoryViewScrollPosition.index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated, highlight: highlight) var first = true - return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), index: index, anchorIndex: anchorIndex, count: 128, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) + return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: index, anchorIndex: anchorIndex, count: 128, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index 6396344dc5..44839c6c05 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -486,7 +486,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { return .single(nil) } - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(message.index), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: message.index, position: .exact) { return .single((message, aroundMessages)) @@ -584,7 +584,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } let historySignal = inputIndex |> mapToSignal { inputIndex -> Signal<(Message, [Message])?, NoError> in - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(inputIndex), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(inputIndex), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation { @@ -614,7 +614,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } else { viewIndex = .lowerBound } - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: viewIndex, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: viewIndex, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation {