From 6f60e7c89ef11a418be1c81f7db7c5026a549720 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 20 Oct 2024 16:04:39 +0400 Subject: [PATCH] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 +- .../Sources/ThemedTextAlertController.swift | 4 +- .../Sources/ChatListSearchListPaneNode.swift | 37 ++++++++++++++++++- .../Sources/ContactsPeerItem.swift | 7 +++- .../Display/Source/TextAlertController.swift | 15 ++++++-- .../Sources/AlertTheme.swift | 8 ++-- 6 files changed, 61 insertions(+), 12 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 43792c1328..0677a3bd71 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13121,5 +13121,5 @@ Sorry for the inconvenience."; "ChatList.Search.TopAppsInfo" = "Which apps are included here? [Learn >]()"; "TopApps.Info.Title" = "Top Mini Apps"; -"TopApps.Info.Text" = "This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini app in [@botfather]() (as described [here]()), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on weekly average."; +"TopApps.Info.Text" = "This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini app in [@botfather]() (as described [here](https://core.telegram.org/bots/webapps#launching-the-main-mini-app)), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on weekly average."; "TopApps.Info.Done" = "Understood"; diff --git a/submodules/AlertUI/Sources/ThemedTextAlertController.swift b/submodules/AlertUI/Sources/ThemedTextAlertController.swift index c7ef5ddff0..d2d40d0464 100644 --- a/submodules/AlertUI/Sources/ThemedTextAlertController.swift +++ b/submodules/AlertUI/Sources/ThemedTextAlertController.swift @@ -14,8 +14,8 @@ public final class AlertControllerContext { } } -public func textAlertController(alertContext: AlertControllerContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController { - let controller = standardTextAlertController(theme: alertContext.theme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap) +public func textAlertController(alertContext: AlertControllerContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { + let controller = standardTextAlertController(theme: alertContext.theme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction) let presentationDataDisposable = alertContext.themeSignal.start(next: { [weak controller] theme in controller?.theme = theme }) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index c9ca2a8af4..416332d70f 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -312,6 +312,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { buttonAction: buttonAction, index: nil, header: header, + alwaysShowLastSeparator: key == .apps, action: { _ in if let chatPeer = peer.peer.peers[peer.peer.peerId] { peerSelected(EnginePeer(chatPeer), nil, section == .recommendedChannels || section == .popularApps) @@ -3832,14 +3833,48 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }, animationCache: strongSelf.animationCache, animationRenderer: strongSelf.animationRenderer, openStories: { peerId, avatarNode in interaction.openStories?(peerId, avatarNode) }, openTopAppsInfo: { + var dismissImpl: (() -> Void)? let alertController = textAlertController( context: context, title: presentationData.strings.TopApps_Info_Title, text: presentationData.strings.TopApps_Info_Text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.TopApps_Info_Done, action: {})], - parseMarkdown: true + parseMarkdown: true, + linkAction: { attributes, _ in + guard let self, let navigationController = self.navigationController else { + return + } + dismissImpl?() + if let value = attributes[NSAttributedString.Key(rawValue: "URL")] as? String { + if !value.isEmpty { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {}) + } else { + let _ = (context.engine.peers.resolvePeerByName(name: "botfather") + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + |> deliverOnMainQueue).start(next: { [weak navigationController] peer in + guard let navigationController, let peer else { + return + } + context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: context, + chatLocation: .peer(peer), + keepStack: .always + )) + }) + } + } + } ) interaction.present(alertController, nil) + dismissImpl = { [weak alertController] in + alertController?.dismissAnimated() + } }, isChannelsTabExpanded: recentItems.isChannelsTabExpanded, toggleChannelsTabExpanded: { diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index e25dd59ac9..a782b777fd 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -187,6 +187,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { let actionIcon: ContactsPeerItemActionIcon let buttonAction: ContactsPeerItemButtonAction? let searchQuery: String? + let alwaysShowLastSeparator: Bool let action: ((ContactsPeerItemPeer) -> Void)? let disabledAction: ((ContactsPeerItemPeer) -> Void)? let setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? @@ -229,6 +230,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { index: SortIndex?, header: ListViewItemHeader?, searchQuery: String? = nil, + alwaysShowLastSeparator: Bool = false, action: ((ContactsPeerItemPeer) -> Void)?, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, @@ -261,6 +263,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { self.actionIcon = actionIcon self.buttonAction = buttonAction self.searchQuery = searchQuery + self.alwaysShowLastSeparator = alwaysShowLastSeparator self.action = action self.disabledAction = disabledAction self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions @@ -1675,7 +1678,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) strongSelf.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(nodeLayout.insets.top, separatorHeight)), size: CGSize(width: nodeLayout.contentSize.width, height: separatorHeight)) strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: max(0.0, nodeLayout.size.width - leftInset), height: separatorHeight)) - strongSelf.separatorNode.isHidden = last + if !item.alwaysShowLastSeparator { + strongSelf.separatorNode.isHidden = last + } if let userPresence = userPresence { strongSelf.peerPresenceManager?.reset(presence: userPresence) diff --git a/submodules/Display/Source/TextAlertController.swift b/submodules/Display/Source/TextAlertController.swift index 55ee25acd1..8072f48881 100644 --- a/submodules/Display/Source/TextAlertController.swift +++ b/submodules/Display/Source/TextAlertController.swift @@ -188,7 +188,7 @@ public final class TextAlertContentNode: AlertContentNode { } } - public init(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout, dismissOnOutsideTap: Bool) { + public init(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout, dismissOnOutsideTap: Bool, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) { self.theme = theme self.actionLayout = actionLayout self._dismissOnOutsideTap = dismissOnOutsideTap @@ -214,6 +214,15 @@ public final class TextAlertContentNode: AlertContentNode { self.textNode.isAccessibilityElement = true self.textNode.accessibilityLabel = text.string self.textNode.insets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) + self.textNode.tapAttributeAction = linkAction + self.textNode.highlightAttributeAction = { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] { + return NSAttributedString.Key(rawValue: "URL") + } else { + return nil + } + } + self.textNode.linkHighlightColor = theme.accentColor.withMultipliedAlpha(0.1) if text.length != 0 { if let paragraphStyle = text.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle { self.textNode.textAlignment = paragraphStyle.alignment @@ -450,7 +459,7 @@ public func textAlertController(theme: AlertControllerTheme, title: NSAttributed return AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title, text: text, actions: actions, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap)) } -public func standardTextAlertController(theme: AlertControllerTheme, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController { +public func standardTextAlertController(theme: AlertControllerTheme, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { var dismissImpl: (() -> Void)? let attributedText: NSAttributedString if parseMarkdown { @@ -470,7 +479,7 @@ public func standardTextAlertController(theme: AlertControllerTheme, title: Stri dismissImpl?() action.action() }) - }, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap), allowInputInset: allowInputInset) + }, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction), allowInputInset: allowInputInset) dismissImpl = { [weak controller] in controller?.dismissAnimated() } diff --git a/submodules/PresentationDataUtils/Sources/AlertTheme.swift b/submodules/PresentationDataUtils/Sources/AlertTheme.swift index 610480dfc7..1fa7f48d18 100644 --- a/submodules/PresentationDataUtils/Sources/AlertTheme.swift +++ b/submodules/PresentationDataUtils/Sources/AlertTheme.swift @@ -5,11 +5,11 @@ import AccountContext import SwiftSignalKit import TelegramPresentationData -public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController { - return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap) +public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { + return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction) } -public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController { +public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { var presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } if let forceTheme = forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) @@ -21,7 +21,7 @@ public func textAlertController(sharedContext: SharedAccountContext, updatedPres presentationData = presentationData.withUpdated(theme: forceTheme) } return AlertControllerTheme(presentationData: presentationData) - }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap) + }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction) } public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {