Add support for multiple users selection in bots peer requests

This commit is contained in:
Ilya Laktyushin 2023-12-21 04:04:17 +04:00
parent c3f15c4914
commit 6795603c4e
26 changed files with 247 additions and 124 deletions

View File

@ -8719,6 +8719,7 @@ Sorry for the inconvenience.";
"CreateGroup.PublicLinkInfo" = "You can use **a-z**, **0-9** and underscores. Minimum length is **5** characters."; "CreateGroup.PublicLinkInfo" = "You can use **a-z**, **0-9** and underscores. Minimum length is **5** characters.";
"Notification.RequestedPeer" = "You shared %1$@ with %2$@."; "Notification.RequestedPeer" = "You shared %1$@ with %2$@.";
"Notification.RequestedPeerMultiple" = "You shared %1$@ with %2$@.";
"Conversation.ViewInChannel" = "View in Channel"; "Conversation.ViewInChannel" = "View in Channel";
@ -10829,3 +10830,8 @@ Sorry for the inconvenience.";
"Channel.Info.BoostLevelPlusBadge" = "Level %@+"; "Channel.Info.BoostLevelPlusBadge" = "Level %@+";
"Channel.Info.AppearanceItem" = "Appearance"; "Channel.Info.AppearanceItem" = "Appearance";
"RequestPeer.SelectUsers" = "Choose Users";
"RequestPeer.SelectUsers.SearchPlaceholder" = "Search";
"RequestPeer.ReachedMaximum_1" = "You can select up to %@ user.";
"RequestPeer.ReachedMaximum_any" = "You can select up to %@ users.";

View File

@ -70,6 +70,7 @@ public enum ContactMultiselectionControllerMode {
case channelCreation case channelCreation
case chatSelection(ChatSelection) case chatSelection(ChatSelection)
case premiumGifting case premiumGifting
case requestedUsersSelection
} }
public enum ContactListFilter { public enum ContactListFilter {

View File

@ -404,6 +404,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) } dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) } dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) } dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
dict[1232373075] = { return Api.InputStickerSet.parse_inputStickerSetEmojiChannelDefaultStatuses($0) }
dict[701560302] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultStatuses($0) } dict[701560302] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultStatuses($0) }
dict[1153562857] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultTopicIcons($0) } dict[1153562857] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultTopicIcons($0) }
dict[80008398] = { return Api.InputStickerSet.parse_inputStickerSetEmojiGenericAnimations($0) } dict[80008398] = { return Api.InputStickerSet.parse_inputStickerSetEmojiGenericAnimations($0) }
@ -447,7 +448,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) } dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
dict[1358175439] = { return Api.KeyboardButton.parse_keyboardButtonGame($0) } dict[1358175439] = { return Api.KeyboardButton.parse_keyboardButtonGame($0) }
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) } dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
dict[218842764] = { return Api.KeyboardButton.parse_keyboardButtonRequestPeer($0) } dict[1406648280] = { return Api.KeyboardButton.parse_keyboardButtonRequestPeer($0) }
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) } dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) } dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
dict[-1598009252] = { return Api.KeyboardButton.parse_keyboardButtonSimpleWebView($0) } dict[-1598009252] = { return Api.KeyboardButton.parse_keyboardButtonSimpleWebView($0) }
@ -503,7 +504,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1892568281] = { return Api.MessageAction.parse_messageActionPaymentSentMe($0) } dict[-1892568281] = { return Api.MessageAction.parse_messageActionPaymentSentMe($0) }
dict[-2132731265] = { return Api.MessageAction.parse_messageActionPhoneCall($0) } dict[-2132731265] = { return Api.MessageAction.parse_messageActionPhoneCall($0) }
dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) } dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) }
dict[-25742243] = { return Api.MessageAction.parse_messageActionRequestedPeer($0) } dict[827428507] = { return Api.MessageAction.parse_messageActionRequestedPeer($0) }
dict[1200788123] = { return Api.MessageAction.parse_messageActionScreenshotTaken($0) } dict[1200788123] = { return Api.MessageAction.parse_messageActionScreenshotTaken($0) }
dict[-648257196] = { return Api.MessageAction.parse_messageActionSecureValuesSent($0) } dict[-648257196] = { return Api.MessageAction.parse_messageActionSecureValuesSent($0) }
dict[455635795] = { return Api.MessageAction.parse_messageActionSecureValuesSentMe($0) } dict[455635795] = { return Api.MessageAction.parse_messageActionSecureValuesSentMe($0) }
@ -1262,7 +1263,7 @@ public extension Api {
return parser(reader) return parser(reader)
} }
else { else {
telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found")
return nil return nil
} }
} }

View File

@ -325,6 +325,7 @@ public extension Api {
case inputStickerSetAnimatedEmoji case inputStickerSetAnimatedEmoji
case inputStickerSetAnimatedEmojiAnimations case inputStickerSetAnimatedEmojiAnimations
case inputStickerSetDice(emoticon: String) case inputStickerSetDice(emoticon: String)
case inputStickerSetEmojiChannelDefaultStatuses
case inputStickerSetEmojiDefaultStatuses case inputStickerSetEmojiDefaultStatuses
case inputStickerSetEmojiDefaultTopicIcons case inputStickerSetEmojiDefaultTopicIcons
case inputStickerSetEmojiGenericAnimations case inputStickerSetEmojiGenericAnimations
@ -352,6 +353,12 @@ public extension Api {
buffer.appendInt32(-427863538) buffer.appendInt32(-427863538)
} }
serializeString(emoticon, buffer: buffer, boxed: false) serializeString(emoticon, buffer: buffer, boxed: false)
break
case .inputStickerSetEmojiChannelDefaultStatuses:
if boxed {
buffer.appendInt32(1232373075)
}
break break
case .inputStickerSetEmojiDefaultStatuses: case .inputStickerSetEmojiDefaultStatuses:
if boxed { if boxed {
@ -407,6 +414,8 @@ public extension Api {
return ("inputStickerSetAnimatedEmojiAnimations", []) return ("inputStickerSetAnimatedEmojiAnimations", [])
case .inputStickerSetDice(let emoticon): case .inputStickerSetDice(let emoticon):
return ("inputStickerSetDice", [("emoticon", emoticon as Any)]) return ("inputStickerSetDice", [("emoticon", emoticon as Any)])
case .inputStickerSetEmojiChannelDefaultStatuses:
return ("inputStickerSetEmojiChannelDefaultStatuses", [])
case .inputStickerSetEmojiDefaultStatuses: case .inputStickerSetEmojiDefaultStatuses:
return ("inputStickerSetEmojiDefaultStatuses", []) return ("inputStickerSetEmojiDefaultStatuses", [])
case .inputStickerSetEmojiDefaultTopicIcons: case .inputStickerSetEmojiDefaultTopicIcons:
@ -441,6 +450,9 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_inputStickerSetEmojiChannelDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? {
return Api.InputStickerSet.inputStickerSetEmojiChannelDefaultStatuses
}
public static func parse_inputStickerSetEmojiDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? { public static func parse_inputStickerSetEmojiDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? {
return Api.InputStickerSet.inputStickerSetEmojiDefaultStatuses return Api.InputStickerSet.inputStickerSetEmojiDefaultStatuses
} }

View File

@ -429,7 +429,7 @@ public extension Api {
case keyboardButtonCallback(flags: Int32, text: String, data: Buffer) case keyboardButtonCallback(flags: Int32, text: String, data: Buffer)
case keyboardButtonGame(text: String) case keyboardButtonGame(text: String)
case keyboardButtonRequestGeoLocation(text: String) case keyboardButtonRequestGeoLocation(text: String)
case keyboardButtonRequestPeer(text: String, buttonId: Int32, peerType: Api.RequestPeerType) case keyboardButtonRequestPeer(text: String, buttonId: Int32, peerType: Api.RequestPeerType, maxQuantity: Int32)
case keyboardButtonRequestPhone(text: String) case keyboardButtonRequestPhone(text: String)
case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String) case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String)
case keyboardButtonSimpleWebView(text: String, url: String) case keyboardButtonSimpleWebView(text: String, url: String)
@ -490,13 +490,14 @@ public extension Api {
} }
serializeString(text, buffer: buffer, boxed: false) serializeString(text, buffer: buffer, boxed: false)
break break
case .keyboardButtonRequestPeer(let text, let buttonId, let peerType): case .keyboardButtonRequestPeer(let text, let buttonId, let peerType, let maxQuantity):
if boxed { if boxed {
buffer.appendInt32(218842764) buffer.appendInt32(1406648280)
} }
serializeString(text, buffer: buffer, boxed: false) serializeString(text, buffer: buffer, boxed: false)
serializeInt32(buttonId, buffer: buffer, boxed: false) serializeInt32(buttonId, buffer: buffer, boxed: false)
peerType.serialize(buffer, true) peerType.serialize(buffer, true)
serializeInt32(maxQuantity, buffer: buffer, boxed: false)
break break
case .keyboardButtonRequestPhone(let text): case .keyboardButtonRequestPhone(let text):
if boxed { if boxed {
@ -582,8 +583,8 @@ public extension Api {
return ("keyboardButtonGame", [("text", text as Any)]) return ("keyboardButtonGame", [("text", text as Any)])
case .keyboardButtonRequestGeoLocation(let text): case .keyboardButtonRequestGeoLocation(let text):
return ("keyboardButtonRequestGeoLocation", [("text", text as Any)]) return ("keyboardButtonRequestGeoLocation", [("text", text as Any)])
case .keyboardButtonRequestPeer(let text, let buttonId, let peerType): case .keyboardButtonRequestPeer(let text, let buttonId, let peerType, let maxQuantity):
return ("keyboardButtonRequestPeer", [("text", text as Any), ("buttonId", buttonId as Any), ("peerType", peerType as Any)]) return ("keyboardButtonRequestPeer", [("text", text as Any), ("buttonId", buttonId as Any), ("peerType", peerType as Any), ("maxQuantity", maxQuantity as Any)])
case .keyboardButtonRequestPhone(let text): case .keyboardButtonRequestPhone(let text):
return ("keyboardButtonRequestPhone", [("text", text as Any)]) return ("keyboardButtonRequestPhone", [("text", text as Any)])
case .keyboardButtonRequestPoll(let flags, let quiz, let text): case .keyboardButtonRequestPoll(let flags, let quiz, let text):
@ -714,11 +715,14 @@ public extension Api {
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.RequestPeerType _3 = Api.parse(reader, signature: signature) as? Api.RequestPeerType
} }
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
if _c1 && _c2 && _c3 { let _c4 = _4 != nil
return Api.KeyboardButton.keyboardButtonRequestPeer(text: _1!, buttonId: _2!, peerType: _3!) if _c1 && _c2 && _c3 && _c4 {
return Api.KeyboardButton.keyboardButtonRequestPeer(text: _1!, buttonId: _2!, peerType: _3!, maxQuantity: _4!)
} }
else { else {
return nil return nil

View File

@ -683,7 +683,7 @@ public extension Api {
case messageActionPaymentSentMe(flags: Int32, currency: String, totalAmount: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, charge: Api.PaymentCharge) case messageActionPaymentSentMe(flags: Int32, currency: String, totalAmount: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, charge: Api.PaymentCharge)
case messageActionPhoneCall(flags: Int32, callId: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?) case messageActionPhoneCall(flags: Int32, callId: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?)
case messageActionPinMessage case messageActionPinMessage
case messageActionRequestedPeer(buttonId: Int32, peer: Api.Peer) case messageActionRequestedPeer(buttonId: Int32, peers: [Api.Peer])
case messageActionScreenshotTaken case messageActionScreenshotTaken
case messageActionSecureValuesSent(types: [Api.SecureValueType]) case messageActionSecureValuesSent(types: [Api.SecureValueType])
case messageActionSecureValuesSentMe(values: [Api.SecureValue], credentials: Api.SecureCredentialsEncrypted) case messageActionSecureValuesSentMe(values: [Api.SecureValue], credentials: Api.SecureCredentialsEncrypted)
@ -920,12 +920,16 @@ public extension Api {
} }
break break
case .messageActionRequestedPeer(let buttonId, let peer): case .messageActionRequestedPeer(let buttonId, let peers):
if boxed { if boxed {
buffer.appendInt32(-25742243) buffer.appendInt32(827428507)
} }
serializeInt32(buttonId, buffer: buffer, boxed: false) serializeInt32(buttonId, buffer: buffer, boxed: false)
peer.serialize(buffer, true) buffer.appendInt32(481674261)
buffer.appendInt32(Int32(peers.count))
for item in peers {
item.serialize(buffer, true)
}
break break
case .messageActionScreenshotTaken: case .messageActionScreenshotTaken:
if boxed { if boxed {
@ -1076,8 +1080,8 @@ public extension Api {
return ("messageActionPhoneCall", [("flags", flags as Any), ("callId", callId as Any), ("reason", reason as Any), ("duration", duration as Any)]) return ("messageActionPhoneCall", [("flags", flags as Any), ("callId", callId as Any), ("reason", reason as Any), ("duration", duration as Any)])
case .messageActionPinMessage: case .messageActionPinMessage:
return ("messageActionPinMessage", []) return ("messageActionPinMessage", [])
case .messageActionRequestedPeer(let buttonId, let peer): case .messageActionRequestedPeer(let buttonId, let peers):
return ("messageActionRequestedPeer", [("buttonId", buttonId as Any), ("peer", peer as Any)]) return ("messageActionRequestedPeer", [("buttonId", buttonId as Any), ("peers", peers as Any)])
case .messageActionScreenshotTaken: case .messageActionScreenshotTaken:
return ("messageActionScreenshotTaken", []) return ("messageActionScreenshotTaken", [])
case .messageActionSecureValuesSent(let types): case .messageActionSecureValuesSent(let types):
@ -1505,14 +1509,14 @@ public extension Api {
public static func parse_messageActionRequestedPeer(_ reader: BufferReader) -> MessageAction? { public static func parse_messageActionRequestedPeer(_ reader: BufferReader) -> MessageAction? {
var _1: Int32? var _1: Int32?
_1 = reader.readInt32() _1 = reader.readInt32()
var _2: Api.Peer? var _2: [Api.Peer]?
if let signature = reader.readInt32() { if let _ = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Peer _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
if _c1 && _c2 { if _c1 && _c2 {
return Api.MessageAction.messageActionRequestedPeer(buttonId: _1!, peer: _2!) return Api.MessageAction.messageActionRequestedPeer(buttonId: _1!, peers: _2!)
} }
else { else {
return nil return nil

View File

@ -328,6 +328,21 @@ public extension Api.functions.account {
}) })
} }
} }
public extension Api.functions.account {
static func getChannelDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.EmojiStatuses>) {
let buffer = Buffer()
buffer.appendInt32(1999087573)
serializeInt64(hash, buffer: buffer, boxed: false)
return (FunctionDescription(name: "account.getChannelDefaultEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in
let reader = BufferReader(buffer)
var result: Api.account.EmojiStatuses?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses
}
return result
})
}
}
public extension Api.functions.account { public extension Api.functions.account {
static func getChannelRestrictedStatusEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.EmojiList>) { static func getChannelRestrictedStatusEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.EmojiList>) {
let buffer = Buffer() let buffer = Buffer()
@ -6750,14 +6765,18 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func sendBotRequestedPeer(peer: Api.InputPeer, msgId: Int32, buttonId: Int32, requestedPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func sendBotRequestedPeer(peer: Api.InputPeer, msgId: Int32, buttonId: Int32, requestedPeers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-29831141) buffer.appendInt32(-1850552224)
peer.serialize(buffer, true) peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(buttonId, buffer: buffer, boxed: false) serializeInt32(buttonId, buffer: buffer, boxed: false)
requestedPeer.serialize(buffer, true) buffer.appendInt32(481674261)
return (FunctionDescription(name: "messages.sendBotRequestedPeer", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("requestedPeer", String(describing: requestedPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in buffer.appendInt32(Int32(requestedPeers.count))
for item in requestedPeers {
item.serialize(buffer, true)
}
return (FunctionDescription(name: "messages.sendBotRequestedPeer", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("requestedPeers", String(describing: requestedPeers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -63,7 +63,7 @@ extension ReplyMarkupButton {
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: false)) self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: false))
case let .keyboardButtonSimpleWebView(text, url): case let .keyboardButtonSimpleWebView(text, url):
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: true)) self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: true))
case let .keyboardButtonRequestPeer(text, buttonId, peerType): case let .keyboardButtonRequestPeer(text, buttonId, peerType, maxQuantity):
let mappedPeerType: ReplyMarkupButtonRequestPeerType let mappedPeerType: ReplyMarkupButtonRequestPeerType
switch peerType { switch peerType {
case let .requestPeerTypeUser(_, bot, premium): case let .requestPeerTypeUser(_, bot, premium):
@ -88,7 +88,7 @@ extension ReplyMarkupButton {
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)) botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
)) ))
} }
self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId)) self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId, maxQuantity: maxQuantity))
} }
} }
} }

