diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 4141089a0a..db62c26c5b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -3863,6 +3863,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg let counterSize = self.counterTextNode.updateLayout(CGSize(width: 40.0, height: 40.0)) let counterFrame = CGRect(origin: CGPoint(x: backgroundSize.width - 11.0 - counterSize.width, y: 4.0), size: CGSize(width: counterSize.width, height: counterSize.height)) transition.updateFrame(node: self.counterTextNode, frame: counterFrame) + transition.updateAlpha(node: self.counterTextNode, alpha: backgroundSize.height > 50.0 ? 1.0 : 0.0) } private func installEmojiSuggestionPreviewGesture(hostView: UIView) { diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 0a9fe2b676..19848504e5 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -1034,6 +1034,23 @@ public final class MessageInputPanelComponent: Component { ) } + let rightAction: ChatTextInputPanelComponent.RightAction + if component.sendStarsAction != nil { + rightAction = ChatTextInputPanelComponent.RightAction(kind: .stars(count: Int(component.starStars?.totalStars ?? 0), isFilled: component.starStars?.hasOutgoingStars ?? false), action: { [weak self] sourceView in + guard let self, let component = self.component else { + return + } + component.sendStarsAction?(sourceView, false) + }, longPressAction: { [weak self] sourceView in + guard let self, let component = self.component else { + return + } + component.sendStarsAction?(sourceView, true) + }) + } else { + rightAction = ChatTextInputPanelComponent.RightAction(kind: .empty, action: { _ in }) + } + let inputPanelSize = inputPanel.update( transition: transition, component: AnyComponent(ChatTextInputPanelComponent( @@ -1056,17 +1073,7 @@ public final class MessageInputPanelComponent: Component { component.toggleLiveChatExpanded?() } }), - rightAction: ChatTextInputPanelComponent.RightAction(kind: .stars(count: Int(component.starStars?.totalStars ?? 0), isFilled: component.starStars?.hasOutgoingStars ?? false), action: { [weak self] sourceView in - guard let self, let component = self.component else { - return - } - component.sendStarsAction?(sourceView, false) - }, longPressAction: { [weak self] sourceView in - guard let self, let component = self.component else { - return - } - component.sendStarsAction?(sourceView, true) - }), + rightAction: rightAction, sendAsConfiguration: component.liveChatState?.isEnabled == true ? sendAsConfiguration : nil, //TODO:localize placeholder: component.liveChatState?.isEnabled == true ? placeholder : "Comments are disabled", diff --git a/submodules/TelegramUI/Components/PeerNameTextComponent/BUILD b/submodules/TelegramUI/Components/PeerNameTextComponent/BUILD new file mode 100644 index 0000000000..272c740cb0 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerNameTextComponent/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerNameTextComponent", + module_name = "PeerNameTextComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/AppBundle", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/TelegramPresentationData", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerNameTextComponent/Sources/PeerNameTextComponent.swift b/submodules/TelegramUI/Components/PeerNameTextComponent/Sources/PeerNameTextComponent.swift new file mode 100644 index 0000000000..946251a9ac --- /dev/null +++ b/submodules/TelegramUI/Components/PeerNameTextComponent/Sources/PeerNameTextComponent.swift @@ -0,0 +1,185 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import MultilineTextComponent +import EmojiStatusComponent +import TelegramCore +import AccountContext + +public final class PeerNameTextComponent: Component { + public enum TextContent: Equatable { + case name + case custom(String) + } + + public let context: AccountContext + public let peer: EnginePeer? + public let text: TextContent + public let font: UIFont + public let textColor: UIColor + public let iconBackgroundColor: UIColor + public let iconForegroundColor: UIColor + public let strings: PresentationStrings + + public init( + context: AccountContext, + peer: EnginePeer?, + text: TextContent, + font: UIFont, + textColor: UIColor, + iconBackgroundColor: UIColor, + iconForegroundColor: UIColor, + strings: PresentationStrings + ) { + self.context = context + self.peer = peer + self.text = text + self.font = font + self.textColor = textColor + self.iconBackgroundColor = iconBackgroundColor + self.iconForegroundColor = iconForegroundColor + self.strings = strings + } + + public static func ==(lhs: PeerNameTextComponent, rhs: PeerNameTextComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.font != rhs.font { + return false + } + if lhs.textColor != rhs.textColor { + return false + } + if lhs.iconBackgroundColor != rhs.iconBackgroundColor { + return false + } + if lhs.iconForegroundColor != rhs.iconForegroundColor { + return false + } + if lhs.strings !== rhs.strings { + return false + } + return true + } + + public final class View: HighlightTrackingButton { + private let title = ComponentView() + private var icon: ComponentView? + + private var component: PeerNameTextComponent? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: PeerNameTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + var iconContent: EmojiStatusComponent.Content? + if let peer = component.peer { + if peer.isScam { + iconContent = .text(color: UIColor(rgb: 0xeb5545), string: component.strings.Message_ScamAccount.uppercased()) + } else if peer.isFake { + iconContent = .text(color: UIColor(rgb: 0xeb5545), string: component.strings.Message_FakeAccount.uppercased()) + } + + if peer.isVerified { + iconContent = .verified(fillColor: component.iconBackgroundColor, foregroundColor: component.iconForegroundColor, sizeType: .compact) + } + } + + let titleText: String + switch component.text { + case .name: + if let peer = component.peer { + titleText = peer.displayTitle(strings: component.strings, displayOrder: .firstLast) + } else { + titleText = " " + } + case let .custom(value): + titleText = value + } + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: titleText, font: component.font, textColor: .white)), + truncationType: .end, + maximumNumberOfLines: 1 + )), + environment: {}, + containerSize: availableSize + ) + + var titleX: CGFloat = 0.0 + if let iconContent { + let icon: ComponentView + if let current = self.icon { + icon = current + } else { + icon = ComponentView() + self.icon = icon + } + + let containerSize = CGSize(width: 16.0, height: 16.0) + let iconSize = icon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: iconContent, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: containerSize + ) + let iconFrame = CGRect(origin: CGPoint(x: titleX, y: floorToScreenPixels((titleSize.height - iconSize.height) * 0.5)), size: iconSize) + titleX += iconSize.width + 4.0 + if let iconView = icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + transition.setFrame(view: iconView, frame: iconFrame) + } + } else if let icon = self.icon { + self.icon = nil + icon.view?.removeFromSuperview() + } + + let titleFrame = CGRect(origin: CGPoint(x: titleX, y: 0.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.layer.anchorPoint = CGPoint() + self.addSubview(titleView) + } + transition.setPosition(view: titleView, position: titleFrame.origin) + titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) + } + + return CGSize(width: titleFrame.maxX, height: titleSize.height) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/BUILD b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/BUILD index f0d8f78141..fc8f8c4fd8 100644 --- a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/BUILD +++ b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/BUILD @@ -21,6 +21,7 @@ swift_library( "//submodules/AvatarNode", "//submodules/TelegramPresentationData", "//submodules/TelegramUI/Components/StarsParticleEffect", + "//submodules/TelegramUI/Components/PeerNameTextComponent", "//submodules/TextFormat", ], visibility = [ diff --git a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift index 6ea9cb3e48..ca573a5e51 100644 --- a/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift +++ b/submodules/TelegramUI/Components/Stories/LiveChat/StoryLiveChatMessageComponent/Sources/StoryLiveChatMessageComponent.swift @@ -11,6 +11,7 @@ import AccountContext import StarsParticleEffect import AppBundle import TextFormat +import PeerNameTextComponent private func generateStarsAmountImage() -> UIImage { return UIImage(bundleImageName: "Chat/Message/StarsCount")!.precomposed().withRenderingMode(.alwaysTemplate) @@ -305,16 +306,24 @@ public final class StoryLiveChatMessageComponent: Component { maxTextWidth -= cutoutWidth } + let textIconsForegroundColor: UIColor + if let paidStars = component.message.paidStars, let baseColor = GroupCallMessagesContext.getStarAmountParamMapping(params: LiveChatMessageParams(appConfig: component.context.currentAppConfiguration.with({ $0 })), value: paidStars).color { + textIconsForegroundColor = StoryLiveChatMessageComponent.getMessageColor(color: baseColor).withAlphaComponent(component.layout.transparentBackground ? 0.7 : 1.0) + } else { + textIconsForegroundColor = .black + } + let authorTitleSize = self.authorTitle.update( transition: .immediate, - component: AnyComponent(MultilineTextWithEntitiesComponent( + component: AnyComponent(PeerNameTextComponent( context: component.context, - animationCache: component.context.animationCache, - animationRenderer: component.context.animationRenderer, - placeholderColor: .gray, - text: .plain(NSAttributedString(string: component.message.author?.displayTitle(strings: component.strings, displayOrder: .firstLast) ?? " ", font: Font.semibold(15.0), textColor: secondaryTextColor)), - maximumNumberOfLines: 1, - lineSpacing: 0.1 + peer: component.message.author, + text: .name, + font: Font.semibold(15.0), + textColor: secondaryTextColor, + iconBackgroundColor: .white, + iconForegroundColor: textIconsForegroundColor, + strings: component.strings )), environment: {}, containerSize: CGSize(width: min(maxTextWidth - 80.0, 180.0), height: 100000.0) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD index 8d049bc116..54ef6207cb 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD @@ -113,6 +113,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu", "//submodules/Components/HierarchyTrackingLayer", "//submodules/Utils/LokiRng", + "//submodules/TelegramUI/Components/PeerNameTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift index a06d2dda92..5456187898 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/PinnedBarComponent.swift @@ -13,6 +13,7 @@ import AvatarNode import ContextUI import StarsParticleEffect import StoryLiveChatMessageComponent +import PeerNameTextComponent private final class PinnedBarMessageComponent: Component { let context: AccountContext @@ -161,6 +162,9 @@ private final class PinnedBarMessageComponent: Component { self.containerNode.isGestureEnabled = component.contextGesture != nil + let params = LiveChatMessageParams(appConfig: component.context.currentAppConfiguration.with({ $0 })) + let baseColor = StoryLiveChatMessageComponent.getMessageColor(color: GroupCallMessagesContext.getStarAmountParamMapping(params: params, value: component.message.paidStars ?? 0).color ?? GroupCallMessagesContext.Message.Color(rawValue: 0x985FDC)) + let itemHeight: CGFloat = 32.0 let avatarInset: CGFloat = 4.0 let avatarSize: CGFloat = 24.0 @@ -169,8 +173,15 @@ private final class PinnedBarMessageComponent: Component { let titleSize = self.title.update( transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: component.message.author?.displayTitle(strings: component.strings, displayOrder: .firstLast) ?? " ", font: Font.semibold(15.0), textColor: .white)) + component: AnyComponent(PeerNameTextComponent( + context: component.context, + peer: component.message.author, + text: .name, + font: Font.semibold(15.0), + textColor: .white, + iconBackgroundColor: .white, + iconForegroundColor: baseColor, + strings: component.strings )), environment: {}, containerSize: CGSize(width: 200.0, height: itemHeight) @@ -207,8 +218,6 @@ private final class PinnedBarMessageComponent: Component { self.foregroundView.image = self.backgroundView.image } - let params = LiveChatMessageParams(appConfig: component.context.currentAppConfiguration.with({ $0 })) - let baseColor = StoryLiveChatMessageComponent.getMessageColor(color: GroupCallMessagesContext.getStarAmountParamMapping(params: params, value: component.message.paidStars ?? 0).color ?? GroupCallMessagesContext.Message.Color(rawValue: 0x985FDC)) self.backgroundView.tintColor = baseColor.withMultipliedBrightnessBy(0.7) self.foregroundView.tintColor = baseColor diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift index 124221db3e..bc4ff30a48 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryAuthorInfoComponent.swift @@ -8,6 +8,7 @@ import TelegramStringFormatting import MultilineTextComponent import TelegramPresentationData import AvatarNode +import PeerNameTextComponent final class StoryAuthorInfoComponent: Component { struct Counters: Equatable { @@ -16,6 +17,7 @@ final class StoryAuthorInfoComponent: Component { } let context: AccountContext + let theme: PresentationTheme let strings: PresentationStrings let isEmbeddedInCamera: Bool let peer: EnginePeer? @@ -27,8 +29,9 @@ final class StoryAuthorInfoComponent: Component { let isLiveStream: Bool let customSubtitle: String? - init(context: AccountContext, strings: PresentationStrings, isEmbeddedInCamera: Bool, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, author: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool, isLiveStream: Bool, customSubtitle: String?) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, isEmbeddedInCamera: Bool, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, author: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool, isLiveStream: Bool, customSubtitle: String?) { self.context = context + self.theme = theme self.strings = strings self.isEmbeddedInCamera = isEmbeddedInCamera self.peer = peer @@ -45,6 +48,9 @@ final class StoryAuthorInfoComponent: Component { if lhs.context !== rhs.context { return false } + if lhs.theme !== rhs.theme { + return false + } if lhs.strings !== rhs.strings { return false } @@ -179,10 +185,15 @@ final class StoryAuthorInfoComponent: Component { let titleSize = self.title.update( transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: title, font: Font.medium(14.0), textColor: .white)), - truncationType: .end, - maximumNumberOfLines: 1 + component: AnyComponent(PeerNameTextComponent( + context: component.context, + peer: component.peer, + text: .custom(title), + font: Font.medium(14.0), + textColor: .white, + iconBackgroundColor: component.theme.list.itemCheckColors.fillColor, + iconForegroundColor: component.theme.list.itemCheckColors.foregroundColor, + strings: component.strings )), environment: {}, containerSize: CGSize(width: availableSize.width - leftInset, height: availableSize.height) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift index 7b6c478881..fba88b394d 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift @@ -437,8 +437,15 @@ final class StoryContentLiveChatComponent: Component { canDelete = true } + //TODO:localize if !isMyMessage, let author = message.author { - items.append(.action(ContextMenuActionItem(text: "Open Profile", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + let openProfileString: String + if case .channel = author { + openProfileString = "Open Channel" + } else { + openProfileString = "Open Profile" + } + items.append(.action(ContextMenuActionItem(text: openProfileString, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in guard let self else { return } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 8dbc46fd69..205ba69915 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2987,6 +2987,13 @@ public final class StoryItemSetContainerComponent: Component { var sendPaidMessageStars = isLiveStream ? self.sendMessageContext.currentLiveStreamMessageStars : component.slice.additionalPeerData.sendPaidMessageStars var maxInputLength = 4096 var maxEmojiCount: Int? + var canSendStars = false + + if isLiveStream { + if component.slice.item.peerId != component.context.account.peerId { + canSendStars = true + } + } if let visibleItemView = self.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View { if let liveChatStateValue = visibleItemView.liveChatState { @@ -3288,7 +3295,7 @@ public final class StoryItemSetContainerComponent: Component { visibleItemView.toggleLiveChatExpanded() } }, - sendStarsAction: isLiveStream ? { [weak self] sourceView, isLongPress in + sendStarsAction: (isLiveStream && canSendStars) ? { [weak self] sourceView, isLongPress in guard let self else { return } @@ -4272,6 +4279,7 @@ public final class StoryItemSetContainerComponent: Component { let centerInfoComponent = AnyComponent(StoryAuthorInfoComponent( context: component.context, + theme: component.theme, strings: component.strings, isEmbeddedInCamera: component.isEmbeddedInCamera, peer: component.slice.effectivePeer,