diff --git a/Localization/app.json b/Localization/app.json index 8a7817d40..b82bcb151 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -651,8 +651,9 @@ } }, "searching": { - "posts": "Posts with \"%@\"", - "people": "People with \"%@\"", + "posts": "Posts matching \"%@\"", + "people": "People matching \"%@\"", + "hashtag": "Go to #%@", "profile": "Go to @%@@%@", "url": "Open Link", "empty_state": { diff --git a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewSection.swift b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewSection.swift index 6cc4b8942..a9a1438c9 100644 --- a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewSection.swift +++ b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewSection.swift @@ -15,9 +15,10 @@ enum SearchResultOverviewItem: Hashable { case suggestion(SuggestionSectionEntry) enum DefaultSectionEntry: Hashable { - case posts(String) - case people(String) - case profile(username: String, domain: String) + case showHashtag(hashtag: String) + case posts(matching: String) + case people(matching: String) + case showProfile(username: String, domain: String) case openLink(String) var title: String { @@ -26,23 +27,27 @@ enum SearchResultOverviewItem: Hashable { return L10n.Scene.Search.Searching.posts(text) case .people(let username): return L10n.Scene.Search.Searching.people(username) - case .profile(let username, let instanceName): + case .showProfile(let username, let instanceName): return L10n.Scene.Search.Searching.profile(username, instanceName) case .openLink(_): return L10n.Scene.Search.Searching.url + case .showHashtag(let hashtag): + return L10n.Scene.Search.Searching.hashtag(hashtag) } } var icon: UIImage? { switch self { case .posts(_): - return UIImage(systemName: "number") + return UIImage(systemName: "magnifyingglass") case .people(_): return UIImage(systemName: "person.2") - case .profile(_, _): + case .showProfile(_, _): return UIImage(systemName: "person.crop.circle") case .openLink(_): return UIImage(systemName: "link") + case .showHashtag(_): + return UIImage(systemName: "number") } } } diff --git a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift index 726a19b2b..2d4d61cef 100644 --- a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift +++ b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift @@ -4,6 +4,7 @@ import UIKit import MastodonCore import MastodonSDK import MastodonLocalization +import MastodonUI protocol SearchResultsOverviewTableViewControllerDelegate: AnyObject { func goTo(_ viewController: SearchResultsOverviewTableViewController, urlString: String) @@ -95,26 +96,40 @@ class SearchResultsOverviewTableViewController: UIViewController, NeedsDependenc var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .default)) - snapshot.appendItems([.default(.posts(searchText)), - .default(.people(searchText))], toSection: .default) - let components = searchText.split(separator: "@") - if components.count == 2 { - let username = String(components[0]) - let domain = String(components[1]) - if domain.split(separator: ".").count >= 2 { - snapshot.appendItems([.default(.profile(username: username, domain: domain))], toSection: .default) - } else { - snapshot.appendItems([.default(.profile(username: username, domain: authContext.mastodonAuthenticationBox.domain))], toSection: .default) + if searchText.lowercased().starts(with: "https://") && (searchText.contains(" ") == false) { + if URL(string: searchText)?.isValidURL() ?? false { + snapshot.appendItems([.default(.openLink(searchText))], toSection: .default) } - } else { - snapshot.appendItems([.default(.profile(username: searchText, domain: authContext.mastodonAuthenticationBox.domain))], toSection: .default) } - if URL(string: searchText)?.isValidURL() ?? false { - snapshot.appendItems([.default(.openLink(searchText))], toSection: .default) + //TODO: Check for Hashtag-Regex! + if searchText.starts(with: "#") && searchText.length > 1 { + snapshot.appendItems([.default(.showHashtag(hashtag: searchText.replacingOccurrences(of: "#", with: "")))], + toSection: .default) } + if searchText.length > 1, + let usernameRegex = try? NSRegularExpression(pattern: MastodonRegex.Search.username, options: .caseInsensitive), + usernameRegex.firstMatch(in: searchText, range: NSRange(location: 0, length: searchText.length-1)) != nil { + let components = searchText.split(separator: "@") + if components.count == 2 { + let username = String(components[0]).replacingOccurrences(of: "@", with: "") + + let domain = String(components[1]) + if domain.split(separator: ".").count >= 2 { + snapshot.appendItems([.default(.showProfile(username: username, domain: domain))], toSection: .default) + } else { + snapshot.appendItems([.default(.showProfile(username: username, domain: authContext.mastodonAuthenticationBox.domain))], toSection: .default) + } + } else { + snapshot.appendItems([.default(.showProfile(username: searchText, domain: authContext.mastodonAuthenticationBox.domain))], toSection: .default) + } + } + + snapshot.appendItems([.default(.posts(matching: searchText)), + .default(.people(matching: searchText))], toSection: .default) + dataSource.apply(snapshot, animatingDifferences: false) } @@ -187,10 +202,13 @@ extension SearchResultsOverviewTableViewController: UITableViewDelegate { delegate?.searchForPosts(self, withSearchText: searchText) case .people(let searchText): delegate?.searchForPeople(self, withName: searchText) - case .profile(let username, let domain): + case .showProfile(let username, let domain): delegate?.searchForPerson(self, username: username, domain: domain) case .openLink(let urlString): delegate?.goTo(self, urlString: urlString) + case .showHashtag(let hashtagText): + let tag = Mastodon.Entity.Tag(name: hashtagText, url: "") + delegate?.showPosts(self, tag: tag) } case .suggestion(let suggestionSectionEntry): switch suggestionSectionEntry { diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index 4484b6e96..3367b4805 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -1279,13 +1279,17 @@ public enum L10n { public static let clear = L10n.tr("Localizable", "Scene.Search.Searching.Clear", fallback: "Clear") /// Clear all public static let clearAll = L10n.tr("Localizable", "Scene.Search.Searching.ClearAll", fallback: "Clear all") - /// People with "%@" - public static func people(_ p1: Any) -> String { - return L10n.tr("Localizable", "Scene.Search.Searching.People", String(describing: p1), fallback: "People with \"%@\"") + /// Go to #%@ + public static func hashtag(_ p1: Any) -> String { + return L10n.tr("Localizable", "Scene.Search.Searching.Hashtag", String(describing: p1), fallback: "Go to #%@") } - /// Posts with "%@" + /// People matching "%@" + public static func people(_ p1: Any) -> String { + return L10n.tr("Localizable", "Scene.Search.Searching.People", String(describing: p1), fallback: "People matching \"%@\"") + } + /// Posts matching "%@" public static func posts(_ p1: Any) -> String { - return L10n.tr("Localizable", "Scene.Search.Searching.Posts", String(describing: p1), fallback: "Posts with \"%@\"") + return L10n.tr("Localizable", "Scene.Search.Searching.Posts", String(describing: p1), fallback: "Posts matching \"%@\"") } /// Go to @%@@%@ public static func profile(_ p1: Any, _ p2: Any) -> String { @@ -1293,8 +1297,8 @@ public enum L10n { } /// Recent searches public static let recentSearch = L10n.tr("Localizable", "Scene.Search.Searching.RecentSearch", fallback: "Recent searches") - /// Open Link - public static let url = L10n.tr("Localizable", "Scene.Search.Searching.Url", fallback: "Open Link") + /// Open URL in Mastodon + public static let url = L10n.tr("Localizable", "Scene.Search.Searching.Url", fallback: "Open URL in Mastodon") public enum EmptyState { /// No results public static let noResults = L10n.tr("Localizable", "Scene.Search.Searching.EmptyState.NoResults", fallback: "No results") diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index 0b5102ec0..9ef23c951 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -443,10 +443,11 @@ uploaded to Mastodon."; "Scene.Search.Searching.ClearAll" = "Clear all"; "Scene.Search.Searching.EmptyState.NoResults" = "No results"; "Scene.Search.Searching.RecentSearch" = "Recent searches"; -"Scene.Search.Searching.Posts" = "Posts with \"%@\""; -"Scene.Search.Searching.People" = "People with \"%@\""; +"Scene.Search.Searching.Posts" = "Posts matching \"%@\""; +"Scene.Search.Searching.People" = "People matching \"%@\""; "Scene.Search.Searching.Profile" = "Go to @%@@%@"; -"Scene.Search.Searching.Url" = "Open Link"; +"Scene.Search.Searching.Hashtag" = "Go to #%@"; +"Scene.Search.Searching.Url" = "Open URL in Mastodon"; "Scene.Search.Searching.NoUser.Title" = "No User Account Found"; "Scene.Search.Searching.NoUser.Message" = "There's no Useraccount \"%@\" on %@"; diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Helper/MastodonRegex.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Helper/MastodonRegex.swift index c8c3f498b..0cc925f6d 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Helper/MastodonRegex.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Helper/MastodonRegex.swift @@ -22,4 +22,9 @@ public enum MastodonRegex { /// #… /// :… public static let autoCompletePattern = "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))|(^\\B:|\\s:)([a-zA-Z0-9_]+)" + + public enum Search { + public static let username = "^@?[a-z0-9_-]+(@[\\S]+)?$" + } } +