View File

@ -242,8 +242,8 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
for id in userIds { for id in userIds {
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))) result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)))
} }
case let .messageActionRequestedPeer(_, peer): case let .messageActionRequestedPeer(_, peers):
result.append(peer.peerId) result.append(contentsOf: peers.map(\.peerId))
case let .messageActionGiftCode(_, boostPeer, _, _, _, _, _, _): case let .messageActionGiftCode(_, boostPeer, _, _, _, _, _, _):
if let boostPeer = boostPeer { if let boostPeer = boostPeer {
result.append(boostPeer.peerId) result.append(boostPeer.peerId)

View File

@ -121,8 +121,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return TelegramMediaAction(action: .topicEdited(components: components)) return TelegramMediaAction(action: .topicEdited(components: components))
case let .messageActionSuggestProfilePhoto(photo): case let .messageActionSuggestProfilePhoto(photo):
return TelegramMediaAction(action: .suggestedProfilePhoto(image: telegramMediaImageFromApiPhoto(photo))) return TelegramMediaAction(action: .suggestedProfilePhoto(image: telegramMediaImageFromApiPhoto(photo)))
case let .messageActionRequestedPeer(buttonId, peer): case let .messageActionRequestedPeer(buttonId, peers):
return TelegramMediaAction(action: .requestedPeer(buttonId: buttonId, peerId: peer.peerId)) return TelegramMediaAction(action: .requestedPeer(buttonId: buttonId, peerIds: peers.map { $0.peerId }))
case let .messageActionSetChatWallPaper(flags, wallpaper): case let .messageActionSetChatWallPaper(flags, wallpaper):
if (flags & (1 << 0)) != 0 { if (flags & (1 << 0)) != 0 {
return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper))) return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper)))

