diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2dccba4ca3..aa70fb4365 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12653,5 +12653,6 @@ Sorry for the inconvenience."; "Story.Cover" = "Story Cover"; "Story.SaveCover" = "Save Cover"; +"WebBrowser.CopyLink" = "Copy Link"; "WebBrowser.DeleteBookmark" = "Delete Bookmark"; "WebBrowser.RemoveRecent" = "Remove from Recent"; diff --git a/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift b/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift index b22bb333e9..e17589cd96 100644 --- a/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift @@ -9,6 +9,7 @@ import TelegramCore import AccountContext import TelegramPresentationData import ContextUI +import UndoUI final class BrowserAddressListComponent: Component { let context: AccountContext @@ -336,13 +337,23 @@ final class BrowserAddressListComponent: Component { } }, contextAction: { [weak self] webPage, message, sourceView, gesture in - guard let self, let component = self.component else { + guard let self, let component = self.component, let url = webPage.content.url else { return } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } var itemList: [ContextMenuItem] = [] + itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + UIPasteboard.general.string = url + if let self, let component = self.component { + component.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false })) + } + }))) if let message { itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in diff --git a/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift b/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift index 97079f1686..05ff000d7d 100644 --- a/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift @@ -186,6 +186,7 @@ final class BrowserAddressListItemComponent: Component { } address = address.replacingOccurrences(of: "https://www.", with: "") address = address.replacingOccurrences(of: "https://", with: "") + address = address.replacingOccurrences(of: "tonsite://", with: "") address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/")) subtitle = address diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index a3bcafe42c..64ec20912b 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -16,6 +16,8 @@ import UrlWhitelist import SearchUI import SearchBarNode import ChatHistorySearchContainerNode +import ContextUI +import UndoUI public final class BrowserBookmarksScreen: ViewController { final class Node: ViewControllerTracingNode, ASScrollViewDelegate { @@ -39,6 +41,7 @@ public final class BrowserBookmarksScreen: ViewController { self.presentationData = presentationData var openMessageImpl: ((Message) -> Bool)? + var openContextMenuImpl: ((Message, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void)? self.controllerInteraction = ChatControllerInteraction(openMessage: { message, _ in if let openMessageImpl = openMessageImpl { return openMessageImpl(message) @@ -47,7 +50,8 @@ public final class BrowserBookmarksScreen: ViewController { } }, openPeer: { _, _, _, _ in }, openPeerMention: { _, _ in - }, openMessageContextMenu: { _, _, _, _, _, _ in + }, openMessageContextMenu: { message, _, sourceView, rect, gesture, _ in + openContextMenuImpl?(message, sourceView, rect, gesture) }, openMessageReactionContextMenu: { _, _, _, _ in }, updateMessageReaction: { _, _, _, _ in }, activateMessagePinch: { _ in @@ -220,6 +224,47 @@ public final class BrowserBookmarksScreen: ViewController { self.containerLayoutUpdated(layout: layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) } } + + openContextMenuImpl = { [weak self] message, sourceNode, rect, gesture in + guard let self, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else { + return + } + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + + var itemList: [ContextMenuItem] = [] + if let webPage = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, let url = webPage.content.url { + itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + UIPasteboard.general.string = url + if let self { + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + } + }))) + } + itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let self { + let _ = self.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone() + } + }))) + + let items = ContextController.Items(content: .list(itemList)) + let controller = ContextController( + presentationData: presentationData, + source: .extracted(BrowserBookmarksContextExtractedContentSource(contentNode: sourceNode)), + items: .single(items), + recognizer: nil, + gesture: gesture as? ContextGesture + ) + self.controller?.presentInGlobalOverlay(controller) + } } func activateSearch(placeholderNode: SearchBarPlaceholderNode) { @@ -515,3 +560,23 @@ private class BottomPanelNode: ASDisplayNode { } } + +final class BrowserBookmarksContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = false + let ignoreContentTouches: Bool = false + let blurBackground: Bool = true + + private let contentNode: ContextExtractedContentContainingNode + + init(contentNode: ContextExtractedContentContainingNode) { + self.contentNode = contentNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.contentNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift index ab0d7066ce..89e0387e3d 100644 --- a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift @@ -261,6 +261,8 @@ final class BrowserNavigationBarComponent: CombinedComponent { availableWidth -= itemSpacing * CGFloat(max(0, leftItemList.count - 1)) + itemSpacing * CGFloat(max(0, rightItemList.count - 1)) + 30.0 } availableWidth -= context.component.sideInset * 2.0 + + let canCenter = availableWidth > 660.0 availableWidth = min(660.0, availableWidth) let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction) @@ -276,7 +278,11 @@ final class BrowserNavigationBarComponent: CombinedComponent { var centerX = maxCenterInset + (context.availableSize.width - maxCenterInset * 2.0) / 2.0 if "".isEmpty { - centerX = centerLeftInset + (context.availableSize.width - centerLeftInset - centerRightInset) / 2.0 + if canCenter { + centerX = context.availableSize.width / 2.0 + } else { + centerX = centerLeftInset + (context.availableSize.width - centerLeftInset - centerRightInset) / 2.0 + } } if let centerItem = centerItem { let centerItemPosition = CGPoint(x: centerX, y: context.component.topInset + contentHeight / 2.0 + verticalOffset) diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index ebd10a37e0..c1d0d9ff8a 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -153,24 +153,24 @@ private final class BrowserScreenComponent: CombinedComponent { ] if isTablet { - navigationLeftItems.append( - AnyComponentWithIdentity( - id: "minimize", - component: AnyComponent( - Button( - content: AnyComponent( - BundleIconComponent( - name: "Media Gallery/PictureInPictureButton", - tintColor: environment.theme.rootController.navigationBar.accentTextColor - ) - ), - action: { - performAction.invoke(.close) - } - ) - ) - ) - ) +// navigationLeftItems.append( +// AnyComponentWithIdentity( +// id: "minimize", +// component: AnyComponent( +// Button( +// content: AnyComponent( +// BundleIconComponent( +// name: "Media Gallery/PictureInPictureButton", +// tintColor: environment.theme.rootController.navigationBar.accentTextColor +// ) +// ), +// action: { +// performAction.invoke(.close) +// } +// ) +// ) +// ) +// ) let canGoBack = context.component.contentState?.canGoBack ?? false let canGoForward = context.component.contentState?.canGoForward ?? false diff --git a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift index d0308f9d5d..de53829137 100644 --- a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift @@ -348,7 +348,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { } address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/")) - let plainUrlString = NSAttributedString(string: address.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor) + let plainUrlString = NSAttributedString(string: address.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor) let urlString = NSMutableAttributedString() urlString.append(plainUrlString) urlString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.URL), value: content.url, range: NSMakeRange(0, urlString.length)) @@ -412,8 +412,13 @@ public final class ListMessageSnippetItemNode: ListMessageNode { let rawUrlString = urlString var parsedUrl = URL(string: urlString) if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") { - urlString = "http://" + urlString - parsedUrl = URL(string: urlString) + if let mappedURL = URL(string: "https://\(urlString)"), let host = mappedURL.host, host.lowercased().hasSuffix(".ton") { + urlString = "tonsite://" + urlString + parsedUrl = URL(string: urlString) + } else { + urlString = "http://" + urlString + parsedUrl = URL(string: urlString) + } } var host: String? = concealed ? urlString : parsedUrl?.host if host == nil { @@ -463,7 +468,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { urlString = address let urlAttributedString = NSMutableAttributedString() - urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)) + urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)) if item.presentationData.theme.theme.list.itemAccentColor.isEqual(item.presentationData.theme.theme.list.itemPrimaryTextColor) { urlAttributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: NSMakeRange(0, urlAttributedString.length)) } @@ -495,8 +500,13 @@ public final class ListMessageSnippetItemNode: ListMessageNode { let rawUrlString = urlString var parsedUrl = URL(string: urlString) if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") { - urlString = "http://" + urlString - parsedUrl = URL(string: urlString) + if let mappedURL = URL(string: "https://\(urlString)"), let host = mappedURL.host, host.lowercased().hasSuffix(".ton") { + urlString = "tonsite://" + urlString + parsedUrl = URL(string: urlString) + } else { + urlString = "http://" + urlString + parsedUrl = URL(string: urlString) + } } let host: String? = concealed ? urlString : parsedUrl?.host if let url = parsedUrl, let host = host { @@ -533,7 +543,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { urlString = address let urlAttributedString = NSMutableAttributedString() - urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)) + urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)) if item.presentationData.theme.theme.list.itemAccentColor.isEqual(item.presentationData.theme.theme.list.itemPrimaryTextColor) { urlAttributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: NSMakeRange(0, urlAttributedString.length)) }