diff --git a/Telegram/BUILD b/Telegram/BUILD index c5a4f7e67b..03c601571e 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -1578,7 +1578,7 @@ ios_application( ":NotificationContentExtension", ":NotificationServiceExtension", ":IntentsExtension", -# ":WidgetExtension", + ":WidgetExtension", ], }), watch_application = select({ diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 8ef09e3458..b4ba6cc3b2 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -701,7 +701,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag } @available(iOSApplicationExtension 14.0, iOS 14.0, *) - func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) { + func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) { let _ = (self.accountPromise.get() |> take(1) |> mapToSignal { account -> Signal<[Friend], NoError> in diff --git a/Telegram/SiriIntents/Intents.intentdefinition b/Telegram/SiriIntents/Intents.intentdefinition index e44976dabf..dd46b6e4ca 100644 --- a/Telegram/SiriIntents/Intents.intentdefinition +++ b/Telegram/SiriIntents/Intents.intentdefinition @@ -146,7 +146,7 @@ INIntentParameterArraySizeSize - 8 + 2 INIntentParameterArraySizeSizeClass Medium diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 2a02c2d21c..41f86bc74b 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -171,7 +171,7 @@ struct Provider: IntentTimelineProvider { }, badge: badge, message: mappedMessage)) } } - return WidgetDataPeers(accountPeerId: widgetPeers.accountPeerId, peers: peers) + return WidgetDataPeers(accountPeerId: widgetPeers.accountPeerId, peers: peers, updateTimestamp: Int32(Date().timeIntervalSince1970)) }) |> deliverOnMainQueue).start(next: { peers in completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(peers))], policy: .atEnd)) @@ -442,7 +442,7 @@ struct WidgetView: View { }) } - func chatContentView(_ index: Int) -> AnyView { + func chatContentView(_ index: Int, size: CGSize) -> AnyView { let peers: WidgetDataPeers switch data { case let .peers(peersValue): @@ -454,16 +454,57 @@ struct WidgetView: View { return AnyView(Spacer()) } + let itemHeight = (size.height - 22.0) / 2.0 + return AnyView( Link(destination: URL(string: linkForPeer(id: peers.peers[index].id))!, label: { HStack(alignment: .center, spacing: 0.0, content: { - AvatarItemView(accountPeerId: peers.accountPeerId, peer: peers.peers[index], itemSize: 60.0, displayBadge: false).frame(width: 60.0, height: 60.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0)) + AvatarItemView(accountPeerId: peers.accountPeerId, peer: peers.peers[index], itemSize: 54.0, displayBadge: false).frame(width: 54.0, height: 54.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0)) chatContent(peers.peers[index]).frame(maxWidth: .infinity).padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 10.0)) }) - }) + }).position(x: size.width / 2.0, y: (itemHeight * 2.0) / 4.0 + CGFloat(index) * itemHeight).frame(width: size.width, height: itemHeight, alignment: .leading) ) } + func chatSeparatorView(size: CGSize) -> some View { + let separatorWidth = size.width - 54.0 - 20.0 + let itemHeight = (size.height - 22.0) / 2.0 + return Rectangle().foregroundColor(getSeparatorColor()).position(x: (54.0 + 20.0 + separatorWidth) / 2.0, y: itemHeight / 2.0).frame(width: separatorWidth, height: 0.33, alignment: .leading) + } + + func chatsUpdateBackgroundView(size: CGSize) -> some View { + return Rectangle().foregroundColor(getUpdatedBackgroundColor()).position(x: size.width / 2.0, y: size.height - 22.0 - 11.0).frame(width: size.width, height: 22.0, alignment: .leading) + } + + func chatsUpdateTimestampView(size: CGSize) -> some View { + let text: String + switch data { + case let .peers(peersValue): + let date = Date(timeIntervalSince1970: Double(peersValue.updateTimestamp)) + let calendar = Calendar.current + //TODO:localize + if !calendar.isDate(Date(), inSameDayAs: date) { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .none + text = "updated on \(formatter.string(from: date))" + } else { + let formatter = DateFormatter() + formatter.dateStyle = .none + formatter.timeStyle = .short + text = "updated at \(formatter.string(from: date))" + } + default: + text = "" + } + + return HStack(alignment: .center, spacing: 0.0, content: { + Text(text) + .font(Font.system(size: 12.0)) + .foregroundColor(getUpdatedTextColor()) + }).position(x: size.width / 2.0, y: size.height - 22.0 - 11.0).frame(width: size.width, height: 22.0, alignment: .leading) + } + func getSeparatorColor() -> Color { switch colorScheme { case .light: @@ -475,12 +516,36 @@ struct WidgetView: View { } } + func getUpdatedBackgroundColor() -> Color { + switch colorScheme { + case .light: + return Color(.sRGB, red: 242.0 / 255.0, green: 242.0 / 255.0, blue: 247.0 / 255.0, opacity: 1.0) + case .dark: + return Color(.sRGB, red: 21.0 / 255.0, green: 21.0 / 255.0, blue: 21.0 / 255.0, opacity: 1.0) + @unknown default: + return .secondary + } + } + + func getUpdatedTextColor() -> Color { + switch colorScheme { + case .light: + return Color(.sRGB, red: 142.0 / 255.0, green: 142.0 / 255.0, blue: 146.0 / 255.0, opacity: 1.0) + case .dark: + return Color(.sRGB, red: 142.0 / 255.0, green: 142.0 / 255.0, blue: 146.0 / 255.0, opacity: 1.0) + @unknown default: + return .secondary + } + } + var body: some View { GeometryReader(content: { geometry in - ZStack { - chatContentView(0).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading) - chatContentView(1).position(x: geometry.size.width / 2.0, y: geometry.size.height / 2.0 + geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading) - Rectangle().foregroundColor(getSeparatorColor()).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: 0.33, alignment: .leading) + return ZStack { + chatContentView(0, size: geometry.size) + chatContentView(1, size: geometry.size) + chatSeparatorView(size: geometry.size) + chatsUpdateBackgroundView(size: geometry.size) + chatsUpdateTimestampView(size: geometry.size) } }) .padding(0.0) diff --git a/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift b/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift index f084cfa88d..3463b324f1 100644 --- a/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift +++ b/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift @@ -13,6 +13,7 @@ public enum ChatListFilterSettingsHeaderAnimation { case folders case newFolder case discussionGroupSetup + case autoRemove } public class ChatListFilterSettingsHeaderItem: ListViewItem, ItemListItem { @@ -106,7 +107,26 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode { return { item, params, neighbors in let leftInset: CGFloat = 32.0 + params.leftInset - let topInset: CGFloat = 92.0 + + let animationName: String + var size = 192 + var insetDifference = 100 + var playbackMode: AnimatedStickerPlaybackMode = .once + switch item.animation { + case .folders: + animationName = "ChatListFolders" + case .newFolder: + animationName = "ChatListNewFolder" + case .discussionGroupSetup: + animationName = "DiscussionGroupSetup" + case .autoRemove: + animationName = "MessageAutoRemove" + size = 260 + insetDifference = 120 + playbackMode = .loop + } + + let topInset: CGFloat = CGFloat(size - insetDifference) let attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: item.theme.list.freeTextColor) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) @@ -119,17 +139,8 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode { return (layout, { [weak self] in if let strongSelf = self { if strongSelf.item == nil { - let animationName: String - switch item.animation { - case .folders: - animationName = "ChatListFolders" - case .newFolder: - animationName = "ChatListNewFolder" - case .discussionGroupSetup: - animationName = "DiscussionGroupSetup" - } if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") { - strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: size, height: size, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil)) strongSelf.animationNode.visibility = true } } @@ -137,7 +148,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode { strongSelf.item = item strongSelf.accessibilityLabel = attributedText.string - let iconSize = CGSize(width: 96.0, height: 96.0) + let iconSize = CGSize(width: CGFloat(size) / 2.0, height: CGFloat(size) / 2.0) strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize) strongSelf.animationNode.updateLayout(size: iconSize) diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h index 500c2712a2..514aad3bda 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h @@ -13,6 +13,7 @@ @property (nonatomic, assign) CGFloat startValue; @property (nonatomic, assign) CGFloat value; +@property (nonatomic, assign) int minimumUndottedValue; @property (nonatomic, assign) CGFloat markValue; diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m index 22bd9ee45d..21fcdc9223 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m @@ -174,13 +174,59 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f; CGContextSetBlendMode(context, kCGBlendModeCopy); } - CGContextSetFillColorWithColor(context, _backColor.CGColor); - [self drawRectangle:backFrame cornerRadius:self.trackCornerRadius context:context]; + if (_minimumUndottedValue > 0 && self.positionsCount > 1) { + CGContextSetLineWidth(context, backFrame.size.height); + CGContextSetLineCap(context, kCGLineCapRound); + + for (NSInteger i = 1; i < self.positionsCount; i++) + { + CGFloat previousX = margin + totalLength / (self.positionsCount - 1) * (i - 1); + CGFloat currentX = margin + totalLength / (self.positionsCount - 1) * i; + + if (_minimumUndottedValue < i) { + CGFloat normalDashWidth = 16.0f; + CGFloat dashFraction = 0.6f; + CGFloat totalLineWidth = currentX - previousX; + int numberOfDashes = (int)floor((double)(totalLineWidth / normalDashWidth)); + CGFloat dashWidth = (totalLineWidth / (CGFloat)numberOfDashes); + + CGFloat innerWidth = dashWidth * dashFraction - 2.0f; + CGFloat innerOffset = (dashWidth - innerWidth) / 2.0f; + + CGFloat dottedX = previousX; + + while (dottedX + innerWidth < currentX) { + bool highlighted = dottedX + dashWidth / 2.0f < CGRectGetMaxX(trackFrame); + + CGContextSetStrokeColorWithColor(context, highlighted ? _trackColor.CGColor : _backColor.CGColor); + + CGContextMoveToPoint(context, dottedX + innerOffset, CGRectGetMidY(backFrame)); + CGContextAddLineToPoint(context, dottedX + innerOffset + innerWidth, CGRectGetMidY(backFrame)); + CGContextStrokePath(context); + + dottedX += dashWidth; + } + } else { + bool highlighted = (previousX + (currentX - previousX) / 2.0f) < CGRectGetMaxX(trackFrame); + CGContextSetStrokeColorWithColor(context, highlighted ? _trackColor.CGColor : _backColor.CGColor); + + CGContextMoveToPoint(context, previousX, CGRectGetMidY(backFrame)); + CGContextAddLineToPoint(context, currentX, CGRectGetMidY(backFrame)); + CGContextStrokePath(context); + } + } + } else { + CGContextSetFillColorWithColor(context, _backColor.CGColor); + [self drawRectangle:backFrame cornerRadius:self.trackCornerRadius context:context]; + } CGContextSetBlendMode(context, kCGBlendModeNormal); - CGContextSetFillColorWithColor(context, _trackColor.CGColor); - [self drawRectangle:trackFrame cornerRadius:self.trackCornerRadius context:context]; + if (_minimumUndottedValue > 0) { + } else { + CGContextSetFillColorWithColor(context, _trackColor.CGColor); + [self drawRectangle:trackFrame cornerRadius:self.trackCornerRadius context:context]; + } if (!_startHidden || self.displayEdges) { diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift new file mode 100644 index 0000000000..c3b0450bf7 --- /dev/null +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift @@ -0,0 +1,319 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import SyncCore +import TelegramPresentationData +import ItemListUI +import PresentationDataUtils +import AccountContext +import ChatListFilterSettingsHeaderItem + +private final class PeerAutoremoveSetupArguments { + let toggleGlobal: (Bool) -> Void + let updateValue: (Int32) -> Void + + init(toggleGlobal: @escaping (Bool) -> Void, updateValue: @escaping (Int32) -> Void) { + self.toggleGlobal = toggleGlobal + self.updateValue = updateValue + } +} + +private enum PeerAutoremoveSetupSection: Int32 { + case header + case time + case global +} + +private enum PeerAutoremoveSetupEntry: ItemListNodeEntry { + case header + case timeHeader(String) + case timeValue(Int32) + case timeComment(String) + case globalSwitch(String, Bool) + + var section: ItemListSectionId { + switch self { + case .header: + return PeerAutoremoveSetupSection.header.rawValue + case .timeHeader, .timeValue, .timeComment: + return PeerAutoremoveSetupSection.time.rawValue + case .globalSwitch: + return PeerAutoremoveSetupSection.global.rawValue + } + } + + var stableId: Int32 { + switch self { + case .header: + return 0 + case .timeHeader: + return 1 + case .timeValue: + return 2 + case .timeComment: + return 3 + case .globalSwitch: + return 4 + } + } + + static func ==(lhs: PeerAutoremoveSetupEntry, rhs: PeerAutoremoveSetupEntry) -> Bool { + switch lhs { + case .header: + if case .header = rhs { + return true + } else { + return false + } + case let .timeHeader(lhsText): + if case let .timeHeader(rhsText) = rhs, lhsText == rhsText { + return true + } else { + return false + } + case let .timeValue(lhsValue): + if case let .timeValue(rhsValue) = rhs, lhsValue == rhsValue { + return true + } else { + return false + } + case let .timeComment(lhsText): + if case let .timeComment(rhsText) = rhs, lhsText == rhsText { + return true + } else { + return false + } + case let .globalSwitch(lhsText, lhsValue): + if case let .globalSwitch(rhsText, rhsValue) = rhs, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + } + } + + static func <(lhs: PeerAutoremoveSetupEntry, rhs: PeerAutoremoveSetupEntry) -> Bool { + return lhs.stableId < rhs.stableId + } + + func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { + let arguments = arguments as! PeerAutoremoveSetupArguments + switch self { + case .header: + return ChatListFilterSettingsHeaderItem(theme: presentationData.theme, text: "", animation: .autoRemove, sectionId: self.section) + case let .timeHeader(text): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + case let .timeValue(value): + return PeerRemoveTimeoutItem(theme: presentationData.theme, value: value, maxValue: Int32.max, enabled: true, sectionId: self.section, updated: { value in + arguments.updateValue(value) + }, tag: nil) + case let .timeComment(text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .globalSwitch(text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleGlobal(value) + }) + } + } +} + +private struct PeerAutoremoveSetupState: Equatable { + var changedValue: Int32? + var changedGlobalValue: Bool? + var applyingSetting: Bool = false +} + +private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: PresentationData, defaultValue: Int32?, defaultGlobalValue: Bool, state: PeerAutoremoveSetupState) -> [PeerAutoremoveSetupEntry] { + var entries: [PeerAutoremoveSetupEntry] = [] + let value = state.changedValue ?? defaultValue + let globalValue = state.changedGlobalValue ?? defaultGlobalValue + + //TODO:localize + entries.append(.header) + entries.append(.timeHeader("AUTO-DELETE MESSAGES")) + entries.append(.timeValue(value ?? Int32.max)) + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + entries.append(.timeComment("Automatically delete messages sent in this channel after a certain period of time.")) + } else { + entries.append(.timeComment("Automatically delete messages sent in this chat after a certain period of time.")) + } + if let user = peer as? TelegramUser { + entries.append(.globalSwitch("Also auto-delete for \(user.compactDisplayTitle)", globalValue)) + } + + return entries +} + +public enum PeerAutoremoveSetupScreenResult { + case unchanged + case updated(Int32?) +} + +public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, completion: @escaping (PeerAutoremoveSetupScreenResult) -> Void = { _ in }) -> ViewController { + let statePromise = ValuePromise(PeerAutoremoveSetupState(), ignoreRepeated: true) + let stateValue = Atomic(value: PeerAutoremoveSetupState()) + let updateState: ((PeerAutoremoveSetupState) -> PeerAutoremoveSetupState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + var pushControllerImpl: ((ViewController) -> Void)? + var dismissImpl: (() -> Void)? + + let actionsDisposable = DisposableSet() + + let applyDisposable = MetaDisposable() + actionsDisposable.add(applyDisposable) + + let arguments = PeerAutoremoveSetupArguments(toggleGlobal: { value in + updateState { state in + var state = state + state.changedGlobalValue = value + return state + } + }, updateValue: { value in + updateState { state in + var state = state + state.changedValue = value + return state + } + }) + + let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.viewTracker.peerView(peerId)) + |> deliverOnMainQueue + |> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState, Any)) in + var defaultValue: Int32 = Int32.max + var maxValue: Int32 = Int32.max + var defaultGlobalValue = true + if let cachedData = view.cachedData as? CachedChannelData { + if case let .known(value) = cachedData.autoremoveTimeout { + defaultValue = value?.myValue ?? Int32.max + maxValue = value?.peerValue ?? Int32.max + defaultGlobalValue = value?.isGlobal ?? true + } + } else if let cachedData = view.cachedData as? CachedGroupData { + if case let .known(value) = cachedData.autoremoveTimeout { + defaultValue = value?.myValue ?? Int32.max + maxValue = value?.peerValue ?? Int32.max + defaultGlobalValue = value?.isGlobal ?? true + } + } else if let cachedData = view.cachedData as? CachedUserData { + if case let .known(value) = cachedData.autoremoveTimeout { + defaultValue = value?.myValue ?? Int32.max + maxValue = value?.peerValue ?? Int32.max + defaultGlobalValue = value?.isGlobal ?? true + } + } + + let peer = view.peers[view.peerId] + + let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + dismissImpl?() + }) + var rightNavigationButton: ItemListNavigationButton? + if state.applyingSetting { + rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) + } else { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + var value: Int32? + var globalValue: Bool? + updateState { state in + var state = state + state.applyingSetting = true + value = state.changedValue + globalValue = state.changedGlobalValue + return state + } + var updated = false + if let value = value, value != defaultValue { + updated = true + } + if let globalValue = globalValue, globalValue != defaultGlobalValue { + updated = true + } + if updated { + let resolvedValue = value ?? defaultValue + let resolvedGlobalValue = globalValue ?? defaultGlobalValue + + let signal = setChatMessageAutoremoveTimeoutInteractively(account: context.account, peerId: peerId, timeout: resolvedValue == Int32.max ? nil : resolvedValue, isGlobal: resolvedGlobalValue) + |> deliverOnMainQueue + + applyDisposable.set((signal + |> deliverOnMainQueue).start(error: { _ in + }, completed: { + dismissImpl?() + if resolvedValue != defaultValue { + completion(.updated(resolvedValue)) + } else { + completion(.unchanged) + } + })) + } else { + dismissImpl?() + completion(.unchanged) + } + /*if let value = value, value != defaultValue { + if peerId.namespace == Namespaces.Peer.CloudGroup { + let signal = convertGroupToSupergroup(account: context.account, peerId: peerId) + |> mapToSignal { upgradedPeerId -> Signal in + return updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: upgradedPeerId, historyAvailableForNewMembers: value) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + |> then(.single(upgradedPeerId)) + |> castError(ConvertGroupToSupergroupError.self) + } + |> deliverOnMainQueue + applyDisposable.set((signal + |> deliverOnMainQueue).start(next: { upgradedPeerId in + if let upgradedPeerId = upgradedPeerId { + upgradedToSupergroup(upgradedPeerId, { + dismissImpl?() + }) + } + }, error: { error in + switch error { + case .tooManyChannels: + pushControllerImpl?(oldChannelsController(context: context, intent: .upgrade)) + default: + break + } + })) + } else { + applyDisposable.set((updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: peerId, historyAvailableForNewMembers: value) + |> deliverOnMainQueue).start(completed: { + dismissImpl?() + })) + } + } else { + dismissImpl?() + }*/ + }) + } + + //TODO:localize + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Auto-Deletion"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, defaultValue: defaultValue, defaultGlobalValue: defaultGlobalValue, state: state), style: .blocks) + + return (controllerState, (listState, arguments)) + } + |> afterDisposed { + actionsDisposable.dispose() + } + + let controller = ItemListController(context: context, state: signal) + controller.navigationPresentation = .modal + dismissImpl = { [weak controller] in + controller?.view.endEditing(true) + controller?.dismiss() + } + pushControllerImpl = { [weak controller] c in + controller?.push(c) + } + return controller +} diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift new file mode 100644 index 0000000000..f44fa50fb5 --- /dev/null +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift @@ -0,0 +1,324 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramCore +import SyncCore +import TelegramPresentationData +import TelegramUIPreferences +import LegacyComponents +import ItemListUI +import PresentationDataUtils +import AppBundle + +class PeerRemoveTimeoutItem: ListViewItem, ItemListItem { + let theme: PresentationTheme + let value: Int32 + let maxValue: Int32 + let enabled: Bool + let sectionId: ItemListSectionId + let updated: (Int32) -> Void + let tag: ItemListItemTag? + + init(theme: PresentationTheme, value: Int32, maxValue: Int32, enabled: Bool = true, sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void, tag: ItemListItemTag? = nil) { + self.theme = theme + self.value = value + self.maxValue = maxValue + self.enabled = enabled + self.sectionId = sectionId + self.updated = updated + self.tag = tag + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = PeerRemoveTimeoutItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply() }) + }) + } + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? PeerRemoveTimeoutItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } + } + } +} + +private func generateKnobImage() -> UIImage? { + return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setShadow(offset: CGSize(width: 0.0, height: -1.0), blur: 3.5, color: UIColor(white: 0.0, alpha: 0.25).cgColor) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0))) + }) +} + +class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private var sliderView: TGPhotoEditorSliderView? + private let titleNodes: [TextNode] + private let disabledOverlayNode: ASDisplayNode + + private var item: PeerRemoveTimeoutItem? + private var layoutParams: ListViewItemLayoutParams? + + var tag: ItemListItemTag? { + return self.item?.tag + } + + init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + + self.disabledOverlayNode = ASDisplayNode() + + self.titleNodes = (0 ..< 3).map { _ in + return TextNode() + } + + super.init(layerBacked: false, dynamicBounce: false) + + self.titleNodes.forEach(self.addSubnode) + + self.addSubnode(self.disabledOverlayNode) + } + + override func didLoad() { + super.didLoad() + + let sliderView = TGPhotoEditorSliderView() + sliderView.enablePanHandling = true + sliderView.trackCornerRadius = 1.0 + sliderView.lineSize = 2.0 + sliderView.dotSize = 5.0 + sliderView.minimumValue = 0.0 + sliderView.maximumValue = 2.0 + sliderView.startValue = 0.0 + sliderView.positionsCount = 3 + sliderView.useLinesForPositions = true + sliderView.minimumUndottedValue = 2 + sliderView.disablesInteractiveTransitionGestureRecognizer = true + if let item = self.item, let params = self.layoutParams { + sliderView.isUserInteractionEnabled = item.enabled + + let value: CGFloat + switch item.value { + case 24 * 60 * 60: + value = 0.0 + case 7 * 24 * 60 * 60: + value = 1.0 + default: + value = 2.0 + } + sliderView.value = value + sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor + sliderView.backColor = item.theme.list.disclosureArrowColor + sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor + sliderView.knobImage = generateKnobImage() + + let sliderInset: CGFloat = params.leftInset + 16.0 + + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 38.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) + } + self.view.insertSubview(sliderView, belowSubview: self.disabledOverlayNode.view) + sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged) + self.sliderView = sliderView + } + + func asyncLayout() -> (_ item: PeerRemoveTimeoutItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + let currentItem = self.item + + let makeTitleNodeLayouts = self.titleNodes.map(TextNode.asyncLayout) + + return { item, params, neighbors in + var themeUpdated = false + if currentItem?.theme !== item.theme { + themeUpdated = true + } + + let contentSize: CGSize + var insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + + let titleLayouts = zip(0 ..< makeTitleNodeLayouts.count, makeTitleNodeLayouts).map { index, makeLayout -> (TextNodeLayout, () -> TextNode) in + let text: String + //TODO:localize + switch index { + case 0: + text = "After 24 hours" + case 1: + text = "After 7 days" + default: + text = "Never" + } + return makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) + } + + contentSize = CGSize(width: params.width, height: 88.0) + insets = itemListNeighborsGroupedInsets(neighbors) + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + if let strongSelf = self { + let firstTime = strongSelf.item == nil + strongSelf.item = item + strongSelf.layoutParams = params + + let leftInset = 16.0 + params.leftInset + + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + strongSelf.disabledOverlayNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4) + strongSelf.disabledOverlayNode.isHidden = item.enabled + strongSelf.disabledOverlayNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: 44.0)) + + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = params.leftInset + 16.0 + bottomStripeOffset = -separatorHeight + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + + zip(0 ..< titleLayouts.count, titleLayouts).forEach { index, layoutAndApply in + let textNode = layoutAndApply.1() + + let size = layoutAndApply.0.size + switch index { + case 0: + textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 13.0), size: size) + case 1: + textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - size.width) / 2.0), y: 13.0), size: size) + default: + textNode.frame = CGRect(origin: CGPoint(x: params.width - size.width - 16.0, y: 13.0), size: size) + } + } + + if let sliderView = strongSelf.sliderView { + sliderView.isUserInteractionEnabled = item.enabled + sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor + + if themeUpdated { + sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor + sliderView.backColor = item.theme.list.disclosureArrowColor + sliderView.knobImage = generateKnobImage() + } + + let value: CGFloat + switch item.value { + case 24 * 60 * 60: + value = 0.0 + case 7 * 24 * 60 * 60: + value = 1.0 + default: + value = 2.0 + } + if firstTime { + sliderView.value = value + } + + let sliderInset: CGFloat = leftInset + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 38.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) + } + } + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } + + @objc func sliderValueChanged() { + guard let sliderView = self.sliderView else { + return + } + let value: Int32 + switch sliderView.value { + case 0.0: + value = 24 * 60 * 60 + case 1.0: + value = 7 * 24 * 60 * 60 + default: + value = Int32.max + } + self.item?.updated(value) + } +} + diff --git a/submodules/SyncCore/Sources/CachedChannelData.swift b/submodules/SyncCore/Sources/CachedChannelData.swift index 0ea3ce3282..631a121990 100644 --- a/submodules/SyncCore/Sources/CachedChannelData.swift +++ b/submodules/SyncCore/Sources/CachedChannelData.swift @@ -429,15 +429,7 @@ public final class CachedChannelData: CachedPeerData { self.slowModeTimeout = decoder.decodeOptionalInt32ForKey("smt") self.slowModeValidUntilTimestamp = decoder.decodeOptionalInt32ForKey("smv") self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false) - if let value = decoder.decodeOptionalInt32ForKey("art") { - if value == -1 { - self.autoremoveTimeout = .known(nil) - } else { - self.autoremoveTimeout = .known(value) - } - } else { - self.autoremoveTimeout = .unknown - } + self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown self.statsDatacenterId = decoder.decodeInt32ForKey("sdi", orElse: 0) self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init) @@ -545,16 +537,7 @@ public final class CachedChannelData: CachedPeerData { encoder.encodeNil(forKey: "smv") } encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm") - switch self.autoremoveTimeout { - case let .known(value): - if let value = value { - encoder.encodeInt32(value, forKey: "art") - } else { - encoder.encodeInt32(-1, forKey: "art") - } - case .unknown: - encoder.encodeNil(forKey: "art") - } + encoder.encodeObject(self.autoremoveTimeout, forKey: "artv") encoder.encodeInt32(self.statsDatacenterId, forKey: "sdi") if let invitedBy = self.invitedBy { diff --git a/submodules/SyncCore/Sources/CachedGroupData.swift b/submodules/SyncCore/Sources/CachedGroupData.swift index 90e192c062..bce8f701fe 100644 --- a/submodules/SyncCore/Sources/CachedGroupData.swift +++ b/submodules/SyncCore/Sources/CachedGroupData.swift @@ -131,15 +131,7 @@ public final class CachedGroupData: CachedPeerData { self.about = decoder.decodeOptionalStringForKey("ab") self.flags = CachedGroupFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0)) self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false) - if let value = decoder.decodeOptionalInt32ForKey("art") { - if value == -1 { - self.autoremoveTimeout = .known(nil) - } else { - self.autoremoveTimeout = .known(value) - } - } else { - self.autoremoveTimeout = .unknown - } + self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init) @@ -207,16 +199,7 @@ public final class CachedGroupData: CachedPeerData { } encoder.encodeInt32(self.flags.rawValue, forKey: "fl") encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm") - switch self.autoremoveTimeout { - case let .known(value): - if let value = value { - encoder.encodeInt32(value, forKey: "art") - } else { - encoder.encodeInt32(-1, forKey: "art") - } - case .unknown: - encoder.encodeNil(forKey: "art") - } + encoder.encodeObject(self.autoremoveTimeout, forKey: "artv") if let invitedBy = self.invitedBy { encoder.encodeInt64(invitedBy.toInt64(), forKey: "invBy") diff --git a/submodules/SyncCore/Sources/CachedUserData.swift b/submodules/SyncCore/Sources/CachedUserData.swift index e3e2bf30c9..3d570180fe 100644 --- a/submodules/SyncCore/Sources/CachedUserData.swift +++ b/submodules/SyncCore/Sources/CachedUserData.swift @@ -1,9 +1,56 @@ import Foundation import Postbox -public enum CachedPeerAutoremoveTimeout: Equatable { +public enum CachedPeerAutoremoveTimeout: Equatable, PostboxCoding { + public struct Value: Equatable, PostboxCoding { + public var myValue: Int32 + public var peerValue: Int32 + public var isGlobal: Bool + + public init(myValue: Int32, peerValue: Int32, isGlobal: Bool) { + self.myValue = myValue + self.peerValue = peerValue + self.isGlobal = isGlobal + } + + public init(decoder: PostboxDecoder) { + self.myValue = decoder.decodeInt32ForKey("myValue", orElse: 7 * 60 * 60) + self.peerValue = decoder.decodeInt32ForKey("peerValue", orElse: 7 * 60 * 60) + self.isGlobal = decoder.decodeInt32ForKey("isGlobal", orElse: 1) != 0 + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.myValue, forKey: "myValue") + encoder.encodeInt32(self.peerValue, forKey: "peerValue") + encoder.encodeInt32(self.isGlobal ? 1 : 0, forKey: "isGlobal") + } + } + case unknown - case known(Int32?) + case known(Value?) + + public init(decoder: PostboxDecoder) { + switch decoder.decodeInt32ForKey("_v", orElse: 0) { + case 1: + self = .known(decoder.decodeObjectForKey("v", decoder: Value.init(decoder:)) as? Value) + default: + self = .unknown + } + } + + public func encode(_ encoder: PostboxEncoder) { + switch self { + case .unknown: + encoder.encodeInt32(0, forKey: "_v") + case let .known(value): + encoder.encodeInt32(1, forKey: "_v") + if let value = value { + encoder.encodeObject(value, forKey: "v") + } else { + encoder.encodeNil(forKey: "v") + } + } + } } public final class CachedUserData: CachedPeerData { @@ -82,15 +129,7 @@ public final class CachedUserData: CachedPeerData { self.callsPrivate = decoder.decodeInt32ForKey("cp", orElse: 0) != 0 self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0 self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false) - if let value = decoder.decodeOptionalInt32ForKey("art") { - if value == -1 { - self.autoremoveTimeout = .known(nil) - } else { - self.autoremoveTimeout = .known(value) - } - } else { - self.autoremoveTimeout = .unknown - } + self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown var messageIds = Set() if let pinnedMessageId = self.pinnedMessageId { @@ -131,16 +170,7 @@ public final class CachedUserData: CachedPeerData { encoder.encodeInt32(self.callsPrivate ? 1 : 0, forKey: "cp") encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm") encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm") - switch self.autoremoveTimeout { - case let .known(value): - if let value = value { - encoder.encodeInt32(value, forKey: "art") - } else { - encoder.encodeInt32(-1, forKey: "art") - } - case .unknown: - encoder.encodeNil(forKey: "art") - } + encoder.encodeObject(self.autoremoveTimeout, forKey: "artv") } public func isEqual(to: CachedPeerData) -> Bool { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index cb778191f7..1b6e769223 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) } dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } - dict[-1415563086] = { return Api.ChatFull.parse_channelFull($0) } - dict[-261341160] = { return Api.ChatFull.parse_chatFull($0) } + dict[-66811386] = { return Api.ChatFull.parse_channelFull($0) } + dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) } dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } @@ -106,7 +106,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[483104362] = { return Api.RichText.parse_textPhone($0) } dict[136105807] = { return Api.RichText.parse_textImage($0) } dict[894777186] = { return Api.RichText.parse_textAnchor($0) } - dict[328899191] = { return Api.UserFull.parse_userFull($0) } + dict[-1522240089] = { return Api.UserFull.parse_userFull($0) } dict[-292807034] = { return Api.InputChannel.parse_inputChannelEmpty($0) } dict[-1343524562] = { return Api.InputChannel.parse_inputChannel($0) } dict[707290417] = { return Api.InputChannel.parse_inputChannelFromMessage($0) } @@ -118,6 +118,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) } dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) } dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) } + dict[-815649386] = { return Api.PeerHistoryTTL.parse_peerHistoryTTLPM($0) } + dict[1041354473] = { return Api.PeerHistoryTTL.parse_peerHistoryTTL($0) } dict[84438264] = { return Api.WallPaperSettings.parse_wallPaperSettings($0) } dict[-1519029347] = { return Api.EmojiURL.parse_emojiURL($0) } dict[1611985938] = { return Api.StatsGroupTopAdmin.parse_statsGroupTopAdmin($0) } @@ -273,6 +275,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) } dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) } dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) } + dict[19291112] = { return Api.Update.parse_updatePeerHistoryTTL($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -1015,6 +1018,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.LangPackDifference: _1.serialize(buffer, boxed) + case let _1 as Api.PeerHistoryTTL: + _1.serialize(buffer, boxed) case let _1 as Api.WallPaperSettings: _1.serialize(buffer, boxed) case let _1 as Api.EmojiURL: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 314cd41732..82d4e59e94 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -2380,14 +2380,14 @@ public extension Api { } public enum ChatFull: TypeConstructorDescription { - case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?) - case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?) + case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttl: Api.PeerHistoryTTL?) + case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttl: Api.PeerHistoryTTL?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod): + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttl): if boxed { - buffer.appendInt32(-1415563086) + buffer.appendInt32(-66811386) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -2421,11 +2421,11 @@ public extension Api { if Int(flags) & Int(1 << 12) != 0 {serializeInt32(statsDc!, buffer: buffer, boxed: false)} serializeInt32(pts, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 21) != 0 {call!.serialize(buffer, true)} - if Int(flags) & Int(1 << 22) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 24) != 0 {ttl!.serialize(buffer, true)} break - case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod): + case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl): if boxed { - buffer.appendInt32(-261341160) + buffer.appendInt32(-500874592) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -2442,17 +2442,17 @@ public extension Api { if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)} - if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {ttl!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod): - return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod)]) - case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod): - return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod)]) + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttl): + return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttl", ttl)]) + case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl): + return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttl", ttl)]) } } @@ -2527,8 +2527,10 @@ public extension Api { if Int(_1!) & Int(1 << 21) != 0 {if let signature = reader.readInt32() { _28 = Api.parse(reader, signature: signature) as? Api.InputGroupCall } } - var _29: Int32? - if Int(_1!) & Int(1 << 22) != 0 {_29 = reader.readInt32() } + var _29: Api.PeerHistoryTTL? + if Int(_1!) & Int(1 << 24) != 0 {if let signature = reader.readInt32() { + _29 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -2557,9 +2559,9 @@ public extension Api { let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _26 != nil let _c27 = _27 != nil let _c28 = (Int(_1!) & Int(1 << 21) == 0) || _28 != nil - let _c29 = (Int(_1!) & Int(1 << 22) == 0) || _29 != nil + let _c29 = (Int(_1!) & Int(1 << 24) == 0) || _29 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 { - return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29) + return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttl: _29) } else { return nil @@ -2600,8 +2602,10 @@ public extension Api { if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() { _11 = Api.parse(reader, signature: signature) as? Api.InputGroupCall } } - var _12: Int32? - if Int(_1!) & Int(1 << 14) != 0 {_12 = reader.readInt32() } + var _12: Api.PeerHistoryTTL? + if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -2615,7 +2619,7 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12) + return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttl: _12) } else { return nil @@ -5080,13 +5084,13 @@ public extension Api { } public enum UserFull: TypeConstructorDescription { - case userFull(flags: Int32, user: Api.User, about: String?, settings: Api.PeerSettings, profilePhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?) + case userFull(flags: Int32, user: Api.User, about: String?, settings: Api.PeerSettings, profilePhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttl: Api.PeerHistoryTTL?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let flags, let user, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod): + case .userFull(let flags, let user, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttl): if boxed { - buffer.appendInt32(328899191) + buffer.appendInt32(-1522240089) } serializeInt32(flags, buffer: buffer, boxed: false) user.serialize(buffer, true) @@ -5098,15 +5102,15 @@ public extension Api { if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} serializeInt32(commonChatsCount, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {ttl!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let flags, let user, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod): - return ("userFull", [("flags", flags), ("user", user), ("about", about), ("settings", settings), ("profilePhoto", profilePhoto), ("notifySettings", notifySettings), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("commonChatsCount", commonChatsCount), ("folderId", folderId), ("ttlPeriod", ttlPeriod)]) + case .userFull(let flags, let user, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttl): + return ("userFull", [("flags", flags), ("user", user), ("about", about), ("settings", settings), ("profilePhoto", profilePhoto), ("notifySettings", notifySettings), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("commonChatsCount", commonChatsCount), ("folderId", folderId), ("ttl", ttl)]) } } @@ -5141,8 +5145,10 @@ public extension Api { _9 = reader.readInt32() var _10: Int32? if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() } - var _11: Int32? - if Int(_1!) & Int(1 << 14) != 0 {_11 = reader.readInt32() } + var _11: Api.PeerHistoryTTL? + if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil @@ -5155,7 +5161,7 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 14) == 0) || _11 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.UserFull.userFull(flags: _1!, user: _2!, about: _3, settings: _4!, profilePhoto: _5, notifySettings: _6!, botInfo: _7, pinnedMsgId: _8, commonChatsCount: _9!, folderId: _10, ttlPeriod: _11) + return Api.UserFull.userFull(flags: _1!, user: _2!, about: _3, settings: _4!, profilePhoto: _5, notifySettings: _6!, botInfo: _7, pinnedMsgId: _8, commonChatsCount: _9!, folderId: _10, ttl: _11) } else { return nil @@ -5512,6 +5518,68 @@ public extension Api { } } + } + public enum PeerHistoryTTL: TypeConstructorDescription { + case peerHistoryTTLPM(flags: Int32, myTtlPeriod: Int32?, peerTtlPeriod: Int32?) + case peerHistoryTTL(ttlPeriod: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerHistoryTTLPM(let flags, let myTtlPeriod, let peerTtlPeriod): + if boxed { + buffer.appendInt32(-815649386) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(myTtlPeriod!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(peerTtlPeriod!, buffer: buffer, boxed: false)} + break + case .peerHistoryTTL(let ttlPeriod): + if boxed { + buffer.appendInt32(1041354473) + } + serializeInt32(ttlPeriod, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerHistoryTTLPM(let flags, let myTtlPeriod, let peerTtlPeriod): + return ("peerHistoryTTLPM", [("flags", flags), ("myTtlPeriod", myTtlPeriod), ("peerTtlPeriod", peerTtlPeriod)]) + case .peerHistoryTTL(let ttlPeriod): + return ("peerHistoryTTL", [("ttlPeriod", ttlPeriod)]) + } + } + + public static func parse_peerHistoryTTLPM(_ reader: BufferReader) -> PeerHistoryTTL? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.PeerHistoryTTL.peerHistoryTTLPM(flags: _1!, myTtlPeriod: _2, peerTtlPeriod: _3) + } + else { + return nil + } + } + public static func parse_peerHistoryTTL(_ reader: BufferReader) -> PeerHistoryTTL? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.PeerHistoryTTL.peerHistoryTTL(ttlPeriod: _1!) + } + else { + return nil + } + } + } public enum WallPaperSettings: TypeConstructorDescription { case wallPaperSettings(flags: Int32, backgroundColor: Int32?, secondBackgroundColor: Int32?, intensity: Int32?, rotation: Int32?) @@ -6822,6 +6890,7 @@ public extension Api { case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32) case updateGroupCall(chatId: Int32, call: Api.GroupCall) case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int32, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) + case updatePeerHistoryTTL(flags: Int32, peer: Api.Peer, ttl: Api.PeerHistoryTTL?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -7583,6 +7652,14 @@ public extension Api { if Int(flags) & Int(1 << 1) != 0 {peerType!.serialize(buffer, true)} serializeString(offset, buffer: buffer, boxed: false) break + case .updatePeerHistoryTTL(let flags, let peer, let ttl): + if boxed { + buffer.appendInt32(19291112) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {ttl!.serialize(buffer, true)} + break } } @@ -7764,6 +7841,8 @@ public extension Api { return ("updateGroupCall", [("chatId", chatId), ("call", call)]) case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): return ("updateBotInlineQuery", [("flags", flags), ("queryId", queryId), ("userId", userId), ("query", query), ("geo", geo), ("peerType", peerType), ("offset", offset)]) + case .updatePeerHistoryTTL(let flags, let peer, let ttl): + return ("updatePeerHistoryTTL", [("flags", flags), ("peer", peer), ("ttl", ttl)]) } } @@ -9289,6 +9368,27 @@ public extension Api { return nil } } + public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Api.PeerHistoryTTL? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttl: _3) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 43eb0622b4..aaaf36b658 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -3995,6 +3995,22 @@ public extension Api { }) } + public static func setHistoryTTL(flags: Int32, peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-859093215) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(period, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.setHistoryTTL", parameters: [("flags", flags), ("peer", peer), ("period", period)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(347716823) @@ -4109,21 +4125,6 @@ public extension Api { return result }) } - - public static func setPeerMessagesTTL(peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-56903344) - peer.serialize(buffer, true) - serializeInt32(period, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setPeerMessagesTTL", parameters: [("peer", peer), ("period", period)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift index 53a8891e78..c468b1f85c 100644 --- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift @@ -112,6 +112,7 @@ enum AccountStateMutationOperation { case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?) case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32) case UpdateGroupCall(peerId: PeerId, call: Api.GroupCall) + case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?) } struct HoleFromPreviousState { @@ -286,6 +287,10 @@ struct AccountMutableState { self.addOperation(.UpdateGroupCall(peerId: peerId, call: call)) } + mutating func updateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?) { + self.addOperation(.UpdateAutoremoveTimeout(peer: peer, value: value)) + } + mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) { self.addOperation(.ReadGroupFeedInbox(groupId, index)) } @@ -494,7 +499,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout: break case let .AddMessages(messages, location): for message in messages { diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 6fe56f708d..40204a00c0 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -1358,6 +1358,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo case let .updateGroupCall(channelId, call): updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), call: call) updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: channelId), call: call) + case let .updatePeerHistoryTTL(_, peer, ttl): + updatedState.updateAutoremoveTimeout(peer: peer, value: CachedPeerAutoremoveTimeout.Value(ttl)) case let .updateLangPackTooLong(langCode): updatedState.updateLangPack(langCode: langCode, difference: nil) case let .updateLangPack(difference): @@ -2148,7 +2150,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -3021,6 +3023,18 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } }) } + case let .UpdateAutoremoveTimeout(peer, value): + transaction.updatePeerCachedData(peerIds: Set([peer.peerId]), update: { _, current in + if let current = current as? CachedUserData { + return current.withUpdatedAutoremoveTimeout(.known(value)) + } else if let current = current as? CachedGroupData { + return current.withUpdatedAutoremoveTimeout(.known(value)) + } else if let current = current as? CachedChannelData { + return current.withUpdatedAutoremoveTimeout(.known(value)) + } else { + return current + } + }) case let .UpdateLangPack(langCode, difference): if let difference = difference { if langPackDifferences[langCode] == nil { diff --git a/submodules/TelegramCore/Sources/DeleteMessages.swift b/submodules/TelegramCore/Sources/DeleteMessages.swift index f8a59dea1d..05c074668f 100644 --- a/submodules/TelegramCore/Sources/DeleteMessages.swift +++ b/submodules/TelegramCore/Sources/DeleteMessages.swift @@ -146,64 +146,72 @@ public enum SetChatMessageAutoremoveTimeoutError { case generic } -public func setChatMessageAutoremoveTimeoutInteractively(account: Account, peerId: PeerId, timeout: Int32?) -> Signal { - return account.postbox.transaction { transaction -> (Api.InputPeer?, CachedPeerAutoremoveTimeout) in - var currentValue: CachedPeerAutoremoveTimeout = .unknown - - transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in - if let current = current as? CachedUserData { - currentValue = current.autoremoveTimeout - return current.withUpdatedAutoremoveTimeout(.known(timeout)) - } else if let current = current as? CachedGroupData { - currentValue = current.autoremoveTimeout - return current.withUpdatedAutoremoveTimeout(.known(timeout)) - } else if let current = current as? CachedChannelData { - currentValue = current.autoremoveTimeout - return current.withUpdatedAutoremoveTimeout(.known(timeout)) - } else { - return current - } - }) - - return (transaction.getPeer(peerId).flatMap(apiInputPeer), currentValue) +public func setChatMessageAutoremoveTimeoutInteractively(account: Account, peerId: PeerId, timeout: Int32?, isGlobal: Bool) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) } |> castError(SetChatMessageAutoremoveTimeoutError.self) - |> mapToSignal { (inputPeer, previousValue) -> Signal in + |> mapToSignal { inputPeer -> Signal in guard let inputPeer = inputPeer else { return .fail(.generic) } - return account.network.request(Api.functions.messages.setPeerMessagesTTL(peer: inputPeer, period: timeout ?? 0)) - |> `catch` { _ -> Signal in - return .single(.boolFalse) + var flags: Int32 = 0 + if !isGlobal { + flags |= 1 << 0 + } + return account.network.request(Api.functions.messages.setHistoryTTL(flags: flags, peer: inputPeer, period: timeout ?? 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) } |> castError(SetChatMessageAutoremoveTimeoutError.self) |> mapToSignal { result -> Signal in - if case .boolTrue = result { - return .complete() + if let result = result { + account.stateManager.addUpdates(result) + + return account.postbox.transaction { transaction -> Void in + transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in + var currentPeerValue: Int32? + if let current = current as? CachedUserData { + if case let .known(value?) = current.autoremoveTimeout { + currentPeerValue = value.peerValue + } + } else if let current = current as? CachedGroupData { + if case let .known(value?) = current.autoremoveTimeout { + currentPeerValue = value.peerValue + } + } else if let current = current as? CachedChannelData { + if case let .known(value?) = current.autoremoveTimeout { + currentPeerValue = value.peerValue + } + } + + let updatedTimeout: CachedPeerAutoremoveTimeout + if let timeout = timeout { + updatedTimeout = .known(CachedPeerAutoremoveTimeout.Value(myValue: timeout, peerValue: currentPeerValue ?? timeout, isGlobal: isGlobal)) + } else { + updatedTimeout = .known(nil) + } + + if let current = current as? CachedUserData { + return current.withUpdatedAutoremoveTimeout(updatedTimeout) + } else if let current = current as? CachedGroupData { + return current.withUpdatedAutoremoveTimeout(updatedTimeout) + } else if let current = current as? CachedChannelData { + return current.withUpdatedAutoremoveTimeout(updatedTimeout) + } else { + return current + } + }) + } + |> castError(SetChatMessageAutoremoveTimeoutError.self) + |> ignoreValues } else { return .fail(.generic) } } |> `catch` { _ -> Signal in - return account.postbox.transaction { transaction -> Void in - transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in - transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in - if let current = current as? CachedUserData { - return current.withUpdatedAutoremoveTimeout(previousValue) - } else if let current = current as? CachedGroupData { - return current.withUpdatedAutoremoveTimeout(previousValue) - } else if let current = current as? CachedChannelData { - return current.withUpdatedAutoremoveTimeout(previousValue) - } else { - return current - } - }) - return current - }) - } - |> castError(SetChatMessageAutoremoveTimeoutError.self) - |> ignoreValues - |> then(.fail(.generic)) + return .complete() } } } diff --git a/submodules/TelegramCore/Sources/EnqueueMessage.swift b/submodules/TelegramCore/Sources/EnqueueMessage.swift index 9f49b7d50b..964f9ab87a 100644 --- a/submodules/TelegramCore/Sources/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/EnqueueMessage.swift @@ -332,6 +332,25 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil)) } + } else if let cachedData = transaction.getPeerCachedData(peerId: peer.id) { + var messageAutoremoveTimeout: Int32? + if let cachedData = cachedData as? CachedUserData { + if case let .known(value) = cachedData.autoremoveTimeout { + messageAutoremoveTimeout = value?.peerValue + } + } else if let cachedData = cachedData as? CachedGroupData { + if case let .known(value) = cachedData.autoremoveTimeout { + messageAutoremoveTimeout = value?.peerValue + } + } else if let cachedData = cachedData as? CachedChannelData { + if case let .known(value) = cachedData.autoremoveTimeout { + messageAutoremoveTimeout = value?.peerValue + } + } + + if let messageAutoremoveTimeout = messageAutoremoveTimeout { + attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil)) + } } attributes.append(contentsOf: filterMessageAttributesForOutgoingMessage(requestedAttributes)) diff --git a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift index 4d0156ed7a..9c225b0dd6 100644 --- a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift @@ -207,7 +207,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI let hasScheduledMessages = (userFull.flags & 1 << 12) != 0 - let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(userFull.ttlPeriod) + let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(userFull.ttl)) return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedAutoremoveTimeout(autoremoveTimeout) @@ -300,7 +300,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI } } - let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(chatFull.ttlPeriod) + let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(chatFull.ttl)) transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in let previous: CachedGroupData @@ -513,7 +513,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI minAvailableMessageIdUpdated = previous.minAvailableMessageId != minAvailableMessageId - let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(ttlPeriod) + let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(ttlPeriod)) return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) @@ -565,3 +565,22 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI } } } + +extension CachedPeerAutoremoveTimeout.Value { + init?(_ apiValue: Api.PeerHistoryTTL?) { + if let apiValue = apiValue { + switch apiValue { + case let .peerHistoryTTLPM(flags, ttlPeriodMy, ttlPeriodPeer): + guard let ttlPeriodPeer = ttlPeriodPeer else { + return nil + } + let pmOneSide = flags & (1 << 0) != 0 + self.init(myValue: ttlPeriodMy ?? ttlPeriodPeer, peerValue: ttlPeriodPeer, isGlobal: !pmOneSide) + case let .peerHistoryTTL(ttlPeriodPeer): + self.init(myValue: ttlPeriodPeer, peerValue: ttlPeriodPeer, isGlobal: true) + } + } else { + return nil + } + } +} diff --git a/submodules/TelegramUI/Resources/Animations/MessageAutoRemove.tgs b/submodules/TelegramUI/Resources/Animations/MessageAutoRemove.tgs new file mode 100644 index 0000000000..c65577a62a Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/MessageAutoRemove.tgs differ diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 4bfe54cd2d..bc19185ab1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2876,7 +2876,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G hasBots = true } if case let .known(value) = cachedGroupData.autoremoveTimeout { - autoremoveTimeout = value + autoremoveTimeout = value?.peerValue } } else if let cachedChannelData = peerView.cachedData as? CachedChannelData { if let channel = peer as? TelegramChannel, case .group = channel.info { @@ -2885,11 +2885,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if case let .known(value) = cachedChannelData.autoremoveTimeout { - autoremoveTimeout = value + autoremoveTimeout = value?.peerValue } } else if let cachedUserData = peerView.cachedData as? CachedUserData { if case let .known(value) = cachedUserData.autoremoveTimeout { - autoremoveTimeout = value + autoremoveTimeout = value?.peerValue } } } @@ -5436,10 +5436,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { return } + guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + return + } if peerId.namespace == Namespaces.Peer.SecretChat { strongSelf.chatDisplayNode.dismissInput() - if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat { + if let peer = peer as? TelegramSecretChat { let controller = ChatSecretAutoremoveTimerActionSheetController(context: strongSelf.context, currentValue: peer.messageAutoremoveTimeout == nil ? 0 : peer.messageAutoremoveTimeout!, applyValue: { value in if let strongSelf = self { let _ = setSecretChatMessageAutoremoveTimeoutInteractively(account: strongSelf.context.account, peerId: peer.id, timeout: value == 0 ? nil : value).start() @@ -5448,24 +5451,60 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(controller, in: .window(.root)) } } else { - strongSelf.chatDisplayNode.dismissInput() + var currentAutoremoveTimeout: Int32? = strongSelf.presentationInterfaceState.autoremoveTimeout + var canSetupAutoremoveTimeout = false - if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { - var availableValues: [Int32] = [ - 60, - 300, - 86400, - 604800 - ] - #if DEBUG - availableValues.insert(5, at: 0) - #endif - let controller = ChatSecretAutoremoveTimerActionSheetController(context: strongSelf.context, currentValue: strongSelf.presentationInterfaceState.autoremoveTimeout ?? 0, availableValues: availableValues, applyValue: { value in - if let strongSelf = self { - let _ = setChatMessageAutoremoveTimeoutInteractively(account: strongSelf.context.account, peerId: peer.id, timeout: value == 0 ? nil : value).start() + if let secretChat = peer as? TelegramSecretChat { + currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout + canSetupAutoremoveTimeout = true + } else if let group = peer as? TelegramGroup { + if case .creator = group.role { + canSetupAutoremoveTimeout = true + } else if case let .admin(rights, _) = group.role { + if rights.flags.contains(.canChangeInfo) { + canSetupAutoremoveTimeout = true } - }) - strongSelf.present(controller, in: .window(.root)) + } else if let defaultBannedRights = group.defaultBannedRights { + if !defaultBannedRights.flags.contains(.banChangeInfo) { + canSetupAutoremoveTimeout = true + } + } + } else if let _ = peer as? TelegramUser { + canSetupAutoremoveTimeout = true + } else if let channel = peer as? TelegramChannel { + if channel.hasPermission(.changeInfo) { + canSetupAutoremoveTimeout = true + } + } + + if canSetupAutoremoveTimeout { + strongSelf.chatDisplayNode.dismissInput() + + let controller = peerAutoremoveSetupScreen(context: strongSelf.context, peerId: peerId) + strongSelf.push(controller) + } else if let currentAutoremoveTimeout = currentAutoremoveTimeout, let rect = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.messageAutoremoveTimeout(currentAutoremoveTimeout)) { + + //TODO:localize + let intervalText = timeIntervalString(strings: strongSelf.presentationData.strings, value: currentAutoremoveTimeout) + let text: String = "Messages in this chat are automatically\ndeleted \(intervalText) after they have been sent." + + if let tooltipController = strongSelf.silentPostTooltipController { + tooltipController.updateContent(.text(text), animated: true, extendTimer: true) + } else { + let tooltipController = TooltipController(content: .text(text), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize) + strongSelf.silentPostTooltipController = tooltipController + tooltipController.dismissed = { [weak tooltipController] _ in + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.silentPostTooltipController === tooltipController { + strongSelf.silentPostTooltipController = nil + } + } + strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { + if let strongSelf = self { + return (strongSelf.chatDisplayNode, rect) + } + return nil + })) + } } } }, sendSticker: { [weak self] file, sourceNode, sourceRect in @@ -7544,6 +7583,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } else if canRemoveGlobally { items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + beginClear(.forLocalPeer) + })) items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() @@ -7559,10 +7602,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) ], parseMarkdown: true), in: .window(.root)) })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - beginClear(.forLocalPeer) - })) } else { items.append(ActionSheetTextItem(title: text)) items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak self, weak actionSheet] in @@ -7581,6 +7620,58 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ], parseMarkdown: true), in: .window(.root)) })) } + + if let peer = self.presentationInterfaceState.renderedPeer?.peer { + var currentAutoremoveTimeout: Int32? = self.presentationInterfaceState.autoremoveTimeout + var canSetupAutoremoveTimeout = false + + if let secretChat = peer as? TelegramSecretChat { + currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout + canSetupAutoremoveTimeout = true + } else if let group = peer as? TelegramGroup { + if case .creator = group.role { + canSetupAutoremoveTimeout = true + } else if case let .admin(rights, _) = group.role { + if rights.flags.contains(.canChangeInfo) { + canSetupAutoremoveTimeout = true + } + } else if let defaultBannedRights = group.defaultBannedRights { + if !defaultBannedRights.flags.contains(.banChangeInfo) { + canSetupAutoremoveTimeout = true + } + } + } else if let _ = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser { + canSetupAutoremoveTimeout = true + } else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel { + if channel.hasPermission(.changeInfo) { + canSetupAutoremoveTimeout = true + } + } + + if canSetupAutoremoveTimeout { + //TODO:localize + items.append(ActionSheetButtonItem(title: currentAutoremoveTimeout == nil ? "Enable Auto-Delete" : "Edit Auto-Delete Settings", color: .accent, action: { [weak self, weak actionSheet] in + guard let actionSheet = actionSheet else { + return + } + actionSheet.dismissAnimated() + + guard let strongSelf = self else { + return + } + + let controller = peerAutoremoveSetupScreen(context: strongSelf.context, peerId: peer.id, completion: { updatedValue in + if case .updated = updatedValue { + if currentAutoremoveTimeout == nil { + self?.navigationButtonAction(.clearHistory) + } + } + }) + strongSelf.chatDisplayNode.dismissInput() + strongSelf.push(controller) + })) + } + } actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift b/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift index 5cad48bba0..f86ae19de5 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift @@ -259,7 +259,9 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte } if canSetupAutoremoveTimeout { - accessoryItems.append(.messageAutoremoveTimeout(currentAutoremoveTimeout)) + if currentAutoremoveTimeout != nil || chatPresentationInterfaceState.renderedPeer?.peer is TelegramSecretChat { + accessoryItems.append(.messageAutoremoveTimeout(currentAutoremoveTimeout)) + } } switch chatPresentationInterfaceState.inputMode { @@ -283,7 +285,7 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte if !extendedSearchLayout { if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat { accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout)) - } else if canSetupAutoremoveTimeout { + } else if currentAutoremoveTimeout != nil { accessoryItems.append(.messageAutoremoveTimeout(currentAutoremoveTimeout)) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 496caac824..05d7576a6c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1235,7 +1235,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr if channel.hasPermission(.changeInfo) { let timeoutString: String if case let .known(value) = (data.cachedData as? CachedChannelData)?.autoremoveTimeout { - if let value = value { + if let value = value?.peerValue { timeoutString = timeIntervalString(strings: presentationData.strings, value: value) } else { timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled @@ -1350,10 +1350,10 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr })) } - if channel.hasPermission(.changeInfo) { + /*if channel.hasPermission(.changeInfo) { let timeoutString: String if case let .known(value) = cachedData.autoremoveTimeout { - if let value = value { + if let value = value?.peerValue { timeoutString = timeIntervalString(strings: presentationData.strings, value: value) } else { timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled @@ -1365,7 +1365,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAutoremove, label: .text(timeoutString), text: presentationData.strings.PeerInfo_AutoremoveMessages, action: { interaction.editingOpenAutoremoveMesages() })) - } + }*/ } } @@ -1436,11 +1436,11 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr interaction.editingOpenPreHistorySetup() })) - let canChangeInfo = true + /*let canChangeInfo = true if canChangeInfo { let timeoutString: String if case let .known(value) = (data.cachedData as? CachedGroupData)?.autoremoveTimeout { - if let value = value { + if let value = value?.value { timeoutString = timeIntervalString(strings: presentationData.strings, value: value) } else { timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled @@ -1452,7 +1452,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAutoremove, label: .text(timeoutString), text: presentationData.strings.PeerInfo_AutoremoveMessages, action: { interaction.editingOpenAutoremoveMesages() })) - } + }*/ var activePermissionCount: Int? if let defaultBannedRights = group.defaultBannedRights { @@ -3859,7 +3859,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } private func editingOpenAutoremoveMesages() { + guard let data = self.data, let peer = data.peer else { + return + } + let controller = peerAutoremoveSetupScreen(context: self.context, peerId: peer.id) + self.controller?.push(controller) } private func openPermissions() { diff --git a/submodules/TelegramUI/Sources/WidgetDataContext.swift b/submodules/TelegramUI/Sources/WidgetDataContext.swift index f962dbc4f2..bd3930a600 100644 --- a/submodules/TelegramUI/Sources/WidgetDataContext.swift +++ b/submodules/TelegramUI/Sources/WidgetDataContext.swift @@ -192,7 +192,7 @@ final class WidgetDataContext { return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in return account.postbox.mediaBox.resourcePath(representation.resource) }, badge: badge, message: message) - }))) + }, updateTimestamp: Int32(Date().timeIntervalSince1970)))) } } |> distinctUntilChanged @@ -282,7 +282,7 @@ final class WidgetDataContext { return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in return account.postbox.mediaBox.resourcePath(representation.resource) }, badge: badge, message: message) - }))) + }, updateTimestamp: Int32(Date().timeIntervalSince1970)))) } } |> distinctUntilChanged diff --git a/submodules/WidgetItems/Sources/WidgetItems.swift b/submodules/WidgetItems/Sources/WidgetItems.swift index 7b0bb61830..ea2dc86599 100644 --- a/submodules/WidgetItems/Sources/WidgetItems.swift +++ b/submodules/WidgetItems/Sources/WidgetItems.swift @@ -238,10 +238,12 @@ public struct WidgetDataPeer: Codable, Equatable { public struct WidgetDataPeers: Codable, Equatable { public var accountPeerId: Int64 public var peers: [WidgetDataPeer] + public var updateTimestamp: Int32 - public init(accountPeerId: Int64, peers: [WidgetDataPeer]) { + public init(accountPeerId: Int64, peers: [WidgetDataPeer], updateTimestamp: Int32) { self.accountPeerId = accountPeerId self.peers = peers + self.updateTimestamp = updateTimestamp } }