View File

@ -70,6 +70,8 @@ extension StickerPackReference {
self = .iconStatusEmoji self = .iconStatusEmoji
case .inputStickerSetEmojiDefaultTopicIcons: case .inputStickerSetEmojiDefaultTopicIcons:
self = .iconTopicEmoji self = .iconTopicEmoji
case .inputStickerSetEmojiChannelDefaultStatuses:
return nil
} }
} }
} }

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization { public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt { public func currentLayer() -> UInt {
return 168 return 169
} }
public func parseMessage(_ data: Data!) -> Any! { public func parseMessage(_ data: Data!) -> Any! {

View File

@ -231,7 +231,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
case setupPoll(isQuiz: Bool?) case setupPoll(isQuiz: Bool?)
case openUserProfile(peerId: PeerId) case openUserProfile(peerId: PeerId)
case openWebView(url: String, simple: Bool) case openWebView(url: String, simple: Bool)
case requestPeer(peerType: ReplyMarkupButtonRequestPeerType, buttonId: Int32) case requestPeer(peerType: ReplyMarkupButtonRequestPeerType, buttonId: Int32, maxQuantity: Int32)
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("v", orElse: 0) { switch decoder.decodeInt32ForKey("v", orElse: 0) {
@ -260,7 +260,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
case 11: case 11:
self = .openWebView(url: decoder.decodeStringForKey("u", orElse: ""), simple: decoder.decodeInt32ForKey("s", orElse: 0) != 0) self = .openWebView(url: decoder.decodeStringForKey("u", orElse: ""), simple: decoder.decodeInt32ForKey("s", orElse: 0) != 0)
case 12: case 12:
self = .requestPeer(peerType: decoder.decode(ReplyMarkupButtonRequestPeerType.self, forKey: "pt") ?? ReplyMarkupButtonRequestPeerType.user(ReplyMarkupButtonRequestPeerType.User(isBot: nil, isPremium: nil)), buttonId: decoder.decodeInt32ForKey("b", orElse: 0)) self = .requestPeer(peerType: decoder.decode(ReplyMarkupButtonRequestPeerType.self, forKey: "pt") ?? ReplyMarkupButtonRequestPeerType.user(ReplyMarkupButtonRequestPeerType.User(isBot: nil, isPremium: nil)), buttonId: decoder.decodeInt32ForKey("b", orElse: 0), maxQuantity: decoder.decodeInt32ForKey("q", orElse: 1))
default: default:
self = .text self = .text
} }
@ -308,10 +308,11 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
encoder.encodeInt32(11, forKey: "v") encoder.encodeInt32(11, forKey: "v")
encoder.encodeString(url, forKey: "u") encoder.encodeString(url, forKey: "u")
encoder.encodeInt32(simple ? 1 : 0, forKey: "s") encoder.encodeInt32(simple ? 1 : 0, forKey: "s")
case let .requestPeer(peerType, buttonId): case let .requestPeer(peerType, buttonId, maxQuantity):
encoder.encodeInt32(12, forKey: "v") encoder.encodeInt32(12, forKey: "v")
encoder.encodeInt32(buttonId, forKey: "b") encoder.encodeInt32(buttonId, forKey: "b")
encoder.encode(peerType, forKey: "pt") encoder.encode(peerType, forKey: "pt")
encoder.encodeInt32(maxQuantity, forKey: "q")
} }
} }
} }

View File

@ -119,7 +119,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case topicEdited(components: [ForumTopicEditComponent]) case topicEdited(components: [ForumTopicEditComponent])
case suggestedProfilePhoto(image: TelegramMediaImage?) case suggestedProfilePhoto(image: TelegramMediaImage?)
case attachMenuBotAllowed case attachMenuBotAllowed
case requestedPeer(buttonId: Int32, peerId: PeerId) case requestedPeer(buttonId: Int32, peerIds: [PeerId])
case setChatWallpaper(wallpaper: TelegramWallpaper, forBoth: Bool) case setChatWallpaper(wallpaper: TelegramWallpaper, forBoth: Bool)
case setSameChatWallpaper(wallpaper: TelegramWallpaper) case setSameChatWallpaper(wallpaper: TelegramWallpaper)
case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?) case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?)
@ -205,7 +205,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case 31: case 31:
self = .attachMenuBotAllowed self = .attachMenuBotAllowed
case 32: case 32:
self = .requestedPeer(buttonId: decoder.decodeInt32ForKey("b", orElse: 0), peerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0))) var peerIds = decoder.decodeInt64ArrayForKey("pis").map { PeerId($0) }
if peerIds.isEmpty {
peerIds = [PeerId(decoder.decodeInt64ForKey("pi", orElse: 0))]
}
self = .requestedPeer(buttonId: decoder.decodeInt32ForKey("b", orElse: 0), peerIds: peerIds)
case 33: case 33:
if let wallpaper = decoder.decode(TelegramWallpaperNativeCodable.self, forKey: "wallpaper")?.value { if let wallpaper = decoder.decode(TelegramWallpaperNativeCodable.self, forKey: "wallpaper")?.value {
self = .setChatWallpaper(wallpaper: wallpaper, forBoth: decoder.decodeBoolForKey("both", orElse: false)) self = .setChatWallpaper(wallpaper: wallpaper, forBoth: decoder.decodeBoolForKey("both", orElse: false))
@ -385,10 +389,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
} }
case .attachMenuBotAllowed: case .attachMenuBotAllowed:
encoder.encodeInt32(31, forKey: "_rawValue") encoder.encodeInt32(31, forKey: "_rawValue")
case let .requestedPeer(buttonId, peerId): case let .requestedPeer(buttonId, peerIds):
encoder.encodeInt32(32, forKey: "_rawValue") encoder.encodeInt32(32, forKey: "_rawValue")
encoder.encodeInt32(buttonId, forKey: "b") encoder.encodeInt32(buttonId, forKey: "b")
encoder.encodeInt64(peerId.toInt64(), forKey: "pi") encoder.encodeInt64Array(peerIds.map { $0.toInt64() }, forKey: "pis")
case let .setChatWallpaper(wallpaper, forBoth): case let .setChatWallpaper(wallpaper, forBoth):
encoder.encodeInt32(33, forKey: "_rawValue") encoder.encodeInt32(33, forKey: "_rawValue")
encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper") encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper")
@ -466,8 +470,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
return [from, to] return [from, to]
case let .inviteToGroupPhoneCall(_, _, peerIds): case let .inviteToGroupPhoneCall(_, _, peerIds):
return peerIds return peerIds
case let .requestedPeer(_, peerId): case let .requestedPeer(_, peerIds):
return [peerId] return peerIds
case let .giftCode(_, _, _, boostPeerId, _, _, _, _, _): case let .giftCode(_, _, _, boostPeerId, _, _, _, _, _):
return boostPeerId.flatMap { [$0] } ?? [] return boostPeerId.flatMap { [$0] } ?? []
default: default:

View File

