diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3d17fee461..bfc89bfeff 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8844,3 +8844,5 @@ Sorry for the inconvenience."; "EmojiInput.TrendingEmoji" = "TRENDING EMOJI"; "Chat.PlaceholderTextNotAllowed" = "Text not allowed"; + +"CallList.DeleteAll" = "Delete All"; diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 23839ef1c3..13f12d8715 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -39,7 +39,7 @@ private final class DeleteAllButtonNode: ASDisplayNode { self.buttonNode.addSubnode(self.titleNode) self.contentNode.contentNode.addSubnode(self.buttonNode) - self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.Notification_Exceptions_DeleteAll, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationBar.accentTextColor) + self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.CallList_DeleteAll, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationBar.accentTextColor) //self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } diff --git a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift index 49c59c2352..da4e6c316b 100644 --- a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift +++ b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift @@ -183,7 +183,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone switch type { case TGAudioSessionTypePlayAndRecord, TGAudioSessionTypePlayAndRecordHeadphones: if legacyContext.sharedContext.currentMediaInputSettings.with({ $0 }).pauseMusicOnRecording { - convertedType = .record(speaker: false) + convertedType = .record(speaker: false, withOthers: false) } else { convertedType = .recordWithOthers } diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index caf0e4674c..264a299943 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -333,7 +333,8 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { UIColor(rgb: 0x5A6EEE), UIColor(rgb: 0x548DFF), UIColor(rgb: 0x54A3FF), - UIColor(rgb: 0x54bdff) + UIColor(rgb: 0x54bdff), + UIColor(rgb: 0x71c8ff) ] i = 0 diff --git a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift index f416a0183e..63055b6364 100644 --- a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift +++ b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift @@ -18,7 +18,7 @@ public enum ManagedAudioSessionType: Equatable { case ambient case play case playWithPossiblePortOverride - case record(speaker: Bool) + case record(speaker: Bool, withOthers: Bool) case voiceCall case videoCall case recordWithOthers @@ -587,7 +587,14 @@ public final class ManagedAudioSession { index += 1 } - let lastIsRecordWithOthers = self.holders.last?.audioSessionType == .recordWithOthers + var lastIsRecordWithOthers = false + if let lastHolder = self.holders.last { + if case let .record(_, withOthers) = lastHolder.audioSessionType { + lastIsRecordWithOthers = withOthers + } else if case .recordWithOthers = lastHolder.audioSessionType { + lastIsRecordWithOthers = true + } + } if !deactivating { if let activeIndex = activeIndex { var deactivate = false @@ -896,13 +903,13 @@ public final class ManagedAudioSession { if resetToBuiltin { var updatedType = type - if case .record(false) = updatedType, self.isHeadsetPluggedInValue { - updatedType = .record(speaker: true) + if case .record(false, let withOthers) = updatedType, self.isHeadsetPluggedInValue { + updatedType = .record(speaker: true, withOthers: withOthers) } switch updatedType { - case .record(false): + case .record(false, _): try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) - case .voiceCall, .playWithPossiblePortOverride, .record(true): + case .voiceCall, .playWithPossiblePortOverride, .record(true, _): try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none) if let routes = AVAudioSession.sharedInstance().availableInputs { var alreadySet = false diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index d77767be48..d13561386f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -356,11 +356,11 @@ public extension TelegramEngine { return EngineMessageReactionListContext(account: self.account, message: message, reaction: reaction) } - public func translate(text: String, toLang: String) -> Signal { + 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 { + public func translateMessages(messageIds: [EngineMessage.Id], toLang: String) -> Signal { return _internal_translateMessages(account: self.account, messageIds: messageIds, toLang: toLang) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index 5df54f9d42..f1ffae6953 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -4,19 +4,36 @@ import SwiftSignalKit import TelegramApi import MtProtoKit -func _internal_translate(network: Network, text: String, toLang: String) -> Signal { +public enum TranslationError { + case generic + case invalidMessageId + case textIsEmpty + case textTooLong + case invalidLanguage + case limitExceeded +} + +func _internal_translate(network: Network, text: String, toLang: String) -> Signal { var flags: Int32 = 0 flags |= (1 << 1) return network.request(Api.functions.messages.translateText(flags: flags, peer: nil, id: nil, text: [.textWithEntities(text: text, entities: [])], toLang: toLang)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal in - guard let result = result else { - return .complete() + |> mapError { error -> TranslationError in + if error.errorDescription.hasPrefix("FLOOD_WAIT") { + return .limitExceeded + } else if error.errorDescription == "MSG_ID_INVALID" { + return .invalidMessageId + } else if error.errorDescription == "INPUT_TEXT_EMPTY" { + return .textIsEmpty + } else if error.errorDescription == "INPUT_TEXT_TOO_LONG" { + return .textTooLong + } else if error.errorDescription == "TO_LANG_INVALID" { + return .invalidLanguage + } else { + return .generic } + } + |> mapToSignal { result -> Signal in switch result { case let .translateResult(results): if case let .textWithEntities(text, _) = results.first { @@ -28,14 +45,15 @@ func _internal_translate(network: Network, text: String, toLang: String) -> Sign } } -func _internal_translateMessages(account: Account, 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 account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } - |> mapToSignal { inputPeer -> Signal in + |> castError(TranslationError.self) + |> mapToSignal { inputPeer -> Signal in guard let inputPeer = inputPeer else { return .never() } @@ -45,12 +63,23 @@ func _internal_translateMessages(account: Account, messageIds: [EngineMessage.Id let id: [Int32] = messageIds.map { $0.id } 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) + |> mapError { error -> TranslationError in + if error.errorDescription.hasPrefix("FLOOD_WAIT") { + return .limitExceeded + } else if error.errorDescription == "MSG_ID_INVALID" { + return .invalidMessageId + } else if error.errorDescription == "INPUT_TEXT_EMPTY" { + return .textIsEmpty + } else if error.errorDescription == "INPUT_TEXT_TOO_LONG" { + return .textTooLong + } else if error.errorDescription == "TO_LANG_INVALID" { + return .invalidLanguage + } else { + return .generic + } } - |> mapToSignal { result -> Signal in - guard let result = result, case let .translateResult(results) = result else { + |> mapToSignal { result -> Signal in + guard case let .translateResult(results) = result else { return .complete() } return account.postbox.transaction { transaction in @@ -71,6 +100,7 @@ func _internal_translateMessages(account: Account, messageIds: [EngineMessage.Id index += 1 } } + |> castError(TranslationError.self) } } } diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 37b3a9b719..b4e31fb1fd 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -34,6 +34,25 @@ enum AvatarBackground: Equatable { } } + var isLight: Bool { + switch self { + case let .gradient(colors): + if colors.count == 1 { + return UIColor(rgb: colors.first!).lightness > 0.99 + } else if colors.count == 2 { + return UIColor(rgb: colors.first!).lightness > 0.99 || UIColor(rgb: colors.last!).lightness > 0.99 + } else { + var lightCount = 0 + for color in colors { + if UIColor(rgb: color).lightness > 0.99 { + lightCount += 1 + } + } + return lightCount >= 2 + } + } + } + func generateImage(size: CGSize) -> UIImage { switch self { case let .gradient(colors): @@ -313,31 +332,28 @@ final class AvatarEditorScreenComponent: Component { ) } } - - let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) - |> map { peer -> Bool in - guard case let .user(user) = peer else { - return false - } - return user.isPremium - } - |> distinctUntilChanged - + let resultSignal = signal |> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in return combineLatest( - context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), - context.engine.stickers.availableReactions(), - hasPremium + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1), + combineLatest(keywords.map { context.engine.stickers.searchStickers(query: $0.emoticons) + |> map { items -> [FoundStickerItem] in + return items.items + } + }) ) - |> take(1) - |> map { view, availableReactions, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in - var result: [(String, TelegramMediaFile?, String)] = [] + |> map { view, stickers -> [EmojiPagerContentComponent.ItemGroup] in + let hasPremium = true + var emoji: [(String, TelegramMediaFile?, String)] = [] + + var existingEmoticons = Set() var allEmoticons: [String: String] = [:] for keyword in keywords { for emoticon in keyword.emoticons { allEmoticons[emoticon] = keyword.keyword + existingEmoticons.insert(emoticon) } } @@ -350,9 +366,9 @@ final class AvatarEditorScreenComponent: Component { case let .CustomEmoji(_, _, alt, _): if !item.file.isPremiumEmoji || hasPremium { if !alt.isEmpty, let keyword = allEmoticons[alt] { - result.append((alt, item.file, keyword)) + emoji.append((alt, item.file, keyword)) } else if alt == query { - result.append((alt, item.file, alt)) + emoji.append((alt, item.file, alt)) } } default: @@ -361,10 +377,10 @@ final class AvatarEditorScreenComponent: Component { } } - var items: [EmojiPagerContentComponent.Item] = [] + var emojiItems: [EmojiPagerContentComponent.Item] = [] var existingIds = Set() - for item in result { + for item in emoji { if let itemFile = item.1 { if existingIds.contains(itemFile.fileId) { continue @@ -378,25 +394,71 @@ final class AvatarEditorScreenComponent: Component { icon: .none, tintMode: animationData.isTemplate ? .primary : .none ) - items.append(item) + emojiItems.append(item) } } - return [EmojiPagerContentComponent.ItemGroup( - supergroupId: "search", - groupId: "search", - title: nil, - subtitle: nil, - actionButtonTitle: nil, - isFeatured: false, - isPremiumLocked: false, - isEmbedded: false, - hasClear: false, - collapsedLineCount: nil, - displayPremiumBadges: false, - headerItem: nil, - items: items - )] + var stickerItems: [EmojiPagerContentComponent.Item] = [] + for stickerResult in stickers { + for sticker in stickerResult { + if existingIds.contains(sticker.file.fileId) { + continue + } + + existingIds.insert(sticker.file.fileId) + let animationData = EntityKeyboardAnimationData(file: sticker.file) + let item = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: sticker.file, + subgroupId: nil, + icon: .none, + tintMode: .none + ) + stickerItems.append(item) + } + } + + var result: [EmojiPagerContentComponent.ItemGroup] = [] + if !emojiItems.isEmpty { + result.append( + EmojiPagerContentComponent.ItemGroup( + supergroupId: "search", + groupId: "emoji", + title: "Emoji", + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: nil, + displayPremiumBadges: false, + headerItem: nil, + items: emojiItems + ) + ) + } + if !stickerItems.isEmpty { + result.append( + EmojiPagerContentComponent.ItemGroup( + supergroupId: "search", + groupId: "stickers", + title: "Stickers", + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: nil, + displayPremiumBadges: false, + headerItem: nil, + items: stickerItems + ) + ) + } + return result } } diff --git a/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift b/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift index 31124eb2be..0f5126ed12 100644 --- a/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift +++ b/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift @@ -395,10 +395,10 @@ final class ManagedAudioRecorderContext { } let _ = self.audioUnit.swap(audioUnit) - + if self.audioSessionDisposable == nil { let queue = self.queue - self.audioSessionDisposable = self.mediaManager.audioSession.push(audioSessionType: .record(speaker: self.beginWithTone), activate: { [weak self] state in + self.audioSessionDisposable = self.mediaManager.audioSession.push(audioSessionType: .record(speaker: self.beginWithTone, withOthers: false), activate: { [weak self] state in queue.async { if let strongSelf = self, !strongSelf.paused { strongSelf.hasAudioSession = true diff --git a/submodules/TelegramUI/Sources/MediaManager.swift b/submodules/TelegramUI/Sources/MediaManager.swift index d9068cfc74..b5a2564bbf 100644 --- a/submodules/TelegramUI/Sources/MediaManager.swift +++ b/submodules/TelegramUI/Sources/MediaManager.swift @@ -44,8 +44,6 @@ private struct GlobalControlOptions: OptionSet { static let seek = GlobalControlOptions(rawValue: 1 << 5) } -public var test: Double? - public final class MediaManagerImpl: NSObject, MediaManager { public static var globalAudioSession: ManagedAudioSession { return sharedAudioSession diff --git a/submodules/TranslateUI/Sources/ChatTranslation.swift b/submodules/TranslateUI/Sources/ChatTranslation.swift index 03d491bbdd..1a869e8512 100644 --- a/submodules/TranslateUI/Sources/ChatTranslation.swift +++ b/submodules/TranslateUI/Sources/ChatTranslation.swift @@ -144,6 +144,9 @@ public func translateMessageIds(context: AccountContext, messageIds: [EngineMess } } return context.engine.messages.translateMessages(messageIds: messageIdsToTranslate, toLang: toLang) + |> `catch` { _ -> Signal in + return .complete() + } } |> switchToLatest }