From 4657ac752dcaffe82173722dd87f369859e9d61e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 27 Sep 2025 01:27:56 +0400 Subject: [PATCH] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 22 +- .../Sources/PresentationCallManager.swift | 1 + .../ReactionContextBackgroundNode.swift | 2 +- .../Sources/ReactionContextNode.swift | 1 - .../Components/MessageItemComponent.swift | 140 +++++++--- .../Components/MessageListComponent.swift | 14 +- .../Sources/PresentationGroupCall.swift | 11 +- .../VideoChatActionButtonComponent.swift | 27 +- .../Sources/VideoChatNotifications.swift | 23 ++ .../Sources/VideoChatScreen.swift | 245 +++++++++++++----- .../Sources/VideoChatScreenMoreMenu.swift | 23 ++ .../State/AccountStateManagementUtils.swift | 7 +- .../TelegramEngine/Calls/GroupCalls.swift | 23 +- .../Sources/ServiceMessageStrings.swift | 8 +- .../Sources/ButtonComponent.swift | 3 +- .../MessageInputActionButtonComponent.swift | 7 +- .../Sources/MessageInputPanelComponent.swift | 12 +- .../Sources/PeerInfoScreen.swift | 2 +- .../Sources/ProfileLevelInfoScreen.swift | 3 +- .../MessagesDisable.imageset/Contents.json | 12 + .../disablemessages_24.pdf | Bin 0 -> 5056 bytes .../MessagesEnable.imageset/Contents.json | 12 + .../enablemessages_24.pdf | Bin 0 -> 4758 bytes .../Contents.json | 12 + .../toastmessagesdisabled_30.pdf | Bin 0 -> 4663 bytes .../Contents.json | 12 + .../toastmessagesenabled_30.pdf | Bin 0 -> 4389 bytes 27 files changed, 462 insertions(+), 160 deletions(-) create mode 100644 submodules/TelegramCallsUI/Sources/VideoChatNotifications.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/disablemessages_24.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/enablemessages_24.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/toastmessagesdisabled_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/toastmessagesenabled_30.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index dc3ddd766b..31dcde8fef 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15098,11 +15098,12 @@ Error: %8$@"; "PeerInfo.NotesInfo" = "only visible to you"; "PeerInfo.AddNotesPlaceholder" = "Add Notes"; "PeerInfo.AddNotesInfo" = "Notes are only visible to you."; - "PeerInfo.NoteActionCopy" = "Copy Note"; "PeerInfo.NoteActionEdit" = "Edit Note"; "PeerInfo.ToastNoteCopied" = "Note copied to clipboard."; +"PeerInfo.ViewDiscussion" = "View Discussion"; + "MESSAGE_SUGGEST_BIRTHDAY" = "%@ suggested you your birthday"; "Gift.UnavailableAction.Title" = "Action Locked"; @@ -15111,3 +15112,22 @@ Error: %8$@"; "Gift.UnavailableAction.OpenFragment_URL" = "https://fragment.com"; "Gift.Unique.Telegram" = "Telegram"; + +"VoiceChat.ContextEnableMessages" = "Enable Messages"; +"VoiceChat.ContextDisableMessages" = "Disable Messages"; + +"VoiceChat.ToastMessagesEnabled" = "Messages enabled"; +"VoiceChat.ToastMessagesDisabled" = "Messages disabled"; + +"VoiceChat.Rotate" = "rotate"; +"VoiceChat.Message" = "message"; +"VoiceChat.ShareButton" = "share"; +"VoiceChat.MessagePlaceholder" = "Message"; +"VoiceChat.SubtitleParticipants_1" = " participant"; +"VoiceChat.SubtitleParticipants_any" = " participants"; +"VoiceChat.SubtitleInvited_1" = " invited"; +"VoiceChat.SubtitleInvited_any" = " invited"; + +"ProfileLevelInfo.NegativeRating" = "Negative rating"; + +"Notification.StarsGift.Assigned" = "You started displaying %@ on your Telegram profile page."; diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 45c8775d18..5af4fe01a9 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -505,6 +505,7 @@ public protocol PresentationGroupCall: AnyObject { func disableScreencast() func switchVideoCamera() func updateDefaultParticipantsAreMuted(isMuted: Bool) + func updateMessagesEnabled(isEnabled: Bool) func setVolume(peerId: EnginePeer.Id, volume: Int32, sync: Bool) func setRequestedVideoList(items: [PresentationGroupCallRequestedVideo]) func setSuspendVideoChannelRequests(_ value: Bool) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift index bec620d3b2..e2ec36e6ab 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift @@ -237,7 +237,7 @@ final class ReactionContextBackgroundNode: ASDisplayNode { var glassBackgroundFrame = contentBounds.insetBy(dx: 10.0, dy: 10.0) glassBackgroundFrame.size.height -= 8.0 transition.updateFrame(view: glassBackgroundView, frame: glassBackgroundFrame, beginWithCurrentState: true) - glassBackgroundView.update(size: glassBackgroundFrame.size, cornerRadius: 23.0, isDark: true, tintColor: .init(kind: .panel, color: UIColor(rgb: 0x4d4f5c, alpha: 0.6)), transition: ComponentTransition(transition)) + glassBackgroundView.update(size: glassBackgroundFrame.size, cornerRadius: 23.0, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72)), transition: ComponentTransition(transition)) transition.updateFrame(view: self.backgroundTintView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentBounds.width, height: contentBounds.height)).insetBy(dx: -10.0, dy: -10.0)) } else { diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index fc44b58dfe..96d968d7cf 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -26,7 +26,6 @@ import GZip import BalancedTextComponent import Markdown import PremiumStarComponent -import GlassBackgroundComponent public final class ReactionItem { public struct Reaction: Equatable { diff --git a/submodules/TelegramCallsUI/Sources/Components/MessageItemComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MessageItemComponent.swift index b2a54f4493..81a88e294c 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MessageItemComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MessageItemComponent.swift @@ -13,10 +13,20 @@ import AccountContext import TextFormat import TelegramPresentationData import ReactionSelectionNode +import BundleIconComponent +import Markdown + +private let glassColor = UIColor(rgb: 0x25272e, alpha: 0.72) final class MessageItemComponent: Component { + public enum Icon: Equatable { + case peer(EnginePeer) + case icon(String) + } + private let context: AccountContext - private let peer: EnginePeer + private let icon: Icon + private let isNotification: Bool private let text: String private let entities: [MessageTextEntity] private let availableReactions: [ReactionItem]? @@ -24,14 +34,16 @@ final class MessageItemComponent: Component { init( context: AccountContext, - peer: EnginePeer, + icon: Icon, + isNotification: Bool, text: String, entities: [MessageTextEntity], availableReactions: [ReactionItem]?, avatarTapped: @escaping () -> Void = {} ) { self.context = context - self.peer = peer + self.icon = icon + self.isNotification = isNotification self.text = text self.entities = entities self.availableReactions = availableReactions @@ -42,7 +54,7 @@ final class MessageItemComponent: Component { if lhs.context !== rhs.context { return false } - if lhs.peer != rhs.peer { + if lhs.icon != rhs.icon { return false } if lhs.text != rhs.text { @@ -61,6 +73,7 @@ final class MessageItemComponent: Component { private let container: UIView private let background: GlassBackgroundView private let avatarNode: AvatarNode + private let icon: ComponentView private let text: ComponentView weak var standaloneReactionAnimation: StandaloneReactionAnimation? @@ -74,6 +87,7 @@ final class MessageItemComponent: Component { self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 12.0)) + self.icon = ComponentView() self.text = ComponentView() super.init(frame: frame) @@ -107,8 +121,8 @@ final class MessageItemComponent: Component { transition.setPosition(view: textSnapshotView, position: CGPoint(x: textSnapshotView.center.x + 71.0, y: textSnapshotView.center.y)) let initialSize = self.background.frame.size - self.background.update(size: globalFrame.size, cornerRadius: cornerRadius, isDark: true, tintColor: .init(kind: .panel, color: UIColor(rgb: 0x4d4f5c, alpha: 0.6)), transition: .immediate) - self.background.update(size: initialSize, cornerRadius: 18.0, isDark: true, tintColor: .init(kind: .panel, color: UIColor(rgb: 0x4d4f5c, alpha: 0.6)), transition: transition) + self.background.update(size: globalFrame.size, cornerRadius: cornerRadius, isDark: true, tintColor: .init(kind: .custom, color: glassColor), transition: .immediate) + self.background.update(size: initialSize, cornerRadius: 18.0, isDark: true, tintColor: .init(kind: .custom, color: glassColor), transition: transition) let deltaX = (globalFrame.width - self.container.frame.width) / 2.0 let deltaY = (globalFrame.height - self.container.frame.height) / 2.0 @@ -147,45 +161,51 @@ final class MessageItemComponent: Component { let textColor: UIColor = .white let linkColor: UIColor = UIColor(rgb: 0x59b6fa) - let minimalHeight: CGFloat = 36.0 + let minimalHeight: CGFloat = component.isNotification ? 50.0 : 36.0 let cornerRadius = minimalHeight * 0.5 - let avatarInset: CGFloat = 4.0 - let avatarSize = CGSize(width: minimalHeight - avatarInset * 2.0, height: minimalHeight - avatarInset * 2.0) + let avatarInset: CGFloat = component.isNotification ? 10.0 : 4.0 + let avatarSize = CGSize(width: component.isNotification ? 30.0 : 28.0, height: component.isNotification ? 30.0 : 28.0) let avatarSpacing: CGFloat = 10.0 + let iconSpacing: CGFloat = 10.0 let rightInset: CGFloat = 13.0 let avatarFrame = CGRect(origin: CGPoint(x: avatarInset, y: avatarInset), size: avatarSize) - if component.peer.smallProfileImage != nil { - self.avatarNode.setPeerV2( - context: component.context, - theme: theme, - peer: component.peer, - authorOfMessage: nil, - overrideImage: nil, - emptyColor: nil, - clipStyle: .round, - synchronousLoad: true, - displayDimensions: avatarSize - ) - } else { - self.avatarNode.setPeer( - context: component.context, - theme: theme, - peer: component.peer, - clipStyle: .round, - synchronousLoad: true, - displayDimensions: avatarSize - ) + if case let .peer(peer) = component.icon { + if peer.smallProfileImage != nil { + self.avatarNode.setPeerV2( + context: component.context, + theme: theme, + peer: peer, + authorOfMessage: nil, + overrideImage: nil, + emptyColor: nil, + clipStyle: .round, + synchronousLoad: true, + displayDimensions: avatarSize + ) + } else { + self.avatarNode.setPeer( + context: component.context, + theme: theme, + peer: peer, + clipStyle: .round, + synchronousLoad: true, + displayDimensions: avatarSize + ) + } } if self.avatarNode.bounds.isEmpty { self.avatarNode.frame = avatarFrame } else { transition.setFrame(view: self.avatarNode.view, frame: avatarFrame) } - - var peerName = component.peer.compactDisplayTitle - if peerName.count > 40 { - peerName = "\(peerName.prefix(40))…" + + var peerName = "" + if !component.isNotification, case let .peer(peer) = component.icon { + peerName = peer.compactDisplayTitle + if peerName.count > 40 { + peerName = "\(peerName.prefix(40))…" + } } let text = component.text @@ -208,8 +228,32 @@ final class MessageItemComponent: Component { } } - let attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: textFont, boldItalicFont: boldTextFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil, entityFiles: self.entityFiles).mutableCopy() as! NSMutableAttributedString - attributedText.insert(NSAttributedString(string: peerName + " ", font: boldTextFont, textColor: textColor), at: 0) + let attributedText: NSAttributedString + if component.isNotification { + attributedText = parseMarkdownIntoAttributedString( + text, + attributes: MarkdownAttributes( + body: MarkdownAttributeSet(font: textFont, textColor: textColor), + bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), + link: MarkdownAttributeSet(font: textFont, textColor: linkColor), + linkAttribute: { _ in return nil } + ) + ) + } else { + let textWithAppliedEntities = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: textFont, boldItalicFont: boldTextFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil, entityFiles: self.entityFiles).mutableCopy() as! NSMutableAttributedString + if !peerName.isEmpty { + textWithAppliedEntities.insert(NSAttributedString(string: peerName + " ", font: boldTextFont, textColor: textColor), at: 0) + } + attributedText = textWithAppliedEntities + } + + let spacing: CGFloat + switch component.icon { + case .peer: + spacing = avatarSpacing + case .icon: + spacing = iconSpacing + } let textSize = self.text.update( transition: transition, @@ -223,12 +267,28 @@ final class MessageItemComponent: Component { lineSpacing: 0.0 )), environment: {}, - containerSize: CGSize(width: availableSize.width - avatarInset - avatarSize.width - avatarSpacing - rightInset, height: .greatestFiniteMagnitude) + containerSize: CGSize(width: availableSize.width - avatarInset - avatarSize.width - spacing - rightInset, height: .greatestFiniteMagnitude) ) - let size = CGSize(width: avatarInset + avatarSize.width + avatarSpacing + textSize.width + rightInset, height: max(minimalHeight, textSize.height + 15.0)) + let size = CGSize(width: avatarInset + avatarSize.width + spacing + textSize.width + rightInset, height: max(minimalHeight, textSize.height + 15.0)) - let textFrame = CGRect(origin: CGPoint(x: avatarInset + avatarSize.width + avatarSpacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + if case let .icon(iconName) = component.icon { + let iconSize = self.icon.update( + transition: transition, + component: AnyComponent(BundleIconComponent(name: iconName, tintColor: .white)), + environment: {}, + containerSize: CGSize(width: 44.0, height: 44.0) + ) + let iconFrame = CGRect(origin: CGPoint(x: avatarInset, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) + if let iconView = self.icon.view { + if iconView.superview == nil { + self.container.addSubview(iconView) + } + transition.setFrame(view: iconView, frame: iconFrame) + } + } + + let textFrame = CGRect(origin: CGPoint(x: avatarInset + avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) if let textView = self.text.view { if textView.superview == nil { self.container.addSubview(textView) @@ -238,7 +298,7 @@ final class MessageItemComponent: Component { transition.setFrame(view: self.container, frame: CGRect(origin: CGPoint(), size: size)) - self.background.update(size: size, cornerRadius: cornerRadius, isDark: true, tintColor: .init(kind: .panel, color: UIColor(rgb: 0x4d4f5c, alpha: 0.6)), transition: transition) + self.background.update(size: size, cornerRadius: cornerRadius, isDark: true, tintColor: .init(kind: .custom, color: glassColor), transition: transition) transition.setFrame(view: self.background, frame: CGRect(origin: CGPoint(), size: size)) if isFirstTime, let availableReactions = component.availableReactions, let textView = self.text.view { diff --git a/submodules/TelegramCallsUI/Sources/Components/MessageListComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MessageListComponent.swift index 3dcd378652..b51de73eef 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MessageListComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MessageListComponent.swift @@ -10,17 +10,20 @@ import ReactionSelectionNode final class MessageListComponent: Component { struct Item: Equatable { let id: AnyHashable - let peer: EnginePeer + let icon: MessageItemComponent.Icon + let isNotification: Bool let text: String let entities: [MessageTextEntity] } class SendActionTransition { + public let randomId: Int64 public let textSnapshotView: UIView public let globalFrame: CGRect public let cornerRadius: CGFloat - init(textSnapshotView: UIView, globalFrame: CGRect, cornerRadius: CGFloat) { + init(randomId: Int64, textSnapshotView: UIView, globalFrame: CGRect, cornerRadius: CGFloat) { + self.randomId = randomId self.textSnapshotView = textSnapshotView self.globalFrame = globalFrame self.cornerRadius = cornerRadius @@ -141,7 +144,7 @@ final class MessageListComponent: Component { let previousContentHeight = self.scrollView.contentSize.height let wasAtBottom = self.isAtBottom(tolerance: 1.0) - let maxWidth: CGFloat = 300.0 + let maxWidth: CGFloat = 330.0 var measured: [(id: AnyHashable, size: CGSize, item: MessageListComponent.Item, itemTransition: ComponentTransition)] = [] measured.reserveCapacity(component.items.count) @@ -160,7 +163,8 @@ final class MessageListComponent: Component { transition: transition, component: AnyComponent(MessageItemComponent( context: component.context, - peer: item.peer, + icon: item.icon, + isNotification: item.isNotification, text: item.text, entities: item.entities, availableReactions: component.availableReactions @@ -182,7 +186,7 @@ final class MessageListComponent: Component { validKeys.insert(entry.id) if let itemView = self.itemViews[entry.id]?.view { var customAnimation = false - if entry.item.peer.id == component.context.account.peerId, let _ = self.nextSendActionTransition { + if let nextSendActionTransition = self.nextSendActionTransition, entry.id == AnyHashable(nextSendActionTransition.randomId) { customAnimation = true } let itemFrame = CGRect( diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 9f0a665baf..cb2bb907e9 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -1104,7 +1104,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } } - case let .call(isTerminated, _, _, _, _, _, _): + case let .call(isTerminated, _, _, _, _, _, _, _): if isTerminated { self.markAsCanBeRemoved() } @@ -2615,6 +2615,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if (state.isCreator || self.stateValue.adminIds.contains(self.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { self.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted } + self.stateValue.messagesAreEnabled = state.messagesAreEnabled.isEnabled self.stateValue.recordingStartTimestamp = state.recordingStartTimestamp self.stateValue.title = state.title self.stateValue.scheduleTimestamp = state.scheduleTimestamp @@ -3907,6 +3908,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted) } + public func updateMessagesEnabled(isEnabled: Bool) { + self.participantsContext?.updateMessagesEnabled(isEnabled: isEnabled) + } + func video(endpointId: String) -> Signal? { return Signal { [weak self] subscriber in guard let self else { @@ -3997,9 +4002,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { }) } - public func sendMessage(text: String, entities: [MessageTextEntity]) { + public func sendMessage(randomId: Int64? = nil, text: String, entities: [MessageTextEntity]) { if let messagesContext = self.messagesContext { - messagesContext.send(fromId: self.joinAsPeerId, text: text, entities: entities) + messagesContext.send(fromId: self.joinAsPeerId, randomId: randomId, text: text, entities: entities) } } } diff --git a/submodules/TelegramCallsUI/Sources/VideoChatActionButtonComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatActionButtonComponent.swift index d809a49d04..f9865647a8 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatActionButtonComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatActionButtonComponent.swift @@ -36,6 +36,7 @@ final class VideoChatActionButtonComponent: Component { case video case rotateCamera case message + case share case leave } @@ -43,6 +44,7 @@ final class VideoChatActionButtonComponent: Component { case video(isActive: Bool) case rotateCamera case message + case share case leave fileprivate var iconType: IconType { @@ -64,6 +66,8 @@ final class VideoChatActionButtonComponent: Component { return .rotateCamera case .message: return .message + case .share: + return .share case .leave: return .leave } @@ -142,12 +146,11 @@ final class VideoChatActionButtonComponent: Component { let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2) - let genericBackgroundColor = UIColor(rgb: 0x2d2f38, alpha: 0.6) + let genericBackgroundColor = UIColor(rgb: 0x25272e, alpha: 0.72) let blueColor = UIColor(rgb: 0x21477d) let titleText: String let backgroundColor: UIColor - var tintColorKind: GlassBackgroundView.TintColor.Kind = .custom let iconDiameter: CGFloat var isEnabled: Bool = true switch component.content { @@ -183,9 +186,6 @@ final class VideoChatActionButtonComponent: Component { backgroundColor = genericBackgroundColor case .muted: backgroundColor = !isActive ? genericBackgroundColor : blueColor - if isActive { - tintColorKind = .custom - } case .unmuted: backgroundColor = !isActive ? genericBackgroundColor : UIColor(rgb: 0x34C659) case .raiseHand, .scheduled: @@ -193,8 +193,7 @@ final class VideoChatActionButtonComponent: Component { } iconDiameter = 58.0 case .rotateCamera: - //TODO:localize - titleText = "rotate" + titleText = component.strings.VoiceChat_Rotate switch component.microphoneState { case .connecting: backgroundColor = genericBackgroundColor @@ -207,14 +206,16 @@ final class VideoChatActionButtonComponent: Component { } iconDiameter = 44.0 case .message: - //TODO:localize - titleText = "message" + titleText = component.strings.VoiceChat_Message + backgroundColor = genericBackgroundColor + iconDiameter = 56.0 + case .share: + titleText = component.strings.VoiceChat_ShareButton backgroundColor = genericBackgroundColor iconDiameter = 56.0 case .leave: titleText = component.strings.VoiceChat_Leave - backgroundColor = UIColor(rgb: 0x4f1613) - tintColorKind = .custom + backgroundColor = UIColor(rgb: 0x330d0b) iconDiameter = 22.0 } @@ -246,6 +247,8 @@ final class VideoChatActionButtonComponent: Component { self.contentImage = UIImage(bundleImageName: "Call/CallSwitchCameraButton")?.precomposed().withRenderingMode(.alwaysTemplate) case .message: self.contentImage = UIImage(bundleImageName: "Call/CallMessageButton")?.precomposed().withRenderingMode(.alwaysTemplate) + case .share: + self.contentImage = UIImage(bundleImageName: "Call/CallShareButton")?.precomposed().withRenderingMode(.alwaysTemplate) case .leave: self.contentImage = generateImage(CGSize(width: 28.0, height: 28.0), opaque: false, rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) @@ -291,7 +294,7 @@ final class VideoChatActionButtonComponent: Component { } } - self.background.update(size: size, cornerRadius: size.width * 0.5, isDark: true, tintColor: .init(kind: tintColorKind, color: backgroundColor), transition: tintTransition) + self.background.update(size: size, cornerRadius: size.width * 0.5, isDark: true, tintColor: .init(kind: .custom, color: backgroundColor), transition: tintTransition) transition.setFrame(view: self.background, frame: CGRect(origin: CGPoint(), size: size)) let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: size.height + 5.0), size: titleSize) diff --git a/submodules/TelegramCallsUI/Sources/VideoChatNotifications.swift b/submodules/TelegramCallsUI/Sources/VideoChatNotifications.swift new file mode 100644 index 0000000000..8fcf7a61de --- /dev/null +++ b/submodules/TelegramCallsUI/Sources/VideoChatNotifications.swift @@ -0,0 +1,23 @@ +import Foundation +import SwiftSignalKit +import TelegramCore + +enum VideoChatNotificationIcon { + case peer(EnginePeer) + case icon(String) +} + +extension VideoChatScreenComponent.View { + func displayNotification(icon: VideoChatNotificationIcon, text: String, duration: Int32) { + let id = Int64.random(in: 0 ..< .max) + + let expiresOn = Int32(CFAbsoluteTimeGetCurrent()) + duration + self.messageNotifications.append((id: id, icon: icon, text: text, expiresOn: expiresOn)) + self.state?.updated(transition: .spring(duration: 0.4)) + + Queue.mainQueue().after(Double(duration)) { + self.messageNotifications.removeAll(where: { $0.id == id }) + self.state?.updated(transition: .spring(duration: 0.4)) + } + } +} diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift index 81a53afd43..ce0bed3673 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift @@ -158,6 +158,15 @@ extension VideoChatCall { conferenceSource.setCurrentAudioOutput(output) } } + + func setMessagesEnabled(isEnabled: Bool) { + switch self { + case let .group(group): + group.updateMessagesEnabled(isEnabled: isEnabled) + case .conferenceSource: + break + } + } } final class VideoChatScreenComponent: Component { @@ -239,7 +248,7 @@ final class VideoChatScreenComponent: Component { let videoControlButton = ComponentView() let leaveButton = ComponentView() let microphoneButton = ComponentView() - let messageButton = ComponentView() + var messageButton: ComponentView? let speakerButton = ComponentView() @@ -265,6 +274,7 @@ final class VideoChatScreenComponent: Component { weak var disappearingReactionContextNode: ReactionContextNode? weak var willDismissReactionContextNode: ReactionContextNode? + var messageNotifications: [(id: Int64, icon: VideoChatNotificationIcon, text: String, expiresOn: Int32)] = [] let messagesList = ComponentView() var messagesState: GroupCallMessagesContext.State? var messagesStateDisposable: Disposable? @@ -495,6 +505,9 @@ final class VideoChatScreenComponent: Component { if let messageListView = self.messagesList.view, view.isDescendant(of: messageListView) { return false } + if let inputPanelView = self.inputPanel.view, view.isDescendant(of: inputPanelView) { + return false + } } return true } @@ -1020,9 +1033,13 @@ final class VideoChatScreenComponent: Component { } private func onMessagePressed() { - self.inputPanelIsActive = true - self.state?.updated() - self.activateInput() + if let callState = self.callState, callState.messagesAreEnabled { + self.inputPanelIsActive = true + self.state?.updated() + self.activateInput() + } else if let inviteLinks = self.inviteLinks { + self.presentShare(inviteLinks) + } } private func onLeavePressed() { @@ -1371,7 +1388,7 @@ final class VideoChatScreenComponent: Component { }) } - private func sendInput() { + private func sendInput(randomId: Int64? = nil) { guard let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View else { return } @@ -1385,11 +1402,11 @@ final class VideoChatScreenComponent: Component { return } let entities = generateTextEntities(text.string, enabledTypes: [.mention, .hashtag, .allUrl], currentEntities: generateChatInputTextEntities(text)) - call.sendMessage(text: text.string, entities: entities) + call.sendMessage(randomId: randomId, text: text.string, entities: entities) } inputPanelView.clearSendMessageInput(updateState: true) - + // self.currentInputMode = .text // if hasFirstResponder(self) { // self.endEditing(true) @@ -1824,7 +1841,8 @@ final class VideoChatScreenComponent: Component { } else { text = environment.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder)).string } - self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false }) + self.displayNotification(icon: .peer(peer), text: text, duration: 3) + //self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false }) }) self.memberEventsDisposable?.dispose() @@ -1849,8 +1867,10 @@ final class VideoChatScreenComponent: Component { } if displayEvent { - let text = environment.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder)).string - self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: event.peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false }) + let text = environment.strings.VoiceChat_PeerJoinedText("**\(event.peer.displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder))**").string + + self.displayNotification(icon: .peer(event.peer), text: text, duration: 3) + //self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: event.peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false }) } } } else { @@ -2294,10 +2314,7 @@ final class VideoChatScreenComponent: Component { alphaTransition.setAlpha(view: navigationLeftButtonView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0) } - var navigationRightButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - navigationButtonAreaWidth + floor((navigationButtonAreaWidth - navigationRightButtonSize.width) * 0.5) + navigationButtonInset, y: topInset + floor((navigationBarHeight - navigationRightButtonSize.height) * 0.5)), size: navigationRightButtonSize) - if buttonsOnTheSide { - navigationRightButtonFrame.origin.x += 42.0 - } + let navigationRightButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - navigationButtonAreaWidth + floor((navigationButtonAreaWidth - navigationRightButtonSize.width) * 0.5) + navigationButtonInset, y: topInset + floor((navigationBarHeight - navigationRightButtonSize.height) * 0.5)), size: navigationRightButtonSize) if let navigationRightButtonView = self.navigationRightButton.view { if navigationRightButtonView.superview == nil { self.containerView.addSubview(navigationRightButtonView) @@ -2368,16 +2385,15 @@ final class VideoChatScreenComponent: Component { var idleTitleStatusText: [AnimatedTextComponent.Item] = [] if let callState = self.callState { if callState.networkState == .connected, let members = self.members { - //TODO:localize let totalCount = max(1, members.totalCount) idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .number(totalCount, minDigits: 0))) - idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(1), isUnbreakable: false, content: .text(totalCount == 1 ? " participant" : " participants"))) + idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(1), isUnbreakable: false, content: .text(environment.strings.VoiceChat_SubtitleParticipants(Int32(totalCount))))) if let lastTitleEvent = self.lastTitleEvent { idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(6), isUnbreakable: false, content: .text(", \(lastTitleEvent)"))) } else if !self.invitedPeers.isEmpty { idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(3), isUnbreakable: true, content: .text(", "))) idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(4), isUnbreakable: false, content: .number(self.invitedPeers.count, minDigits: 0))) - idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(5), isUnbreakable: false, content: .text(" invited"))) + idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(5), isUnbreakable: false, content: .text(environment.strings.VoiceChat_SubtitleInvited(Int32(self.invitedPeers.count))))) } } else if callState.scheduleTimestamp != nil { idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .text(environment.strings.VoiceChat_Scheduled))) @@ -2557,6 +2573,7 @@ final class VideoChatScreenComponent: Component { let videoButtonContent: VideoChatActionButtonComponent.Content? let videoControlButtonContent: VideoChatActionButtonComponent.Content + let messageButtonContent: VideoChatActionButtonComponent.Content? var buttonAudio: VideoChatActionButtonComponent.Content.Audio = .speaker var buttonIsEnabled = false @@ -2599,12 +2616,27 @@ final class VideoChatScreenComponent: Component { } } - + if let callState = self.callState, !callState.messagesAreEnabled { + if let _ = self.inviteLinks { + messageButtonContent = .share + } else { + messageButtonContent = nil + } + } else { + messageButtonContent = .message + } + let actionButtonDiameter: CGFloat = 56.0 let expandedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter let collapsedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter // 116.0 - let buttonsCount = videoButtonContent == nil ? 4 : 5 + var buttonsCount = 3 + if let _ = videoButtonContent { + buttonsCount += 1 + } + if let _ = messageButtonContent { + buttonsCount += 1 + } let buttonsWidth: CGFloat = actionButtonDiameter * CGFloat(buttonsCount) let remainingButtonsSpace: CGFloat @@ -2688,21 +2720,42 @@ final class VideoChatScreenComponent: Component { var thirdActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize) if buttonsCount == 4 { - if buttonsOnTheSide { - firstActionButtonFrame.origin.x = microphoneButtonFrame.minX - secondActionButtonFrame.origin.x = microphoneButtonFrame.minX - thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX - fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX - - microphoneButtonFrame.origin.y = availableSize.height * 0.5 - landscapeControlsSpacing * 0.5 - actionButtonDiameter - firstActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter - thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing - fourthActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + actionButtonDiameter + landscapeControlsSpacing - } else { - microphoneButtonFrame.origin.x = availableSize.width * 0.5 - actionMicrophoneButtonSpacing * 0.5 - actionButtonDiameter - firstActionButtonFrame.origin.x = microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter - thirdActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing - fourthActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + actionButtonDiameter + actionMicrophoneButtonSpacing + if let _ = messageButtonContent { + if buttonsOnTheSide { + firstActionButtonFrame.origin.x = microphoneButtonFrame.minX + secondActionButtonFrame.origin.x = microphoneButtonFrame.minX + thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX + fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX + + microphoneButtonFrame.origin.y = availableSize.height * 0.5 - landscapeControlsSpacing * 0.5 - actionButtonDiameter + firstActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter + thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + fourthActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + actionButtonDiameter + landscapeControlsSpacing + } else { + microphoneButtonFrame.origin.x = availableSize.width * 0.5 - actionMicrophoneButtonSpacing * 0.5 - actionButtonDiameter + firstActionButtonFrame.origin.x = microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter + thirdActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + fourthActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + actionButtonDiameter + actionMicrophoneButtonSpacing + } + } + if let _ = videoButtonContent { + if buttonsOnTheSide { + firstActionButtonFrame.origin.x = microphoneButtonFrame.minX + secondActionButtonFrame.origin.x = microphoneButtonFrame.minX + thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX + fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX + + microphoneButtonFrame.origin.y = availableSize.height * 0.5 - landscapeControlsSpacing * 0.5 - actionButtonDiameter + firstActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter + thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + fourthActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + actionButtonDiameter + landscapeControlsSpacing + } else { + microphoneButtonFrame.origin.x = availableSize.width * 0.5 + actionMicrophoneButtonSpacing * 0.5 + firstActionButtonFrame.origin.x = microphoneButtonFrame.minX - actionMicrophoneButtonSpacing * 2.0 - actionButtonDiameter * 2.0 + secondActionButtonFrame.origin.x = microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter + //thirdActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + fourthActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + } } } else if buttonsOnTheSide { firstActionButtonFrame.origin.x = microphoneButtonFrame.minX @@ -3127,7 +3180,6 @@ final class VideoChatScreenComponent: Component { transition.setPosition(view: microphoneButtonView, position: microphoneButtonFrame.center) transition.setBounds(view: microphoneButtonView, bounds: CGRect(origin: CGPoint(), size: microphoneButtonFrame.size)) } - let _ = self.speakerButton.update( transition: transition, @@ -3202,8 +3254,12 @@ final class VideoChatScreenComponent: Component { ) if let videoButtonView = videoButton.view { if videoButtonView.superview == nil { - if let speakerButtonView = self.speakerButton.view { - self.containerView.insertSubview(videoButtonView, belowSubview: speakerButtonView) + if let microphoneButtonView = self.microphoneButton.view { + self.containerView.insertSubview(videoButtonView, aboveSubview: microphoneButtonView) + } + if !transition.animation.isImmediate { + transition.animateScale(view: videoButtonView, from: 0.01, to: 1.0) + transition.animateAlpha(view: videoButtonView, from: 0.0, to: 1.0) } } videoButtonTransition.setPosition(view: videoButtonView, position: secondActionButtonFrame.center) @@ -3213,40 +3269,64 @@ final class VideoChatScreenComponent: Component { self.videoButton = nil if let videoButtonView = videoButton.view { let transition = ComponentTransition(animation: .curve(duration: 0.25, curve: .easeInOut)) - transition.animateScale(view: videoButtonView, from: 1.0, to: 0.01) - transition.animateAlpha(view: videoButtonView, from: 1.0, to: 0.0, completion: { _ in + transition.setScale(view: videoButtonView, scale: 0.01) + transition.setAlpha(view: videoButtonView, alpha: 0.0, completion: { _ in videoButtonView.removeFromSuperview() }) } } - let _ = self.messageButton.update( - transition: transition, - component: AnyComponent(PlainButtonComponent( - content: AnyComponent(VideoChatActionButtonComponent( - strings: environment.strings, - content: .message, - microphoneState: actionButtonMicrophoneState, - isCollapsed: areButtonsActuallyCollapsed || buttonsOnTheSide - )), - effectAlignment: .center, - action: { [weak self] in - guard let self else { - return - } - self.onMessagePressed() - }, - animateAlpha: false - )), - environment: {}, - containerSize: CGSize(width: actionButtonDiameter, height: actionButtonDiameter) - ) - if let messageButtonView = self.messageButton.view { - if messageButtonView.superview == nil { - self.containerView.addSubview(messageButtonView) + if let messageButtonContent { + var messageButtonTransition = transition + let messageButton: ComponentView + if let current = self.messageButton { + messageButton = current + } else { + messageButtonTransition = .immediate + messageButton = ComponentView() + self.messageButton = messageButton + } + + let _ = messageButton.update( + transition: messageButtonTransition, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(VideoChatActionButtonComponent( + strings: environment.strings, + content: messageButtonContent, + microphoneState: actionButtonMicrophoneState, + isCollapsed: areButtonsActuallyCollapsed || buttonsOnTheSide + )), + effectAlignment: .center, + action: { [weak self] in + self?.onMessagePressed() + }, + animateAlpha: false + )), + environment: {}, + containerSize: CGSize(width: actionButtonDiameter, height: actionButtonDiameter) + ) + if let messageButtonView = messageButton.view { + if messageButtonView.superview == nil { + if let microphoneButtonView = self.microphoneButton.view { + self.containerView.insertSubview(messageButtonView, aboveSubview: microphoneButtonView) + } + if !transition.animation.isImmediate { + transition.animateScale(view: messageButtonView, from: 0.01, to: 1.0) + transition.animateAlpha(view: messageButtonView, from: 0.0, to: 1.0) + } + } + messageButtonTransition.setPosition(view: messageButtonView, position: thirdActionButtonFrame.center) + messageButtonTransition.setBounds(view: messageButtonView, bounds: CGRect(origin: CGPoint(), size: thirdActionButtonFrame.size)) + } + } else if let messageButton = self.messageButton, messageButton.view?.superview != nil { + self.messageButton = nil + if let messageButtonView = messageButton.view { + let transition = ComponentTransition(animation: .curve(duration: 0.25, curve: .easeInOut)) + transition.setScale(view: messageButtonView, scale: 0.01) + transition.setAlpha(view: messageButtonView, alpha: 0.0, completion: { _ in + messageButtonView.removeFromSuperview() + }) } - transition.setPosition(view: messageButtonView, position: thirdActionButtonFrame.center) - transition.setBounds(view: messageButtonView, bounds: CGRect(origin: CGPoint(), size: thirdActionButtonFrame.size)) } let _ = self.leaveButton.update( @@ -3399,7 +3479,6 @@ final class VideoChatScreenComponent: Component { characterLimit = Int(value) } - //TODO:localize self.inputPanel.parentState = state inputPanelSize = self.inputPanel.update( transition: transition, @@ -3409,7 +3488,7 @@ final class VideoChatScreenComponent: Component { theme: environment.theme, strings: environment.strings, style: .glass, - placeholder: .plain("Message"), + placeholder: .plain(environment.strings.VoiceChat_MessagePlaceholder), sendPaidMessageStars: nil, maxLength: characterLimit, queryTypes: [.mention, .hashtag], @@ -3419,10 +3498,8 @@ final class VideoChatScreenComponent: Component { nextInputMode: { _ in return nextInputMode }, areVoiceMessagesAvailable: false, presentController: { c in - //controller.present(c, in: .window(.root)) }, presentInGlobalOverlay: { c in - //controller.presentInGlobalOverlay(c) }, sendMessageAction: { [weak self] transition in guard let self else { @@ -3431,7 +3508,7 @@ final class VideoChatScreenComponent: Component { if self.inputPanelExternalState.hasText { self.nextSendMessageTransition = transition - self.sendInput() + self.sendInput(randomId: transition?.randomId) } else { self.inputPanelIsActive = false self.deactivateInput() @@ -3783,7 +3860,10 @@ final class VideoChatScreenComponent: Component { if let next = self.nextSendMessageTransition { self.nextSendMessageTransition = nil sendActionTransition = MessageListComponent.SendActionTransition( - textSnapshotView: next.textSnapshotView, globalFrame: next.globalFrame, cornerRadius: next.cornerRadius + randomId: next.randomId, + textSnapshotView: next.textSnapshotView, + globalFrame: next.globalFrame, + cornerRadius: next.cornerRadius ) } @@ -3796,13 +3876,34 @@ final class VideoChatScreenComponent: Component { messageItems.append( MessageListComponent.Item( id: message.id, - peer: author, + icon: .peer(author), + isNotification: false, text: message.text, entities: message.entities ) ) } } + + for notification in self.messageNotifications { + let icon: MessageItemComponent.Icon + switch notification.icon { + case let .peer(peer): + icon = .peer(peer) + case let .icon(name): + icon = .icon(name) + } + messageItems.insert( + MessageListComponent.Item( + id: notification.id, + icon: icon, + isNotification: true, + text: notification.text, + entities: [] + ), + at: 0 + ) + } let normalMessagesBottomInset: CGFloat = buttonsOnTheSide ? 16.0 : availableSize.height - microphoneButtonFrame.minY + 16.0 let messagesBottomInset: CGFloat = max(inputPanelBottomInset + inputPanelSize.height + 31.0, normalMessagesBottomInset) + reactionsInset diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift index 92ff915aab..0d95cca58d 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift @@ -227,6 +227,29 @@ extension VideoChatScreenComponent.View { } self.openTitleEditing() }))) + + items.append(.action(ContextMenuActionItem(text: callState.messagesAreEnabled ? environment.strings.VoiceChat_ContextDisableMessages : environment.strings.VoiceChat_ContextEnableMessages, icon: { theme -> UIImage? in + return generateTintedImage(image: UIImage(bundleImageName: callState.messagesAreEnabled ? "Call/MessagesDisable" : "Call/MessagesEnable"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + f(.default) + + guard let self, let currentCall = self.currentCall else { + return + } + let isEnabled = !callState.messagesAreEnabled + currentCall.setMessagesEnabled(isEnabled: isEnabled) + + let iconName: String + let text: String + if isEnabled { + iconName = "Call/ToastMessagesEnabled" + text = environment.strings.VoiceChat_ToastMessagesEnabled + } else { + iconName = "Call/ToastMessagesDisabled" + text = environment.strings.VoiceChat_ToastMessagesDisabled + } + self.displayNotification(icon: .icon(iconName), text: text, duration: 3) + }))) var hasPermissions = true if let peer = self.peer, case let .channel(chatPeer) = peer { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 8183995e6d..3364d2462d 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -4909,9 +4909,12 @@ func replayFinalState( let canChange = (flags & (1 << 2)) != 0 let isVideoEnabled = (flags & (1 << 9)) != 0 let defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange) + let messagesEnabled = (flags & (1 << 17)) != 0 + let canChangeMessagesEnabled = (flags & (1 << 18)) != 0 + let messagesAreEnabled = GroupCallParticipantsContext.State.MessagesAreEnabled(isEnabled: messagesEnabled, canChange: canChangeMessagesEnabled) updatedGroupCallParticipants.append(( info.id, - .call(isTerminated: false, defaultParticipantsAreMuted: defaultParticipantsAreMuted, title: title, recordingStartTimestamp: recordStartDate, scheduleTimestamp: scheduleDate, isVideoEnabled: isVideoEnabled, participantCount: Int(participantsCount)) + .call(isTerminated: false, defaultParticipantsAreMuted: defaultParticipantsAreMuted, messagesAreEnabled: messagesAreEnabled, title: title, recordingStartTimestamp: recordStartDate, scheduleTimestamp: scheduleDate, isVideoEnabled: isVideoEnabled, participantCount: Int(participantsCount)) )) default: break @@ -4920,7 +4923,7 @@ func replayFinalState( case let .groupCallDiscarded(callId, _, _): updatedGroupCallParticipants.append(( callId, - .call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false), title: nil, recordingStartTimestamp: nil, scheduleTimestamp: nil, isVideoEnabled: false, participantCount: nil) + .call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false), messagesAreEnabled: GroupCallParticipantsContext.State.MessagesAreEnabled(isEnabled: false, canChange: false), title: nil, recordingStartTimestamp: nil, scheduleTimestamp: nil, isVideoEnabled: false, participantCount: nil) )) if let peerId { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 9fd25dd6c2..37fb9dfe0f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -766,7 +766,10 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, let isMuted = (flags & (1 << 1)) != 0 let canChange = (flags & (1 << 2)) != 0 let isVideoEnabled = (flags & (1 << 9)) != 0 + let messagesEnabled = (flags & (1 << 17)) != 0 + let canChangeMessagesEnabled = (flags & (1 << 18)) != 0 state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange) + state.messagesAreEnabled = GroupCallParticipantsContext.State.MessagesAreEnabled(isEnabled: messagesEnabled, canChange: canChangeMessagesEnabled) state.title = title state.recordingStartTimestamp = recordStartDate state.scheduleTimestamp = scheduleDate @@ -1583,7 +1586,7 @@ public final class GroupCallParticipantsContext { } case state(update: StateUpdate) - case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted, title: String?, recordingStartTimestamp: Int32?, scheduleTimestamp: Int32?, isVideoEnabled: Bool, participantCount: Int?) + case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted, messagesAreEnabled: State.MessagesAreEnabled, title: String?, recordingStartTimestamp: Int32?, scheduleTimestamp: Int32?, isVideoEnabled: Bool, participantCount: Int?) case conferenceChainBlocks(subChainId: Int, blocks: [Data], nextOffset: Int) } @@ -1961,9 +1964,10 @@ public final class GroupCallParticipantsContext { for update in updates { if case let .state(update) = update { stateUpdates.append(update) - } else if case let .call(_, defaultParticipantsAreMuted, title, recordingStartTimestamp, scheduleTimestamp, isVideoEnabled, participantsCount) = update { + } else if case let .call(_, defaultParticipantsAreMuted, messagesAreEnabled, title, recordingStartTimestamp, scheduleTimestamp, isVideoEnabled, participantsCount) = update { var state = self.stateValue.state state.defaultParticipantsAreMuted = defaultParticipantsAreMuted + state.messagesAreEnabled = messagesAreEnabled state.recordingStartTimestamp = recordingStartTimestamp state.title = title state.scheduleTimestamp = scheduleTimestamp @@ -2336,6 +2340,7 @@ public final class GroupCallParticipantsContext { state.adminIds = strongSelf.stateValue.state.adminIds state.isCreator = strongSelf.stateValue.state.isCreator state.defaultParticipantsAreMuted = strongSelf.stateValue.state.defaultParticipantsAreMuted + state.messagesAreEnabled = strongSelf.stateValue.state.messagesAreEnabled state.title = strongSelf.stateValue.state.title state.recordingStartTimestamp = strongSelf.stateValue.state.recordingStartTimestamp state.scheduleTimestamp = strongSelf.stateValue.state.scheduleTimestamp @@ -2583,12 +2588,12 @@ public final class GroupCallParticipantsContext { } public func updateMessagesEnabled(isEnabled: Bool) { - if isEnabled == self.stateValue.state.messagesAreEnabled.isEnabled { - return - } +// if isEnabled == self.stateValue.state.messagesAreEnabled.isEnabled { +// return +// } self.stateValue.state.messagesAreEnabled.isEnabled = isEnabled - self.updateDefaultMuteDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 2, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: isEnabled ? .boolTrue : .boolFalse)) + self.updateMessagesEnabledDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 2, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: isEnabled ? .boolTrue : .boolFalse)) |> deliverOnMainQueue).start(next: { [weak self] updates in guard let strongSelf = self else { return @@ -3636,7 +3641,7 @@ public final class GroupCallMessagesContext { } } - func send(fromId: EnginePeer.Id, text: String, entities: [MessageTextEntity]) { + func send(fromId: EnginePeer.Id, randomId: Int64?, text: String, entities: [MessageTextEntity]) { let _ = (self.account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(fromId) } @@ -3712,9 +3717,9 @@ public final class GroupCallMessagesContext { }) } - public func send(fromId: EnginePeer.Id, text: String, entities: [MessageTextEntity]) { + public func send(fromId: EnginePeer.Id, randomId: Int64?, text: String, entities: [MessageTextEntity]) { self.impl.with { impl in - impl.send(fromId: fromId, text: text, entities: entities) + impl.send(fromId: fromId, randomId: randomId, text: text, entities: entities) } } } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index f21bc61c56..75b72606ed 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1260,9 +1260,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes) } } - case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, _): + case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, assigned): if case let .unique(gift) = gift { - if !forAdditionalServiceMessage && !"".isEmpty { + if assigned { + let attributes: [Int: MarkdownAttributeSet] = [0: boldAttributes] + let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))" + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Assigned(giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } else if !forAdditionalServiceMessage && !"".isEmpty { attributedString = NSAttributedString(string: "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))", font: titleFont, textColor: primaryTextColor) } else if let messagePeer = message.peers[message.id.peerId] { var peerName = EnginePeer(messagePeer).compactDisplayTitle diff --git a/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift b/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift index 5b003f28b9..f8588251df 100644 --- a/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift +++ b/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift @@ -462,12 +462,11 @@ public final class ButtonComponent: Component { if let self, let component = self.component, component.isEnabled { switch component.background.style { case .glass: - let transition = ComponentTransition(animation: .curve(duration: 0.3, curve: .easeInOut)) + let transition = ComponentTransition(animation: .curve(duration: highlighted ? 0.25 : 0.35, curve: .spring)) if highlighted { let highlightedColor = component.background.color.withMultiplied(hue: 1.0, saturation: 0.77, brightness: 1.01) transition.setBackgroundColor(view: self.containerView, color: highlightedColor) transition.setScale(view: self.containerView, scale: 1.05) - } else { transition.setBackgroundColor(view: self.containerView, color: component.background.color) transition.setScale(view: self.containerView, scale: 1.0) diff --git a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift index f360dbcdea..868119c776 100644 --- a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift @@ -529,14 +529,13 @@ public final class MessageInputActionButtonComponent: Component { self.backgroundView = backgroundView } - var tintColor = UIColor(rgb: 0x4d4f5c, alpha: 0.6) - var tintKind: GlassBackgroundView.TintColor.Kind = .panel + var tintColor = UIColor(rgb: 0x25272e, alpha: 0.72) + let tintKind: GlassBackgroundView.TintColor.Kind = .custom if case .send = component.mode { tintColor = UIColor(rgb: 0x029dff) - tintKind = .custom } let buttonSize = CGSize(width: 40.0, height: 40.0) - backgroundView.update(size: buttonSize, cornerRadius: buttonSize.height / 2.0, isDark: false, tintColor: .init(kind: tintKind, color: tintColor), transition: transition) + backgroundView.update(size: buttonSize, cornerRadius: buttonSize.height / 2.0, isDark: true, tintColor: .init(kind: tintKind, color: tintColor), transition: transition) backgroundView.frame = CGRect(origin: .zero, size: buttonSize) } diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index f7928f2ea8..3476b86958 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -157,11 +157,13 @@ public final class MessageInputPanelComponent: Component { } public final class SendActionTransition { + public let randomId: Int64 public let textSnapshotView: UIView public let globalFrame: CGRect public let cornerRadius: CGFloat - init(textSnapshotView: UIView, globalFrame: CGRect, cornerRadius: CGFloat) { + init(randomId: Int64, textSnapshotView: UIView, globalFrame: CGRect, cornerRadius: CGFloat) { + self.randomId = randomId self.textSnapshotView = textSnapshotView self.globalFrame = globalFrame self.cornerRadius = cornerRadius @@ -785,6 +787,7 @@ public final class MessageInputPanelComponent: Component { var sendActionTransition: MessageInputPanelComponent.SendActionTransition? if let snapshotView = self.textClippingView.snapshotView(afterScreenUpdates: false), let backgroundView = self.fieldGlassBackgroundView { sendActionTransition = MessageInputPanelComponent.SendActionTransition( + randomId: Int64.random(in: .min ..< .max), textSnapshotView: snapshotView, globalFrame: backgroundView.convert(backgroundView.bounds, to: nil), cornerRadius: baseFieldHeight * 0.5 @@ -1101,7 +1104,7 @@ public final class MessageInputPanelComponent: Component { self.fieldBackgroundTint.isHidden = true } if let fieldGlassBackgroundView = self.fieldGlassBackgroundView { - fieldGlassBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, isDark: true, tintColor: .init(kind: .panel, color: UIColor(rgb: 0x4d4f5c, alpha: 0.6)), transition: transition) + fieldGlassBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72)), transition: transition) transition.setFrame(view: fieldGlassBackgroundView, frame: fieldBackgroundFrame) } default: @@ -1348,7 +1351,10 @@ public final class MessageInputPanelComponent: Component { environment: {}, containerSize: availableTextFieldSize ) - let counterFrame = CGRect(origin: CGPoint(x: availableSize.width - insets.right + floorToScreenPixels((insets.right - counterSize.width) * 0.5), y: size.height - insets.bottom - baseFieldHeight - counterSize.height - 5.0), size: counterSize) + var counterFrame = CGRect(origin: CGPoint(x: availableSize.width - insets.right + floorToScreenPixels((insets.right - counterSize.width) * 0.5), y: size.height - insets.bottom - baseFieldHeight - counterSize.height - 5.0), size: counterSize) + if case .glass = component.style { + counterFrame.origin.x -= 7.0 + } if let counterView = self.counter.view { if counterView.superview == nil { self.addSubview(counterView) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 56e8b5201d..1c51a71f3a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -6680,7 +6680,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if !headerButtons.contains(.discussion) && hasDiscussion { //TODO:localize - items.append(.action(ContextMenuActionItem(text: "View Discussion", icon: { theme in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ViewDiscussion, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.dismissWithoutContent) diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift index 047dfe85de..c24f054aeb 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift @@ -480,14 +480,13 @@ private final class ProfileLevelInfoScreenComponent: Component { levelFraction = max(0.0, levelFraction) - //TODO:localize let levelInfoSize = self.levelInfo.update( transition: isChangingPreview ? ComponentTransition.immediate.withUserData(ProfileLevelRatingBarComponent.TransitionHint(animate: true)) : .immediate, component: AnyComponent(ProfileLevelRatingBarComponent( theme: environment.theme, value: levelFraction, leftLabel: currentLevel < 0 ? "" : environment.strings.ProfileLevelInfo_LevelIndex(Int32(currentLevel)), - rightLabel: currentLevel < 0 ? "Negative rating" : nextLevel.flatMap { environment.strings.ProfileLevelInfo_LevelIndex(Int32($0)) } ?? "", + rightLabel: currentLevel < 0 ? environment.strings.ProfileLevelInfo_NegativeRating : nextLevel.flatMap { environment.strings.ProfileLevelInfo_LevelIndex(Int32($0)) } ?? "", badgeValue: badgeText, badgeTotal: badgeTextSuffix, level: Int(currentLevel), diff --git a/submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/Contents.json new file mode 100644 index 0000000000..c603e6f2eb --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "disablemessages_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/disablemessages_24.pdf b/submodules/TelegramUI/Images.xcassets/Call/MessagesDisable.imageset/disablemessages_24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a8574eb66ed358f6b28618f78a5d9659468a8cdd GIT binary patch literal 5056 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/Contents.json new file mode 100644 index 0000000000..f55ee0499b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "enablemessages_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/enablemessages_24.pdf b/submodules/TelegramUI/Images.xcassets/Call/MessagesEnable.imageset/enablemessages_24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c8c915b4fdc2ba2904616db10a324090ef9667b4 GIT binary patch literal 4758 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 Dx~48@ literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/Contents.json new file mode 100644 index 0000000000..ea4ae18d46 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "toastmessagesdisabled_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/toastmessagesdisabled_30.pdf b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesDisabled.imageset/toastmessagesdisabled_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0ca6b3052b9e5e72721a70a9258a3108725a6236 GIT binary patch literal 4663 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 IrfaJAe+W+JQvd(} literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/Contents.json new file mode 100644 index 0000000000..f793a4d80d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "toastmessagesenabled_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/toastmessagesenabled_30.pdf b/submodules/TelegramUI/Images.xcassets/Call/ToastMessagesEnabled.imageset/toastmessagesenabled_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cce6990a97c9f6380af17ff746f81118d3470e0d GIT binary patch literal 4389 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