@ -235,17 +235,17 @@ public enum SendBotRequestedPeerError {
case generic case generic
} }
func _internal_sendBotRequestedPeer(account: Account, peerId: PeerId, messageId: MessageId, buttonId: Int32, requestedPeerId: PeerId) -> Signal<Void, SendBotRequestedPeerError> { func _internal_sendBotRequestedPeer(account: Account, peerId: PeerId, messageId: MessageId, buttonId: Int32, requestedPeerIds: [PeerId]) -> Signal<Void, SendBotRequestedPeerError> {
let signal = account.postbox.transaction { transaction -> Signal<Void, SendBotRequestedPeerError> in return account.postbox.transaction { transaction -> Signal<Void, SendBotRequestedPeerError> in
if let peer = transaction.getPeer(peerId) {
var inputRequestedPeers: [Api.InputPeer] = []
if let peer = transaction.getPeer(peerId), let requestedPeer = transaction.getPeer(requestedPeerId) { for requestedPeerId in requestedPeerIds {
if let requestedPeer = transaction.getPeer(requestedPeerId), let inputRequestedPeer = apiInputPeer(requestedPeer) {
let inputPeer = apiInputPeer(peer) inputRequestedPeers.append(inputRequestedPeer)
let inputRequestedPeer = apiInputPeer(requestedPeer) }
}
if let inputPeer = inputPeer, let inputRequestedPeer = inputRequestedPeer { if let inputPeer = apiInputPeer(peer), !inputRequestedPeers.isEmpty {
let signal = account.network.request(Api.functions.messages.sendBotRequestedPeer(peer: inputPeer, msgId: messageId.id, buttonId: buttonId, requestedPeer: inputRequestedPeer)) let signal = account.network.request(Api.functions.messages.sendBotRequestedPeer(peer: inputPeer, msgId: messageId.id, buttonId: buttonId, requestedPeers: inputRequestedPeers))
|> mapError { error -> SendBotRequestedPeerError in |> mapError { error -> SendBotRequestedPeerError in
return .generic return .generic
} }
@ -254,12 +254,9 @@ func _internal_sendBotRequestedPeer(account: Account, peerId: PeerId, messageId:
} }
return signal return signal
} }
} }
return .single(Void()) return .single(Void())
} }
|> castError(SendBotRequestedPeerError.self) |> castError(SendBotRequestedPeerError.self)
return signal
|> switchToLatest |> switchToLatest
} }

View File

