diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index b322fb591b..85eaa12dba 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8686,13 +8686,14 @@ Sorry for the inconvenience."; "RequestPeer.SelectionConfirmationTitle" = "Are you sure you want to send %1$@ to %2$@?"; "RequestPeer.SelectionConfirmationInviteText" = "This will also add %1$@ to %2$@."; +"RequestPeer.SelectionConfirmationInviteAdminText" = "This will also add %1$@ to %2$@ as an admin."; "RequestPeer.SelectionConfirmationInviteWithRightsText" = "This will also add %1$@ to %2$@ with the following rights:\n\n%3$@"; "RequestPeer.SelectionConfirmationSend" = "Send"; "CreateGroup.PublicLinkTitle" = "SET A PUBLIC LINK"; "CreateGroup.PublicLinkInfo" = "You can use **a-z**, **0-9** and underscores. Minimum length is **5** characters."; -"Notification.RequestedPeer" = "You shared %@ with the bot."; +"Notification.RequestedPeer" = "You shared %1$@ with %2$@."; "Conversation.ViewInChannel" = "View in Channel"; @@ -8706,3 +8707,14 @@ Sorry for the inconvenience."; "Conversation.Translation.Hide" = "Hide"; "Conversation.Translation.AddedToDoNotTranslateText" = "**%@** is added to the Do Not Translate list."; "Conversation.Translation.TranslationBarHiddenText" = "Translation bar is now hidden for this channel."; + +"AvatarEditor.Background" = "BACKGROUND"; +"AvatarEditor.EmojiOrSticker" = "EMOJI OR STICKER"; +"AvatarEditor.Emoji" = "EMOJI"; +"AvatarEditor.Stickers" = "STICKERS"; +"AvatarEditor.SwitchToEmoji" = "SWITCH TO EMOJI"; +"AvatarEditor.SwitchToStickers" = "SWITCH TO STICKERS"; + +"AvatarEditor.SetVideo" = "Set Video"; + +"AvatarEditor.Set" = "Set"; diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 1f91bb562b..0e2ee1396e 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -98,7 +98,7 @@ public enum ChatInputMode: Equatable { case none case text case media(mode: ChatMediaInputMode, expanded: ChatMediaInputExpanded?, focused: Bool) - case inputButtons + case inputButtons(persistent: Bool) } public enum ChatTitlePanelContext: Equatable, Comparable { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 4de19de1e5..54c8f2eed5 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -1077,9 +1077,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[816245886] = { return Api.messages.Stickers.parse_stickers($0) } dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) } dict[-1821037486] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) } - dict[1741309751] = { return Api.messages.TranslatedText.parse_translateNoResult($0) } dict[870003448] = { return Api.messages.TranslatedText.parse_translateResult($0) } - dict[-1575684144] = { return Api.messages.TranslatedText.parse_translateResultText($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) } dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) } diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 42309a4b33..3713973863 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1344,18 +1344,10 @@ public extension Api.messages { } public extension Api.messages { enum TranslatedText: TypeConstructorDescription { - case translateNoResult case translateResult(result: [Api.TextWithEntities]) - case translateResultText(text: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .translateNoResult: - if boxed { - buffer.appendInt32(1741309751) - } - - break case .translateResult(let result): if boxed { buffer.appendInt32(870003448) @@ -1366,29 +1358,16 @@ public extension Api.messages { item.serialize(buffer, true) } break - case .translateResultText(let text): - if boxed { - buffer.appendInt32(-1575684144) - } - serializeString(text, buffer: buffer, boxed: false) - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .translateNoResult: - return ("translateNoResult", []) case .translateResult(let result): return ("translateResult", [("result", result as Any)]) - case .translateResultText(let text): - return ("translateResultText", [("text", text as Any)]) } } - public static func parse_translateNoResult(_ reader: BufferReader) -> TranslatedText? { - return Api.messages.TranslatedText.translateNoResult - } public static func parse_translateResult(_ reader: BufferReader) -> TranslatedText? { var _1: [Api.TextWithEntities]? if let _ = reader.readInt32() { @@ -1402,12 +1381,65 @@ public extension Api.messages { return nil } } - public static func parse_translateResultText(_ reader: BufferReader) -> TranslatedText? { - var _1: String? - _1 = parseString(reader) + + } +} +public extension Api.messages { + enum VotesList: TypeConstructorDescription { + case votesList(flags: Int32, count: Int32, votes: [Api.MessageUserVote], users: [Api.User], nextOffset: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .votesList(let flags, let count, let votes, let users, let nextOffset): + if boxed { + buffer.appendInt32(136574537) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(votes.count)) + for item in votes { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .votesList(let flags, let count, let votes, let users, let nextOffset): + return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + } + } + + public static func parse_votesList(_ reader: BufferReader) -> VotesList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.MessageUserVote]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserVote.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _5: String? + if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) } let _c1 = _1 != nil - if _c1 { - return Api.messages.TranslatedText.translateResultText(text: _1!) + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, users: _4!, nextOffset: _5) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 7a33c0f4b4..508590d536 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1,67 +1,3 @@ -public extension Api.messages { - enum VotesList: TypeConstructorDescription { - case votesList(flags: Int32, count: Int32, votes: [Api.MessageUserVote], users: [Api.User], nextOffset: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .votesList(let flags, let count, let votes, let users, let nextOffset): - if boxed { - buffer.appendInt32(136574537) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(votes.count)) - for item in votes { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .votesList(let flags, let count, let votes, let users, let nextOffset): - return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) - } - } - - public static func parse_votesList(_ reader: BufferReader) -> VotesList? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.MessageUserVote]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserVote.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _5: String? - if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, users: _4!, nextOffset: _5) - } - else { - return nil - } - } - - } -} public extension Api.payments { enum BankCardData: TypeConstructorDescription { case bankCardData(title: String, openUrls: [Api.BankCardOpenUrl]) diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 700b36a3d5..de241641ea 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -6736,6 +6736,22 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func togglePeerTranslations(flags: Int32, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-461589127) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.togglePeerTranslations", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), 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 extension Api.functions.messages { static func toggleStickerSets(flags: Int32, stickersets: [Api.InputStickerSet]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 00c1e83abe..191b3bdce0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -356,12 +356,16 @@ public extension TelegramEngine { return EngineMessageReactionListContext(account: self.account, message: message, reaction: reaction) } - public func translate(text: String, fromLang: String?, toLang: String) -> Signal { - return _internal_translate(network: self.account.network, text: text, fromLang: fromLang, toLang: toLang) + public func translate(text: String, toLang: String) -> Signal { + return _internal_translate(network: self.account.network, text: text, toLang: toLang) } public func translateMessages(messageIds: [EngineMessage.Id], toLang: String) -> Signal { - return _internal_translateMessages(postbox: self.account.postbox, network: self.account.network, messageIds: messageIds, toLang: toLang) + return _internal_translateMessages(account: self.account, messageIds: messageIds, toLang: toLang) + } + + public func togglePeerMessagesTranslationHidden(peerId: EnginePeer.Id, hidden: Bool) -> Signal { + return _internal_togglePeerMessagesTranslationHidden(account: self.account, peerId: peerId, hidden: hidden) } public func transcribeAudio(messageId: MessageId) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index f5c1e4f113..e5c08f1c7c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -4,7 +4,7 @@ import SwiftSignalKit import TelegramApi import MtProtoKit -func _internal_translate(network: Network, text: String, fromLang: String?, toLang: String) -> Signal { +func _internal_translate(network: Network, text: String, toLang: String) -> Signal { var flags: Int32 = 0 flags |= (1 << 1) @@ -18,10 +18,6 @@ func _internal_translate(network: Network, text: String, fromLang: String?, toLa return .complete() } switch result { - case .translateNoResult: - return .single(nil) - case let .translateResultText(text): - return .single(text) case let .translateResult(results): if case let .textWithEntities(text, _) = results.first { return .single(text) @@ -32,11 +28,11 @@ func _internal_translate(network: Network, text: String, fromLang: String?, toLa } } -func _internal_translateMessages(postbox: Postbox, network: Network, messageIds: [EngineMessage.Id], toLang: String) -> Signal { +func _internal_translateMessages(account: Account, messageIds: [EngineMessage.Id], toLang: String) -> Signal { guard let peerId = messageIds.first?.peerId else { return .never() } - return postbox.transaction { transaction -> Api.InputPeer? in + return account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } |> mapToSignal { inputPeer -> Signal in @@ -48,7 +44,7 @@ func _internal_translateMessages(postbox: Postbox, network: Network, messageIds: flags |= (1 << 0) let id: [Int32] = messageIds.map { $0.id } - return network.request(Api.functions.messages.translateText(flags: flags, peer: inputPeer, id: id, text: nil, toLang: toLang)) + return account.network.request(Api.functions.messages.translateText(flags: flags, peer: inputPeer, id: id, text: nil, toLang: toLang)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -57,7 +53,7 @@ func _internal_translateMessages(postbox: Postbox, network: Network, messageIds: guard let result = result, case let .translateResult(results) = result else { return .complete() } - return postbox.transaction { transaction in + return account.postbox.transaction { transaction in var index = 0 for result in results { let messageId = messageIds[index] @@ -79,6 +75,28 @@ func _internal_translateMessages(postbox: Postbox, network: Network, messageIds: } } +func _internal_togglePeerMessagesTranslationHidden(account: Account, peerId: EnginePeer.Id, hidden: Bool) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .never() + } + var flags: Int32 = 0 + if hidden { + flags |= (1 << 0) + } + + return account.network.request(Api.functions.messages.togglePeerTranslations(flags: flags, peer: inputPeer)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> ignoreValues + } +} + public enum EngineAudioTranscriptionResult { case success case error diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 4fbb91613c..904571f73e 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -834,8 +834,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .attachMenuBotAllowed: attributedString = NSAttributedString(string: strings.Notification_BotWriteAllowed, font: titleFont, textColor: primaryTextColor) case let .requestedPeer(_, peerId): + 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) ?? "" - attributedString = addAttributesToStringWithRanges(strings.Notification_RequestedPeer(peerName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, peerId)])) + attributedString = addAttributesToStringWithRanges(strings.Notification_RequestedPeer(peerName, botName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, peerId), (1, message.id.peerId)])) case .unknown: attributedString = nil } diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 3648e9902b..df862dfcba 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -127,13 +127,13 @@ final class AvatarEditorScreenComponent: Component { super.init() if let initialFileId, let initialBackgroundColors { - let _ = context.engine.stickers.resolveInlineStickers(fileIds: [initialFileId]) - |> map { [weak self] files in + let _ = (context.engine.stickers.resolveInlineStickers(fileIds: [initialFileId]) + |> deliverOnMainQueue).start(next: { [weak self] files in if let strongSelf = self, let file = files.values.first { strongSelf.selectedFile = file strongSelf.updated(transition: .immediate) } - } + }) self.selectedBackground = .gradient(initialBackgroundColors.map { UInt32(bitPattern: $0) }) self.previousColor = self.selectedBackground } else { @@ -227,9 +227,12 @@ final class AvatarEditorScreenComponent: Component { } private func updateData(_ data: KeyboardInputData) { + let wasEmpty = self.data == nil self.data = data - self.state?.selectedFile = data.emoji.panelItemGroups.first?.items.first?.itemFile + if wasEmpty && self.state?.selectedFile == nil { + self.state?.selectedFile = data.emoji.panelItemGroups.first?.items.first?.itemFile + } self.state?.updated(transition: .immediate) let updateSearchQuery: (String, String) -> Void = { [weak self] rawQuery, languageCode in @@ -656,6 +659,7 @@ final class AvatarEditorScreenComponent: Component { self.state = state let environment = environment[ViewControllerComponentContainer.Environment.self].value + let strings = environment.strings let controller = environment.controller self.controller = { @@ -703,7 +707,7 @@ final class AvatarEditorScreenComponent: Component { let navigationDoneButtonSize = self.navigationDoneButton.update( transition: transition, component: AnyComponent(Button( - content: AnyComponent(Text(text: "Set", font: Font.semibold(17.0), color: state.isSearchActive ? environment.theme.rootController.navigationBar.accentTextColor : .white)), + content: AnyComponent(Text(text: strings.AvatarEditor_Set, font: Font.semibold(17.0), color: state.isSearchActive ? environment.theme.rootController.navigationBar.accentTextColor : .white)), action: { [weak self] in guard let self else { return @@ -723,8 +727,8 @@ final class AvatarEditorScreenComponent: Component { } self.backgroundColor = environment.theme.list.blocksBackgroundColor - self.backgroundContainerView.backgroundColor = environment.theme.list.plainBackgroundColor - self.keyboardContainerView.backgroundColor = environment.theme.list.plainBackgroundColor + self.backgroundContainerView.backgroundColor = environment.theme.list.itemBlocksBackgroundColor + self.keyboardContainerView.backgroundColor = environment.theme.list.itemBlocksBackgroundColor self.panelSeparatorView.backgroundColor = environment.theme.list.itemPlainSeparatorColor if self.dataDisposable == nil { @@ -845,7 +849,7 @@ final class AvatarEditorScreenComponent: Component { transition: transition, component: AnyComponent(MultilineTextComponent( text: .markdown( - text: "Background".uppercased(), attributes: MarkdownAttributes( + text: strings.AvatarEditor_Background.uppercased(), attributes: MarkdownAttributes( body: body, bold: bold, link: body, @@ -955,14 +959,14 @@ final class AvatarEditorScreenComponent: Component { let keyboardSwitchTitle: String if state.isSearchActive { - keyboardTitle = "Emoji or Sticker" + keyboardTitle = strings.AvatarEditor_EmojiOrSticker keyboardSwitchTitle = " " } else if state.keyboardContentId == AnyHashable("emoji") { - keyboardTitle = "Emoji" - keyboardSwitchTitle = "Switch to Stickers" + keyboardTitle = strings.AvatarEditor_Emoji + keyboardSwitchTitle = strings.AvatarEditor_SwitchToStickers } else if state.keyboardContentId == AnyHashable("stickers") { - keyboardTitle = "Stickers" - keyboardSwitchTitle = "Switch to Emoji" + keyboardTitle = strings.AvatarEditor_Stickers + keyboardSwitchTitle = strings.AvatarEditor_SwitchToEmoji } else { keyboardTitle = " " keyboardSwitchTitle = " " @@ -1136,7 +1140,7 @@ final class AvatarEditorScreenComponent: Component { transition: transition, component: AnyComponent( SolidRoundedButtonComponent( - title: "Set Video", + title: strings.AvatarEditor_SetVideo, theme: SolidRoundedButtonComponent.Theme(theme: environment.theme), fontSize: 17.0, height: 50.0, @@ -1175,7 +1179,7 @@ final class AvatarEditorScreenComponent: Component { entity.scale = 3.3 var documentId: Int64 = 0 - if case let .file(file) = entity.content { + if case let .file(file) = entity.content, !file.isCustomEmoji { documentId = file.fileId.id } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index b871cbae01..da02b08c4c 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4155,11 +4155,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: peerType, hasContactSelector: false, createNewGroup: { createNewGroupImpl?() })) - controller.peerSelected = { [weak self, weak controller] peer, _ in + + let presentConfirmation: (String, @escaping () -> Void) -> Void = { [weak self] peerName, completion in guard let strongSelf = self else { return } - let peerName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) + let attributedTitle: NSAttributedString? let attributedText: NSAttributedString @@ -4180,12 +4181,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } if let botAdminRights { - let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteWithRightsText(botName, peerName, stringForAdminRights(strings: strongSelf.presentationData.strings, adminRights: botAdminRights)) - let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - for range in stringWithRanges.ranges.prefix(2) { - formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + if botAdminRights.rights.isEmpty { + let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteAdminText(botName, peerName) + let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + for range in stringWithRanges.ranges.prefix(2) { + formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + } + attributedText = formattedString + } else { + let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteWithRightsText(botName, peerName, stringForAdminRights(strings: strongSelf.presentationData.strings, adminRights: botAdminRights)) + let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + for range in stringWithRanges.ranges.prefix(2) { + formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + } + attributedText = formattedString } - attributedText = formattedString } else { let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteText(botName, peerName) let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) @@ -4196,28 +4206,46 @@ 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: { [weak controller] in - let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start() - controller?.dismiss() + 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() })]) strongSelf.present(controller, in: .window(.root)) } + + controller.peerSelected = { [weak self, weak controller] peer, _ in + guard let strongSelf = self else { + return + } + + let peerName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) + presentConfirmation(peerName, { + let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start() + controller?.dismiss() + }) + } createNewGroupImpl = { [weak controller] in switch peerType { case .user: break case let .group(group): - let createGroupController = createGroupControllerImpl(context: context, peerIds: peerId.flatMap { [$0] } ?? [], mode: .requestPeer(group), completion: { peerId, dismiss in + let createGroupController = createGroupControllerImpl(context: context, peerIds: peerId.flatMap { [$0] } ?? [], mode: .requestPeer(group), willComplete: { peerName, complete in + presentConfirmation(peerName, { + complete() + }) + }, completion: { peerId, dismiss in let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peerId).start() - dismiss() }) createGroupController.navigationPresentation = .modal controller?.replace(with: createGroupController) case let .channel(channel): - let createChannelController = createChannelController(context: context, mode: .requestPeer(channel), completion: { peerId, dismiss in + let createChannelController = createChannelController(context: context, mode: .requestPeer(channel), willComplete: { peerName, complete in + presentConfirmation(peerName, { + complete() + }) + }, completion: { peerId, dismiss in let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peerId).start() - dismiss() }) createChannelController.navigationPresentation = .modal @@ -11082,10 +11110,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var temporaryChatPresentationInterfaceState = f(self.presentationInterfaceState) if self.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup || self.presentationInterfaceState.keyboardButtonsMessage?.id != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.id { - if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, let _ = keyboardButtonsMessage.visibleButtonKeyboardMarkup { + if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, let keyboardMarkup = keyboardButtonsMessage.visibleButtonKeyboardMarkup { if self.presentationInterfaceState.interfaceState.editMessage == nil && self.presentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.closedButtonKeyboardMessageId && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId && temporaryChatPresentationInterfaceState.botStartPayload == nil { temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ _ in - return .inputButtons + return .inputButtons(persistent: keyboardMarkup.flags.contains(.persistent)) }) } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 311ad495a3..1b5ab39434 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2567,7 +2567,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { case .none: break case .inputButtons: - if let peer = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil { + if let peer = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil, self.chatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup?.flags.contains(.persistent) == true { } else { self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in return (.none, state.keyboardButtonsMessage?.id ?? state.interfaceState.messageActionsState.closedButtonKeyboardMessageId) @@ -3041,7 +3041,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - if let peer = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil, case .inputButtons = self.chatPresentationInterfaceState.inputMode { + if let peer = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil, case .inputButtons = self.chatPresentationInterfaceState.inputMode, self.chatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup?.flags.contains(.persistent) == true { enableGesture = false } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceInputNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceInputNodes.swift index ec08704f02..9df65c6b71 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceInputNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceInputNodes.swift @@ -15,45 +15,15 @@ func inputNodeForChatPresentationIntefaceState(_ chatPresentationInterfaceState: } switch chatPresentationInterfaceState.inputMode { case .media: - if "".isEmpty { - if let currentNode = currentNode as? ChatEntityKeyboardInputNode { - return currentNode - } else if let inputMediaNode = inputMediaNode { - return inputMediaNode - } else if let inputMediaNode = makeMediaInputNode() { - inputMediaNode.interfaceInteraction = interfaceInteraction - return inputMediaNode - } else { - return nil - } + if let currentNode = currentNode as? ChatEntityKeyboardInputNode { + return currentNode + } else if let inputMediaNode = inputMediaNode { + return inputMediaNode + } else if let inputMediaNode = makeMediaInputNode() { + inputMediaNode.interfaceInteraction = interfaceInteraction + return inputMediaNode } else { - if let currentNode = currentNode as? ChatMediaInputNode { - return currentNode - } else if let inputMediaNode = inputMediaNode { - return inputMediaNode - } else { - var peerId: PeerId? - if case let .peer(id) = chatPresentationInterfaceState.chatLocation { - peerId = id - } - let inputNode = ChatMediaInputNode(context: context, peerId: peerId, chatLocation: chatPresentationInterfaceState.chatLocation, controllerInteraction: controllerInteraction, chatWallpaper: chatPresentationInterfaceState.chatWallpaper, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, gifPaneIsActiveUpdated: { [weak interfaceInteraction] value in - if let interfaceInteraction = interfaceInteraction { - interfaceInteraction.updateInputModeAndDismissedButtonKeyboardMessageId { state in - if case let .media(_, expanded, focused) = state.inputMode { - if value { - return (.media(mode: .gif, expanded: expanded, focused: focused), nil) - } else { - return (.media(mode: .other, expanded: expanded, focused: focused), nil) - } - } else { - return (state.inputMode, nil) - } - } - } - }) - inputNode.interfaceInteraction = interfaceInteraction - return inputNode - } + return nil } case .inputButtons: if chatPresentationInterfaceState.forceInputCommandsHidden { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 29a72fe0b8..43f586c2d1 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2970,9 +2970,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.dismissedEmojiSuggestionPosition = nil if let presentationInterfaceState = self.presentationInterfaceState { - if let peer = presentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil, presentationInterfaceState.keyboardButtonsMessage != nil { + if let peer = presentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil, let keyboardButtonsMessage = presentationInterfaceState.keyboardButtonsMessage, let keyboardMarkup = keyboardButtonsMessage.visibleButtonKeyboardMarkup, keyboardMarkup.flags.contains(.persistent) { self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId { _ in - return (.inputButtons, nil) + return (.inputButtons(persistent: true), nil) } } else { switch presentationInterfaceState.inputMode { @@ -3481,7 +3481,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } case .bot: self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in - return (.inputButtons, nil) + return (.inputButtons(persistent: state.keyboardButtonsMessage?.visibleButtonKeyboardMarkup?.flags.contains(.persistent) ?? false), nil) }) } case .commands: diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index 845c541adb..b802d9ab6d 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -211,8 +211,7 @@ public enum CreateChannelMode { case requestPeer(ReplyMarkupButtonRequestPeerType.Channel) } - -public func createChannelController(context: AccountContext, mode: CreateChannelMode = .generic, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController { +public func createChannelController(context: AccountContext, mode: CreateChannelMode = .generic, willComplete: @escaping (String, @escaping () -> Void) -> Void = { _, complete in complete() }, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController { let initialState = CreateChannelState(creating: false, editingName: ItemListAvatarAndNameInfoItemName.title(title: "", type: .channel), editingDescriptionText: "", avatar: nil) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -256,53 +255,55 @@ public func createChannelController(context: AccountContext, mode: CreateChannel } if !creating && !title.isEmpty { - updateState { current in - var current = current - current.creating = true - return current - } - - endEditingImpl?() - actionsDisposable.add((context.engine.peers.createChannel(title: title, description: description.isEmpty ? nil : description) - |> deliverOnMainQueue - |> afterDisposed { - Queue.mainQueue().async { - updateState { current in - var current = current - current.creating = false - return current - } - } - }).start(next: { peerId in - let updatingAvatar = stateValue.with { - return $0.avatar - } - if let _ = updatingAvatar { - let _ = context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) - }).start() + willComplete(title, { + updateState { current in + var current = current + current.creating = true + return current } - let controller = channelVisibilityController(context: context, peerId: peerId, mode: .initialSetup, upgradedToSupergroup: { _, f in f() }) - replaceControllerImpl?(controller) - }, error: { error in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text: String? - switch error { - case .generic, .tooMuchLocationBasedGroups: - text = presentationData.strings.Login_UnknownError - case .tooMuchJoined: - pushControllerImpl?(oldChannelsController(context: context, intent: .create)) - return - case .restricted: - text = presentationData.strings.Common_ActionNotAllowedError - default: - text = nil - } - if let text = text { - presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) - } - })) + endEditingImpl?() + actionsDisposable.add((context.engine.peers.createChannel(title: title, description: description.isEmpty ? nil : description) + |> deliverOnMainQueue + |> afterDisposed { + Queue.mainQueue().async { + updateState { current in + var current = current + current.creating = false + return current + } + } + }).start(next: { peerId in + let updatingAvatar = stateValue.with { + return $0.avatar + } + if let _ = updatingAvatar { + let _ = context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) + }).start() + } + + let controller = channelVisibilityController(context: context, peerId: peerId, mode: .initialSetup, upgradedToSupergroup: { _, f in f() }) + replaceControllerImpl?(controller) + }, error: { error in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let text: String? + switch error { + case .generic, .tooMuchLocationBasedGroups: + text = presentationData.strings.Login_UnknownError + case .tooMuchJoined: + pushControllerImpl?(oldChannelsController(context: context, intent: .create)) + return + case .restricted: + text = presentationData.strings.Common_ActionNotAllowedError + default: + text = nil + } + if let text = text { + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + } + })) + }) } }, changeProfilePhoto: { endEditingImpl?() @@ -506,24 +507,23 @@ public func createChannelController(context: AccountContext, mode: CreateChannel }) let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) - |> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in - - let rightNavigationButton: ItemListNavigationButton - if state.creating { - rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) - } else { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: !state.editingName.composedTitle.isEmpty, action: { - arguments.done() - }) - } - - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelIntro_CreateChannel), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: CreateChannelEntries(presentationData: presentationData, state: state), style: .blocks, focusItemTag: CreateChannelEntryTag.info) - - return (controllerState, (listState, arguments)) - } |> afterDisposed { - actionsDisposable.dispose() + |> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in + let rightNavigationButton: ItemListNavigationButton + if state.creating { + rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) + } else { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: !state.editingName.composedTitle.isEmpty, action: { + arguments.done() + }) } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelIntro_CreateChannel), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: CreateChannelEntries(presentationData: presentationData, state: state), style: .blocks, focusItemTag: CreateChannelEntryTag.info) + + return (controllerState, (listState, arguments)) + } |> afterDisposed { + actionsDisposable.dispose() + } let controller = ItemListController(context: context, state: signal) replaceControllerImpl = { [weak controller] value in diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index b31eb9f350..0cde5a4f6f 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -530,7 +530,7 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat return entries } -public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId], initialTitle: String? = nil, mode: CreateGroupMode = .generic, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController { +public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId], initialTitle: String? = nil, mode: CreateGroupMode = .generic, willComplete: @escaping (String, @escaping () -> Void) -> Void = { _, complete in complete() }, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController { var location: PeerGeoLocation? if case let .locatedGroup(latitude, longitude, address) = mode { location = PeerGeoLocation(latitude: latitude, longitude: longitude, address: address ?? "") @@ -591,52 +591,26 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] } if !creating && !title.isEmpty { - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalAutoremoveTimeout()) - |> deliverOnMainQueue).start(next: { maybeGlobalAutoremoveTimeout in - updateState { current in - var current = current - current.creating = true - return current - } - endEditingImpl?() - - let globalAutoremoveTimeout: Int32 = maybeGlobalAutoremoveTimeout ?? 0 - let autoremoveTimeout = stateValue.with({ $0 }).autoremoveTimeout ?? globalAutoremoveTimeout - let ttlPeriod: Int32? = autoremoveTimeout == 0 ? nil : autoremoveTimeout - - var createSignal: Signal - switch mode { - case .generic: - createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod) - case .supergroup: - createSignal = context.engine.peers.createSupergroup(title: title, description: nil) - |> map(Optional.init) - |> mapError { error -> CreateGroupError in - switch error { - case .generic: - return .generic - case .restricted: - return .restricted - case .tooMuchJoined: - return .tooMuchJoined - case .tooMuchLocationBasedGroups: - return .tooMuchLocationBasedGroups - case let .serverProvided(error): - return .serverProvided(error) - } - } - case .locatedGroup: - guard let location = location else { - return + willComplete(title, { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalAutoremoveTimeout()) + |> deliverOnMainQueue).start(next: { maybeGlobalAutoremoveTimeout in + updateState { current in + var current = current + current.creating = true + return current } + endEditingImpl?() - createSignal = addressPromise.get() - |> castError(CreateGroupError.self) - |> mapToSignal { address -> Signal in - guard let address = address else { - return .complete() - } - return context.engine.peers.createSupergroup(title: title, description: nil, location: (location.latitude, location.longitude, address)) + let globalAutoremoveTimeout: Int32 = maybeGlobalAutoremoveTimeout ?? 0 + let autoremoveTimeout = stateValue.with({ $0 }).autoremoveTimeout ?? globalAutoremoveTimeout + let ttlPeriod: Int32? = autoremoveTimeout == 0 ? nil : autoremoveTimeout + + var createSignal: Signal + switch mode { + case .generic: + createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod) + case .supergroup: + createSignal = context.engine.peers.createSupergroup(title: title, description: nil) |> map(Optional.init) |> mapError { error -> CreateGroupError in switch error { @@ -652,50 +626,18 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] return .serverProvided(error) } } - } - case let .requestPeer(group): - var isForum = false - if let isForumRequested = group.isForum, isForumRequested { - isForum = true - } - - if isForum { - createSignal = context.engine.peers.createSupergroup(title: title, description: nil, isForum: true) - |> map(Optional.init) - |> mapError { error -> CreateGroupError in - switch error { - case .generic: - return .generic - case .restricted: - return .restricted - case .tooMuchJoined: - return .tooMuchJoined - case .tooMuchLocationBasedGroups: - return .tooMuchLocationBasedGroups - case let .serverProvided(error): - return .serverProvided(error) - } + case .locatedGroup: + guard let location = location else { + return } - if let publicLink, !publicLink.isEmpty { - createSignal = createSignal - |> mapToSignal { peerId in - if let peerId = peerId { - return context.engine.peers.updateAddressName(domain: .peer(peerId), name: publicLink) - |> mapError { _ in - return .generic - } - |> map { _ in - return peerId - } - } else { - return .fail(.generic) - } + createSignal = addressPromise.get() + |> castError(CreateGroupError.self) + |> mapToSignal { address -> Signal in + guard let address = address else { + return .complete() } - } - } else { - if let publicLink, !publicLink.isEmpty { - createSignal = context.engine.peers.createSupergroup(title: title, description: nil) + return context.engine.peers.createSupergroup(title: title, description: nil, location: (location.latitude, location.longitude, address)) |> map(Optional.init) |> mapError { error -> CreateGroupError in switch error { @@ -711,6 +653,33 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] return .serverProvided(error) } } + } + case let .requestPeer(group): + var isForum = false + if let isForumRequested = group.isForum, isForumRequested { + isForum = true + } + + let createGroupSignal: (Bool) -> Signal = { isForum in + return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum) + |> map(Optional.init) + |> mapError { error -> CreateGroupError in + switch error { + case .generic: + return .generic + case .restricted: + return .restricted + case .tooMuchJoined: + return .tooMuchJoined + case .tooMuchLocationBasedGroups: + return .tooMuchLocationBasedGroups + case let .serverProvided(error): + return .serverProvided(error) + } + } + } + if let publicLink, !publicLink.isEmpty { + createSignal = createGroupSignal(isForum) |> mapToSignal { peerId in if let peerId = peerId { return context.engine.peers.updateAddressName(domain: .peer(peerId), name: publicLink) @@ -724,82 +693,101 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] return .fail(.generic) } } + } else if isForum || group.userAdminRights != nil { + createSignal = createGroupSignal(isForum) } else { createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: nil) } - } - } - - actionsDisposable.add((createSignal - |> mapToSignal { peerId -> Signal in - guard let peerId = peerId else { - return .single(nil) - } - let updatingAvatar = stateValue.with { - return $0.avatar - } - if let _ = updatingAvatar { - return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) - }) - |> ignoreValues - |> `catch` { _ -> Signal in - return .complete() - } - |> mapToSignal { _ -> Signal in - } - |> then(.single(peerId)) - } else { - return .single(peerId) - } - } - |> deliverOnMainQueue - |> afterDisposed { - Queue.mainQueue().async { - updateState { current in - var current = current - current.creating = false - return current + + if group.userAdminRights?.rights.contains(.canBeAnonymous) == true { + createSignal = createSignal + |> mapToSignal { peerId in + if let peerId = peerId { + return context.engine.peers.updateChannelAdminRights(peerId: peerId, adminId: context.account.peerId, rights: TelegramChatAdminRights(rights: .canBeAnonymous), rank: nil) + |> mapError { _ in + return .generic + } + |> map { _ in + return peerId + } + } else { + return .fail(.generic) + } + } } } - }).start(next: { peerId in - if let peerId = peerId { - if let completion = completion { - completion(peerId, { - dismissImpl?() + + actionsDisposable.add((createSignal + |> mapToSignal { peerId -> Signal in + guard let peerId = peerId else { + return .single(nil) + } + let updatingAvatar = stateValue.with { + return $0.avatar + } + if let _ = updatingAvatar { + return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) }) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + } + |> then(.single(peerId)) } else { - let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: peerId)) - replaceControllerImpl?(controller) + return .single(peerId) } } - }, error: { error in - if case .serverProvided = error { - return - } - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text: String? - switch error { - case .privacy: - text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelMultipleError - case .generic: - text = presentationData.strings.Login_UnknownError - case .restricted: - text = presentationData.strings.Common_ActionNotAllowedError - case .tooMuchJoined: - pushImpl?(oldChannelsController(context: context, intent: .create)) - return - case .tooMuchLocationBasedGroups: - text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch - default: - text = nil - } - - if let text = text { - presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) - } - })) + |> deliverOnMainQueue + |> afterDisposed { + Queue.mainQueue().async { + updateState { current in + var current = current + current.creating = false + return current + } + } + }).start(next: { peerId in + if let peerId = peerId { + if let completion = completion { + completion(peerId, { + dismissImpl?() + }) + } else { + let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: peerId)) + replaceControllerImpl?(controller) + } + } + }, error: { error in + if case .serverProvided = error { + return + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let text: String? + switch error { + case .privacy: + text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelMultipleError + case .generic: + text = presentationData.strings.Login_UnknownError + case .restricted: + text = presentationData.strings.Common_ActionNotAllowedError + case .tooMuchJoined: + pushImpl?(oldChannelsController(context: context, intent: .create)) + return + case .tooMuchLocationBasedGroups: + text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch + default: + text = nil + } + + if let text = text { + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + } + })) + }) }) } }, changeProfilePhoto: { diff --git a/submodules/TranslateUI/Sources/TranslateScreen.swift b/submodules/TranslateUI/Sources/TranslateScreen.swift index b6ad520879..fca8de62e4 100644 --- a/submodules/TranslateUI/Sources/TranslateScreen.swift +++ b/submodules/TranslateUI/Sources/TranslateScreen.swift @@ -99,14 +99,11 @@ private final class TranslateScreenComponent: CombinedComponent { super.init() - self.translationDisposable.set((context.engine.messages.translate(text: text, fromLang: fromLanguage, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in + self.translationDisposable.set((context.engine.messages.translate(text: text, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in guard let strongSelf = self else { return } strongSelf.translatedText = text -// if strongSelf.fromLanguage == nil { -// strongSelf.fromLanguage = result.detectedLanguage -// } strongSelf.updated(transition: .immediate) }, error: { error in @@ -127,14 +124,11 @@ private final class TranslateScreenComponent: CombinedComponent { self.translatedText = nil self.updated(transition: .immediate) - self.translationDisposable.set((self.context.engine.messages.translate(text: text, fromLang: fromLanguage, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in + self.translationDisposable.set((self.context.engine.messages.translate(text: text, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in guard let strongSelf = self else { return } strongSelf.translatedText = text -// if strongSelf.fromLanguage == nil { -// strongSelf.fromLanguage = result.detectedLanguage -// } strongSelf.updated(transition: .immediate) }, error: { error in