diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index bcf2dba434..ff2cc3e23e 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -315,6 +315,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) } dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) } dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) } + dict[-376962181] = { return Api.KeyboardButton.parse_inputKeyboardButtonUserProfile($0) } + dict[814112961] = { return Api.KeyboardButton.parse_keyboardButtonUserProfile($0) } dict[383348795] = { return Api.ContactStatus.parse_contactStatus($0) } dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) } dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index bc73a326cf..9d7c28fa70 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -8051,6 +8051,8 @@ public extension Api { case keyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, buttonId: Int32) case inputKeyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, bot: Api.InputUser) case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String) + case inputKeyboardButtonUserProfile(text: String, userId: Api.InputUser) + case keyboardButtonUserProfile(text: String, userId: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -8135,6 +8137,20 @@ public extension Api { if Int(flags) & Int(1 << 0) != 0 {quiz!.serialize(buffer, true)} serializeString(text, buffer: buffer, boxed: false) break + case .inputKeyboardButtonUserProfile(let text, let userId): + if boxed { + buffer.appendInt32(-376962181) + } + serializeString(text, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + break + case .keyboardButtonUserProfile(let text, let userId): + if boxed { + buffer.appendInt32(814112961) + } + serializeString(text, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + break } } @@ -8162,6 +8178,10 @@ public extension Api { return ("inputKeyboardButtonUrlAuth", [("flags", flags), ("text", text), ("fwdText", fwdText), ("url", url), ("bot", bot)]) case .keyboardButtonRequestPoll(let flags, let quiz, let text): return ("keyboardButtonRequestPoll", [("flags", flags), ("quiz", quiz), ("text", text)]) + case .inputKeyboardButtonUserProfile(let text, let userId): + return ("inputKeyboardButtonUserProfile", [("text", text), ("userId", userId)]) + case .keyboardButtonUserProfile(let text, let userId): + return ("keyboardButtonUserProfile", [("text", text), ("userId", userId)]) } } @@ -8335,6 +8355,36 @@ public extension Api { return nil } } + public static func parse_inputKeyboardButtonUserProfile(_ reader: BufferReader) -> KeyboardButton? { + var _1: String? + _1 = parseString(reader) + var _2: Api.InputUser? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputUser + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.KeyboardButton.inputKeyboardButtonUserProfile(text: _1!, userId: _2!) + } + else { + return nil + } + } + public static func parse_keyboardButtonUserProfile(_ reader: BufferReader) -> KeyboardButton? { + var _1: String? + _1 = parseString(reader) + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.KeyboardButton.keyboardButtonUserProfile(text: _1!, userId: _2!) + } + else { + return nil + } + } } public enum ContactStatus: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index ca8a0fabc1..c4def922a9 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -4464,6 +4464,22 @@ public extension Api { return result }) } + + public static func hideAllChatJoinRequests(flags: Int32, peer: Api.InputPeer, link: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-528091926) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.hideAllChatJoinRequests", parameters: [("flags", flags), ("peer", peer), ("link", link)]), 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 struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -5071,6 +5087,21 @@ public extension Api { return result }) } + + public static func toggleNoForwards(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-219318255) + channel.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleNoForwards", parameters: [("channel", channel), ("enabled", enabled)]), 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 struct payments { public static func getPaymentForm(flags: Int32, peer: Api.InputPeer, msgId: Int32, themeParams: Api.DataJSON?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index d4db336c18..3675efb119 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -84,6 +84,9 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { if (flags & Int32(1 << 20)) != 0 { infoFlags.insert(.hasDiscussionGroup) } + if (flags & Int32(1 << 27)) != 0 { + infoFlags.insert(.copyProtectionEnabled) + } info = .broadcast(TelegramChannelBroadcastInfo(flags: infoFlags)) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift index 82c7ed5789..a922ed23a2 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift @@ -38,6 +38,10 @@ extension ReplyMarkupButton { } } self.init(title: text, titleWhenForwarded: nil, action: .setupPoll(isQuiz: isQuiz)) + case let .keyboardButtonUserProfile(text, userId): + self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)))) + case let .inputKeyboardButtonUserProfile(text, _): + self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)))) } } } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 7e955c3ef5..4444f3e01c 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 134 + return 135 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMarkupMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMarkupMessageAttribute.swift index 33e8c4db04..815f8139cc 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMarkupMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMarkupMessageAttribute.swift @@ -11,6 +11,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { case payment case urlAuth(url: String, buttonId: Int32) case setupPoll(isQuiz: Bool?) + case openUserProfile(peerId: PeerId) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { @@ -34,6 +35,8 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { self = .urlAuth(url: decoder.decodeStringForKey("u", orElse: ""), buttonId: decoder.decodeInt32ForKey("b", orElse: 0)) case 9: self = .setupPoll(isQuiz: decoder.decodeOptionalInt32ForKey("isq").flatMap { $0 != 0 }) + case 10: + self = .openUserProfile(peerId: PeerId(decoder.decodeInt64ForKey("peerId", orElse: 0))) default: self = .text } @@ -73,6 +76,9 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "isq") } + case let .openUserProfile(peerId): + encoder.encodeInt32(10, forKey: "v") + encoder.encodeInt64(peerId.toInt64(), forKey: "peerId") } } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index ac8faf4f24..214e77d868 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -43,6 +43,7 @@ public struct TelegramChannelBroadcastFlags: OptionSet { public static let messagesShouldHaveSignatures = TelegramChannelBroadcastFlags(rawValue: 1 << 0) public static let hasDiscussionGroup = TelegramChannelBroadcastFlags(rawValue: 1 << 1) + public static let copyProtectionEnabled = TelegramChannelBroadcastFlags(rawValue: 1 << 2) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift new file mode 100644 index 0000000000..740514e244 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift @@ -0,0 +1,17 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +func _internal_toggleChannelMessageCopyProtection(account:Account, peerId:PeerId, enabled: Bool) -> Signal { + return account.postbox.transaction { transaction -> Signal in + if let peer = transaction.getPeer(peerId) as? TelegramChannel, let inputChannel = apiInputChannel(peer) { + return account.network.request(Api.functions.channels.toggleNoForwards(channel: inputChannel, enabled: enabled ? .boolTrue : .boolFalse)) |> retryRequest |> map { updates -> Void in + account.stateManager.addUpdates(updates) + } + } else { + return .complete() + } + } |> switchToLatest +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift index d1af4d5cff..f47874357f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift @@ -20,6 +20,25 @@ private func _internal_updateInvitationRequest(account: Account, peerId: PeerId, } |> switchToLatest } +private func _internal_updateAllInvitationRequests(account: Account, peerId: PeerId, link: String?, approve: Bool) -> Signal { + return account.postbox.transaction { transaction -> Signal in + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { + var flags: Int32 = 0 + if approve { + flags |= (1 << 0) + } + if let _ = link { + flags |= (1 << 1) + } + return account.network.request(Api.functions.messages.hideAllChatJoinRequests(flags: flags, peer: inputPeer, link: link)) + |> retryRequest + |> ignoreValues + } else { + return .complete() + } + } |> switchToLatest +} + func _internal_revokePersistentPeerExportedInvitation(account: Account, peerId: PeerId) -> Signal { return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { @@ -1025,6 +1044,16 @@ private final class PeerInvitationImportersContextImpl { } } + func updateAll(action: PeerInvitationImportersContext.UpdateAction) { + self.actionDisposables.add(_internal_updateAllInvitationRequests(account: self.account, peerId: self.peerId, link: nil, approve: action == .approve).start()) + + self.results = [] + self.count = 0 + + self.updateState() + self.updateCache() + } + private func updateCache() { guard self.hasLoadedOnce && !self.isLoadingMore && self.query == nil else { return @@ -1096,6 +1125,12 @@ public final class PeerInvitationImportersContext { impl.update(peerId, action: action) } } + + public func updateAll(action: UpdateAction) { + self.impl.with { impl in + impl.updateAll(action: action) + } + } } public struct ExportedInvitationCreator : Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index b528147794..eac023d380 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -231,6 +231,10 @@ public extension TelegramEngine { return _internal_toggleShouldChannelMessagesSignatures(account: self.account, peerId: peerId, enabled: enabled) } + public func toggleChannelMessageCopyProtection(peerId: PeerId, enabled: Bool) -> Signal { + return _internal_toggleChannelMessageCopyProtection(account: self.account, peerId: peerId, enabled: enabled) + } + public func requestPeerPhotos(peerId: PeerId) -> Signal<[TelegramPeerPhoto], NoError> { return _internal_requestPeerPhotos(postbox: self.account.postbox, network: self.account.network, peerId: peerId) } diff --git a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift index f01a55acf0..99b85e3884 100644 --- a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift @@ -212,6 +212,8 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { } case let .setupPoll(isQuiz): self.controllerInteraction.openPollCreation(isQuiz) + case let .openUserProfile(peerId): + self.controllerInteraction.openPeer(peerId, .info, nil) } if dismissIfOnce { if let message = self.message { diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 85a8a34068..849968962c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -852,6 +852,8 @@ public class ChatMessageItemView: ListViewItemNode { item.controllerInteraction.requestMessageActionUrlAuth(url, .message(id: item.message.id, buttonId: buttonId)) case .setupPoll: break + case let .openUserProfile(peerId): + item.controllerInteraction.openPeer(peerId, .info, nil) } } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 0acb979581..49da25c706 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -566,6 +566,7 @@ private final class PeerInfoInteraction { let editingOpenInviteLinksSetup: () -> Void let editingOpenDiscussionGroupSetup: () -> Void let editingToggleMessageSignatures: (Bool) -> Void + let editingToggleChannelMessageCopyProtection: (Bool) -> Void let openParticipantsSection: (PeerInfoParticipantsSection) -> Void let editingOpenPreHistorySetup: () -> Void let editingOpenAutoremoveMesages: () -> Void @@ -605,6 +606,7 @@ private final class PeerInfoInteraction { editingOpenInviteLinksSetup: @escaping () -> Void, editingOpenDiscussionGroupSetup: @escaping () -> Void, editingToggleMessageSignatures: @escaping (Bool) -> Void, + editingToggleChannelMessageCopyProtection: @escaping (Bool) -> Void, openParticipantsSection: @escaping (PeerInfoParticipantsSection) -> Void, editingOpenPreHistorySetup: @escaping () -> Void, editingOpenAutoremoveMesages: @escaping () -> Void, @@ -643,6 +645,7 @@ private final class PeerInfoInteraction { self.editingOpenInviteLinksSetup = editingOpenInviteLinksSetup self.editingOpenDiscussionGroupSetup = editingOpenDiscussionGroupSetup self.editingToggleMessageSignatures = editingToggleMessageSignatures + self.editingToggleChannelMessageCopyProtection = editingToggleChannelMessageCopyProtection self.openParticipantsSection = openParticipantsSection self.editingOpenPreHistorySetup = editingOpenPreHistorySetup self.editingOpenAutoremoveMesages = editingOpenAutoremoveMesages @@ -1212,6 +1215,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr let ItemDiscussionGroup = 3 let ItemSignMessages = 4 let ItemSignMessagesHelp = 5 + let ItemCopyProtection = 6 if channel.flags.contains(.isCreator) { let linkText: String @@ -1260,16 +1264,23 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.hasPermission(.sendMessages)) { let messagesShouldHaveSignatures: Bool + let messagesCopyProtection: Bool switch channel.info { case let .broadcast(info): messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures) + messagesCopyProtection = info.flags.contains(.copyProtectionEnabled) default: messagesShouldHaveSignatures = false + messagesCopyProtection = false } items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemSignMessages, text: presentationData.strings.Channel_SignMessages, value: messagesShouldHaveSignatures, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in interaction.editingToggleMessageSignatures(value) })) items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help)) + + items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemCopyProtection, text: "Disable Forwards", value: messagesCopyProtection, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in + interaction.editingToggleChannelMessageCopyProtection(value) + })) } case .group: let ItemUsername = 101 @@ -1581,6 +1592,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate private let activeActionDisposable = MetaDisposable() private let resolveUrlDisposable = MetaDisposable() private let toggleShouldChannelMessagesSignaturesDisposable = MetaDisposable() + private let toggleChannelMessageCopyProtectionDisposable = MetaDisposable() private let selectAddMemberDisposable = MetaDisposable() private let addMemberDisposable = MetaDisposable() private let preloadHistoryDisposable = MetaDisposable() @@ -1696,6 +1708,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate editingToggleMessageSignatures: { [weak self] value in self?.editingToggleMessageSignatures(value: value) }, + editingToggleChannelMessageCopyProtection: { [weak self] value in + self?.editingToggleMessageCopyProtection(value: value) + }, openParticipantsSection: { [weak self] section in self?.openParticipantsSection(section: section) }, @@ -2959,6 +2974,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.resolveUrlDisposable.dispose() self.hiddenAvatarRepresentationDisposable.dispose() self.toggleShouldChannelMessagesSignaturesDisposable.dispose() + self.toggleChannelMessageCopyProtectionDisposable.dispose() self.editAvatarDisposable.dispose() self.selectAddMemberDisposable.dispose() self.addMemberDisposable.dispose() @@ -4808,6 +4824,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.toggleShouldChannelMessagesSignaturesDisposable.set(self.context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: self.peerId, enabled: value).start()) } + private func editingToggleMessageCopyProtection(value: Bool) { + self.toggleChannelMessageCopyProtectionDisposable.set(self.context.engine.peers.toggleChannelMessageCopyProtection(peerId: self.peerId, enabled: value).start()) + } + private func openParticipantsSection(section: PeerInfoParticipantsSection) { guard let data = self.data, let peer = data.peer else { return