@ -504,8 +504,8 @@ public extension TelegramEngine {
return _internal_addChannelMember(account: self.account, peerId: peerId, memberId: memberId) return _internal_addChannelMember(account: self.account, peerId: peerId, memberId: memberId)
} }
public func sendBotRequestedPeer(messageId: MessageId, buttonId: Int32, requestedPeerId: PeerId) -> Signal<Void, SendBotRequestedPeerError> { public func sendBotRequestedPeer(messageId: MessageId, buttonId: Int32, requestedPeerIds: [PeerId]) -> Signal<Void, SendBotRequestedPeerError> {
return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerId: requestedPeerId) return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerIds: requestedPeerIds)
} }
public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal<Void, AddChannelMemberError> { public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal<Void, AddChannelMemberError> {

View File

@ -886,10 +886,19 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
case .attachMenuBotAllowed: case .attachMenuBotAllowed:
attributedString = NSAttributedString(string: strings.Notification_BotWriteAllowed, font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: strings.Notification_BotWriteAllowed, font: titleFont, textColor: primaryTextColor)
case let .requestedPeer(_, peerId): case let .requestedPeer(_, peerIds):
let botName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" let botName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""
let peerName = message.peers[peerId].flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" var attributePeerIds: [(Int, EnginePeer.Id?)] = []
attributedString = addAttributesToStringWithRanges(strings.Notification_RequestedPeer(peerName, botName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, peerId), (1, message.id.peerId)])) let resultTitleString: PresentationStrings.FormattedString
if peerIds.count == 1 {
attributePeerIds.append((0, peerIds.first))
attributePeerIds.append((1, message.id.peerId))
resultTitleString = strings.Notification_RequestedPeer(peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder), botName)
} else {
attributePeerIds.append((1, message.id.peerId))
resultTitleString = strings.Notification_RequestedPeerMultiple(peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder), botName)
}
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
case let .setChatWallpaper(_, forBoth): case let .setChatWallpaper(_, forBoth):
if message.author?.id == accountPeerId { if message.author?.id == accountPeerId {
if forBoth { if forBoth {

View File

@ -434,9 +434,9 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
}) })
case let .openWebView(url, simple): case let .openWebView(url, simple):
self.controllerInteraction.openWebView(markupButton.title, url, simple, .generic) self.controllerInteraction.openWebView(markupButton.title, url, simple, .generic)
case let .requestPeer(peerType, buttonId): case let .requestPeer(peerType, buttonId, maxQuantity):
if let message = self.message { if let message = self.message {
self.controllerInteraction.openRequestedPeerSelection(message.id, peerType, buttonId) self.controllerInteraction.openRequestedPeerSelection(message.id, peerType, buttonId, maxQuantity)
} }
} }
if dismissIfOnce { if dismissIfOnce {

View File

@ -565,7 +565,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in }, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in }, openRequestedPeerSelection: { _, _, _, _ in
}, saveMediaToFiles: { _ in }, saveMediaToFiles: { _ in
}, openNoAdsDemo: { }, openNoAdsDemo: {
}, displayGiveawayParticipationStatus: { _ in }, displayGiveawayParticipationStatus: { _ in

View File

@ -225,7 +225,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
public let openJoinLink: (String) -> Void public let openJoinLink: (String) -> Void
public let openWebView: (String, String, Bool, ChatOpenWebViewSource) -> Void public let openWebView: (String, String, Bool, ChatOpenWebViewSource) -> Void
public let activateAdAction: (EngineMessage.Id) -> Void public let activateAdAction: (EngineMessage.Id) -> Void
public let openRequestedPeerSelection: (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void public let openRequestedPeerSelection: (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32, Int32) -> Void
public let saveMediaToFiles: (EngineMessage.Id) -> Void public let saveMediaToFiles: (EngineMessage.Id) -> Void
public let openNoAdsDemo: () -> Void public let openNoAdsDemo: () -> Void
public let displayGiveawayParticipationStatus: (EngineMessage.Id) -> Void public let displayGiveawayParticipationStatus: (EngineMessage.Id) -> Void
@ -346,7 +346,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
openJoinLink: @escaping (String) -> Void, openJoinLink: @escaping (String) -> Void,
openWebView: @escaping (String, String, Bool, ChatOpenWebViewSource) -> Void, openWebView: @escaping (String, String, Bool, ChatOpenWebViewSource) -> Void,
activateAdAction: @escaping (EngineMessage.Id) -> Void, activateAdAction: @escaping (EngineMessage.Id) -> Void,
openRequestedPeerSelection: @escaping (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void, openRequestedPeerSelection: @escaping (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32, Int32) -> Void,
saveMediaToFiles: @escaping (EngineMessage.Id) -> Void, saveMediaToFiles: @escaping (EngineMessage.Id) -> Void,
openNoAdsDemo: @escaping () -> Void, openNoAdsDemo: @escaping () -> Void,
displayGiveawayParticipationStatus: @escaping (EngineMessage.Id) -> Void, displayGiveawayParticipationStatus: @escaping (EngineMessage.Id) -> Void,

View File

@ -2996,7 +2996,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in }, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in }, openRequestedPeerSelection: { _, _, _, _ in
}, saveMediaToFiles: { _ in }, saveMediaToFiles: { _ in
}, openNoAdsDemo: { }, openNoAdsDemo: {
}, displayGiveawayParticipationStatus: { _ in }, displayGiveawayParticipationStatus: { _ in

View File

@ -4174,17 +4174,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
} }
}, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId in }, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId, maxQuantity in
guard let self else { guard let self else {
return return
} }
let botName = self.presentationInterfaceState.renderedPeer?.peer.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? "" let botName = self.presentationInterfaceState.renderedPeer?.peer.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
let context = self.context let context = self.context
let peerId = self.chatLocation.peerId let peerId = self.chatLocation.peerId
var createNewGroupImpl: (() -> Void)?
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [peerType], hasContactSelector: false, createNewGroup: {
createNewGroupImpl?()
}, hasCreation: true))
let presentConfirmation: (String, Bool, @escaping () -> Void) -> Void = { [weak self] peerName, isChannel, completion in let presentConfirmation: (String, Bool, @escaping () -> Void) -> Void = { [weak self] peerName, isChannel, completion in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -4242,18 +4238,64 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationSend, action: { let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationSend, action: {
completion() completion()
})]) })])
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
} }
if case .user = peerType, maxQuantity > 1 {
let presentationData = self.presentationData
var reachedLimitImpl: ((Int32) -> Void)?
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .requestedUsersSelection, options: [], isPeerEnabled: { peer in
if case let .user(user) = peer, user.botInfo == nil {
return true
} else {
return false
}
}, limit: maxQuantity, reachedLimit: { limit in
reachedLimitImpl?(limit)
}))
controller.navigationPresentation = .modal
reachedLimitImpl = { [weak controller] limit in
guard let controller else {
return
}
HapticFeedback().error()
controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.RequestPeer_ReachedMaximum(limit), timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
}
let _ = (controller.result
|> deliverOnMainQueue).startStandalone(next: { [weak controller] result in
guard let controller else {
return
}
var peerIds: [PeerId] = []
if case let .result(peerIdsValue, _) = result {
peerIds = peerIdsValue.compactMap({ peerId in
if case let .peer(peerId) = peerId {
return peerId
} else {
return nil
}
})
}
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: peerIds).startStandalone()
controller.dismiss()
})
self.push(controller)
} else {
var createNewGroupImpl: (() -> Void)?
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [peerType], hasContactSelector: false, createNewGroup: {
createNewGroupImpl?()
}, hasCreation: true))
controller.peerSelected = { [weak self, weak controller] peer, _ in controller.peerSelected = { [weak self, weak controller] peer, _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if case .user = peerType { if case .user = peerType {
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).startStandalone() let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: [peer.id]).startStandalone()
controller?.dismiss() controller?.dismiss()
} else { } else {
var isChannel = false var isChannel = false
@ -4262,7 +4304,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
let peerName = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) let peerName = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
presentConfirmation(peerName, isChannel, { presentConfirmation(peerName, isChannel, {
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).startStandalone() let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: [peer.id]).startStandalone()
controller?.dismiss() controller?.dismiss()
}) })
} }
@ -4277,7 +4319,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
complete() complete()
}) })
}, completion: { peerId, dismiss in }, completion: { peerId, dismiss in
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peerId).startStandalone() let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: [peerId]).startStandalone()
dismiss() dismiss()
}) })
createGroupController.navigationPresentation = .modal createGroupController.navigationPresentation = .modal
@ -4288,7 +4330,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
complete() complete()
}) })
}, completion: { peerId, dismiss in }, completion: { peerId, dismiss in
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peerId).startStandalone() let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: [peerId]).startStandalone()
dismiss() dismiss()
}) })
createChannelController.navigationPresentation = .modal createChannelController.navigationPresentation = .modal
@ -4296,6 +4338,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
self.push(controller) self.push(controller)
}
}, saveMediaToFiles: { [weak self] messageId in }, saveMediaToFiles: { [weak self] messageId in
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|> deliverOnMainQueue).startStandalone(next: { message in |> deliverOnMainQueue).startStandalone(next: { message in

View File

@ -202,12 +202,19 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
self.navigationItem.rightBarButtonItem = self.rightNavigationButton self.navigationItem.rightBarButtonItem = self.rightNavigationButton
rightNavigationButton.isEnabled = true //count != 0 || self.params.alwaysEnabled rightNavigationButton.isEnabled = true //count != 0 || self.params.alwaysEnabled
case .premiumGifting: case .premiumGifting:
let maxCount: Int32 = 10 let maxCount: Int32 = self.limit ?? 10
var count = 0 var count = 0
if case let .contacts(contactsNode) = self.contactsNode.contentNode { if case let .contacts(contactsNode) = self.contactsNode.contentNode {
count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0 count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0
} }
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(count)/\(maxCount)") self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(count)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = self.limit ?? 10
var count = 0
if case let .contacts(contactsNode) = self.contactsNode.contentNode {
count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0
}
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.RequestPeer_SelectUsers, counter: "\(count)/\(maxCount)")
case .channelCreation: case .channelCreation:
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.GroupInfo_AddParticipantTitle, counter: "") self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.GroupInfo_AddParticipantTitle, counter: "")
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed)) let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
@ -325,7 +332,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
switch strongSelf.mode { switch strongSelf.mode {
case .groupCreation, .peerSelection, .chatSelection: case .groupCreation, .peerSelection, .chatSelection:
strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled
case .channelCreation, .premiumGifting: case .channelCreation, .premiumGifting, .requestedUsersSelection:
break break
} }
@ -334,8 +341,11 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000 let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)") strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)")
case .premiumGifting: case .premiumGifting:
let maxCount: Int32 = 10 let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)") strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.RequestPeer_SelectUsers, counter: "\(updatedCount)/\(maxCount)")
case .peerSelection, .channelCreation, .chatSelection: case .peerSelection, .channelCreation, .chatSelection:
break break
} }
@ -407,7 +417,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
switch strongSelf.mode { switch strongSelf.mode {
case .groupCreation, .peerSelection, .chatSelection: case .groupCreation, .peerSelection, .chatSelection:
strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled
case .channelCreation, .premiumGifting: case .channelCreation, .premiumGifting, .requestedUsersSelection:
break break
} }
switch strongSelf.mode { switch strongSelf.mode {
@ -415,8 +425,11 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000 let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)") strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)")
case .premiumGifting: case .premiumGifting:
let maxCount: Int32 = 10 let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)") strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.RequestPeer_SelectUsers, counter: "\(updatedCount)/\(maxCount)")
case .peerSelection, .channelCreation, .chatSelection: case .peerSelection, .channelCreation, .chatSelection:
break break
} }

View File

@ -108,6 +108,11 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
self.footerPanelNode = FooterPanelNode(theme: self.presentationData.theme, strings: self.presentationData.strings, action: { self.footerPanelNode = FooterPanelNode(theme: self.presentationData.theme, strings: self.presentationData.strings, action: {
proceedImpl?() proceedImpl?()
}) })
case .requestedUsersSelection:
placeholder = self.presentationData.strings.RequestPeer_SelectUsers_SearchPlaceholder
self.footerPanelNode = FooterPanelNode(theme: self.presentationData.theme, strings: self.presentationData.strings, action: {
proceedImpl?()
})
default: default:
placeholder = self.presentationData.strings.Compose_TokenListPlaceholder placeholder = self.presentationData.strings.Compose_TokenListPlaceholder
self.footerPanelNode = nil self.footerPanelNode = nil
@ -149,6 +154,8 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
var displayTopPeers = false var displayTopPeers = false
if case .premiumGifting = mode { if case .premiumGifting = mode {
displayTopPeers = true displayTopPeers = true
} else if case .requestedUsersSelection = mode {
displayTopPeers = true
} }
self.contentNode = .contacts(ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, selectionState: ContactListNodeGroupSelectionState())) self.contentNode = .contacts(ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, selectionState: ContactListNodeGroupSelectionState()))
} }
@ -233,7 +240,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
searchGroups = true searchGroups = true
searchChannels = true searchChannels = true
globalSearch = false globalSearch = false
case .premiumGifting: case .premiumGifting, .requestedUsersSelection:
searchChatList = true searchChatList = true
} }
let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups, searchChannels: searchChannels, globalSearch: globalSearch)), filters: filters, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true) let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups, searchChannels: searchChannels, globalSearch: globalSearch)), filters: filters, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true)

View File

@ -166,7 +166,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in }, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in }, openRequestedPeerSelection: { _, _, _, _ in
}, saveMediaToFiles: { _ in }, saveMediaToFiles: { _ in
}, openNoAdsDemo: { }, openNoAdsDemo: {
}, displayGiveawayParticipationStatus: { _ in }, displayGiveawayParticipationStatus: { _ in

View File

@ -1670,7 +1670,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in }, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in }, openRequestedPeerSelection: { _, _, _, _ in
}, saveMediaToFiles: { _ in }, saveMediaToFiles: { _ in
}, openNoAdsDemo: { }, openNoAdsDemo: {
}, displayGiveawayParticipationStatus: { _ in }, displayGiveawayParticipationStatus: { _ in